1da6c28aaSamw /* 2da6c28aaSamw * CDDL HEADER START 3da6c28aaSamw * 4da6c28aaSamw * The contents of this file are subject to the terms of the 5da6c28aaSamw * Common Development and Distribution License (the "License"). 6da6c28aaSamw * You may not use this file except in compliance with the License. 7da6c28aaSamw * 8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9da6c28aaSamw * or http://www.opensolaris.org/os/licensing. 10da6c28aaSamw * See the License for the specific language governing permissions 11da6c28aaSamw * and limitations under the License. 12da6c28aaSamw * 13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each 14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the 16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying 17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner] 18da6c28aaSamw * 19da6c28aaSamw * CDDL HEADER END 20da6c28aaSamw */ 21da6c28aaSamw /* 22148c5f43SAlan Wright * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23*b819cea2SGordon Ross * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 24da6c28aaSamw */ 25da6c28aaSamw 26*b819cea2SGordon Ross #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) 27da6c28aaSamw #include <stdio.h> 28da6c28aaSamw #include <strings.h> 29da6c28aaSamw #include <stdlib.h> 30da6c28aaSamw #include <syslog.h> 31*b819cea2SGordon Ross #else /* !_KERNEL && !_FAKE_KERNEL */ 32da6c28aaSamw #include <sys/types.h> 33*b819cea2SGordon Ross #include <sys/systm.h> 34da6c28aaSamw #include <sys/sunddi.h> 35*b819cea2SGordon Ross #endif /* !_KERNEL && !_FAKE_KERNEL */ 36da6c28aaSamw 376537f381Sas200622 #include <smbsrv/smb_sid.h> 38da6c28aaSamw 396537f381Sas200622 static smb_sid_t *smb_sid_alloc(size_t); 40da6c28aaSamw 41da6c28aaSamw /* 426537f381Sas200622 * smb_sid_isvalid 43da6c28aaSamw * 446537f381Sas200622 * Performs a minimal SID validation. 45da6c28aaSamw */ 466537f381Sas200622 boolean_t 476537f381Sas200622 smb_sid_isvalid(smb_sid_t *sid) 48da6c28aaSamw { 496537f381Sas200622 if (sid == NULL) 506537f381Sas200622 return (B_FALSE); 51da6c28aaSamw 526537f381Sas200622 return ((sid->sid_revision == NT_SID_REVISION) && 536537f381Sas200622 (sid->sid_subauthcnt < NT_SID_SUBAUTH_MAX)); 54da6c28aaSamw } 55da6c28aaSamw 56da6c28aaSamw /* 576537f381Sas200622 * smb_sid_len 58da6c28aaSamw * 59da6c28aaSamw * Returns the number of bytes required to hold the sid. 60da6c28aaSamw */ 61da6c28aaSamw int 626537f381Sas200622 smb_sid_len(smb_sid_t *sid) 63da6c28aaSamw { 646537f381Sas200622 if (sid == NULL) 65da6c28aaSamw return (0); 66da6c28aaSamw 676537f381Sas200622 return (sizeof (smb_sid_t) - sizeof (uint32_t) 686537f381Sas200622 + (sid->sid_subauthcnt * sizeof (uint32_t))); 69da6c28aaSamw } 70da6c28aaSamw 71da6c28aaSamw /* 726537f381Sas200622 * smb_sid_dup 73da6c28aaSamw * 746537f381Sas200622 * Make a duplicate of the specified sid. The memory for the new sid 756537f381Sas200622 * should be freed by calling smb_sid_free(). 766537f381Sas200622 * A pointer to the new sid is returned. 77da6c28aaSamw */ 786537f381Sas200622 smb_sid_t * 796537f381Sas200622 smb_sid_dup(smb_sid_t *sid) 80da6c28aaSamw { 816537f381Sas200622 smb_sid_t *new_sid; 82da6c28aaSamw int size; 83da6c28aaSamw 846537f381Sas200622 if (sid == NULL) 856537f381Sas200622 return (NULL); 86da6c28aaSamw 876537f381Sas200622 size = smb_sid_len(sid); 886537f381Sas200622 if ((new_sid = smb_sid_alloc(size)) == NULL) 896537f381Sas200622 return (NULL); 90da6c28aaSamw 916537f381Sas200622 bcopy(sid, new_sid, size); 92da6c28aaSamw return (new_sid); 93da6c28aaSamw } 94da6c28aaSamw 95da6c28aaSamw 96da6c28aaSamw /* 976537f381Sas200622 * smb_sid_splice 98da6c28aaSamw * 996537f381Sas200622 * Make a full sid from a domain sid and a relative id (rid). 1006537f381Sas200622 * The memory for the result sid should be freed by calling 1016537f381Sas200622 * smb_sid_free(). A pointer to the new sid is returned. 102da6c28aaSamw */ 1036537f381Sas200622 smb_sid_t * 1046537f381Sas200622 smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid) 105da6c28aaSamw { 1066537f381Sas200622 smb_sid_t *sid; 107da6c28aaSamw int size; 108da6c28aaSamw 1096537f381Sas200622 if (domain_sid == NULL) 1106537f381Sas200622 return (NULL); 111da6c28aaSamw 1126537f381Sas200622 size = smb_sid_len(domain_sid); 1136537f381Sas200622 if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL) 1146537f381Sas200622 return (NULL); 115da6c28aaSamw 1166537f381Sas200622 bcopy(domain_sid, sid, size); 117da6c28aaSamw 1186537f381Sas200622 sid->sid_subauth[domain_sid->sid_subauthcnt] = rid; 1196537f381Sas200622 ++sid->sid_subauthcnt; 120da6c28aaSamw 121da6c28aaSamw return (sid); 122da6c28aaSamw } 123da6c28aaSamw 124da6c28aaSamw /* 1256537f381Sas200622 * smb_sid_getrid 126da6c28aaSamw * 1276537f381Sas200622 * Return the Relative Id (RID) of the specified SID. It is the 128da6c28aaSamw * caller's responsibility to ensure that this is an appropriate SID. 129da6c28aaSamw * All we do here is return the last sub-authority from the SID. 130da6c28aaSamw */ 131da6c28aaSamw int 1326537f381Sas200622 smb_sid_getrid(smb_sid_t *sid, uint32_t *rid) 133da6c28aaSamw { 1346537f381Sas200622 if (!smb_sid_isvalid(sid) || (rid == NULL) || 1356537f381Sas200622 (sid->sid_subauthcnt == 0)) 136da6c28aaSamw return (-1); 137da6c28aaSamw 1386537f381Sas200622 *rid = sid->sid_subauth[sid->sid_subauthcnt - 1]; 139da6c28aaSamw return (0); 140da6c28aaSamw } 141da6c28aaSamw 142da6c28aaSamw /* 1436537f381Sas200622 * smb_sid_split 144da6c28aaSamw * 1456537f381Sas200622 * Take a full sid and split it into a domain sid and a relative id (rid). 1467f667e74Sjose borrego * The domain SID is allocated and a pointer to it will be returned. The 1477f667e74Sjose borrego * RID value is passed back in 'rid' arg if it's not NULL. The allocated 1487f667e74Sjose borrego * memory for the domain SID must be freed by caller. 149da6c28aaSamw */ 1507f667e74Sjose borrego smb_sid_t * 1516537f381Sas200622 smb_sid_split(smb_sid_t *sid, uint32_t *rid) 152da6c28aaSamw { 1537f667e74Sjose borrego smb_sid_t *domsid; 154*b819cea2SGordon Ross int size; 155da6c28aaSamw 1567f667e74Sjose borrego if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0)) 1577f667e74Sjose borrego return (NULL); 1587f667e74Sjose borrego 159*b819cea2SGordon Ross /* We will reduce sid_subauthcnt by one. */ 160*b819cea2SGordon Ross size = smb_sid_len(sid) - sizeof (uint32_t); 161*b819cea2SGordon Ross if ((domsid = smb_sid_alloc(size)) == NULL) 1627f667e74Sjose borrego return (NULL); 1637f667e74Sjose borrego 164*b819cea2SGordon Ross bcopy(sid, domsid, size); 165*b819cea2SGordon Ross domsid->sid_subauthcnt = sid->sid_subauthcnt - 1; 166*b819cea2SGordon Ross 167da6c28aaSamw if (rid) 1687f667e74Sjose borrego *rid = domsid->sid_subauth[domsid->sid_subauthcnt]; 1697f667e74Sjose borrego 1707f667e74Sjose borrego return (domsid); 171da6c28aaSamw } 172da6c28aaSamw 173da6c28aaSamw /* 1746537f381Sas200622 * smb_sid_splitstr 175da6c28aaSamw * 1766537f381Sas200622 * Takes a full sid in string form and split it into a domain sid and a 1776537f381Sas200622 * relative id (rid). 1786537f381Sas200622 * 1796537f381Sas200622 * IMPORTANT: The original sid is modified in place. This function assumes 1806537f381Sas200622 * given SID is in valid string format. 181da6c28aaSamw */ 1826537f381Sas200622 int 1836537f381Sas200622 smb_sid_splitstr(char *strsid, uint32_t *rid) 184da6c28aaSamw { 1856537f381Sas200622 char *p; 186da6c28aaSamw 1876537f381Sas200622 if ((p = strrchr(strsid, '-')) == NULL) 1886537f381Sas200622 return (-1); 189da6c28aaSamw 1906537f381Sas200622 *p++ = '\0'; 1916537f381Sas200622 if (rid) { 192*b819cea2SGordon Ross #if defined(_KERNEL) || defined(_FAKE_KERNEL) 1936537f381Sas200622 unsigned long sua = 0; 1946537f381Sas200622 (void) ddi_strtoul(p, NULL, 10, &sua); 1956537f381Sas200622 *rid = (uint32_t)sua; 1966537f381Sas200622 #else 1976537f381Sas200622 *rid = strtoul(p, NULL, 10); 1986537f381Sas200622 #endif 1996537f381Sas200622 } 2006537f381Sas200622 201da6c28aaSamw return (0); 202da6c28aaSamw } 203da6c28aaSamw 204da6c28aaSamw /* 2056537f381Sas200622 * smb_sid_cmp 206da6c28aaSamw * 207da6c28aaSamw * Compare two SIDs and return a boolean result. The checks are ordered 208da6c28aaSamw * such that components that are more likely to differ are checked 209da6c28aaSamw * first. For example, after checking that the SIDs contain the same 2106537f381Sas200622 * sid_subauthcnt, we check the sub-authorities in reverse order because 211da6c28aaSamw * the RID is the most likely differentiator between two SIDs, i.e. 212da6c28aaSamw * they are probably going to be in the same domain. 213da6c28aaSamw */ 2146537f381Sas200622 boolean_t 2156537f381Sas200622 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2) 216da6c28aaSamw { 217da6c28aaSamw int i; 218da6c28aaSamw 2196537f381Sas200622 if (sid1 == NULL || sid2 == NULL) 2206537f381Sas200622 return (B_FALSE); 221da6c28aaSamw 2226537f381Sas200622 if (sid1->sid_subauthcnt != sid2->sid_subauthcnt || 2236537f381Sas200622 sid1->sid_revision != sid2->sid_revision) 2246537f381Sas200622 return (B_FALSE); 225da6c28aaSamw 2266537f381Sas200622 for (i = sid1->sid_subauthcnt - 1; i >= 0; --i) 2276537f381Sas200622 if (sid1->sid_subauth[i] != sid2->sid_subauth[i]) 2286537f381Sas200622 return (B_FALSE); 229da6c28aaSamw 2306537f381Sas200622 if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX)) 2316537f381Sas200622 return (B_FALSE); 232da6c28aaSamw 2336537f381Sas200622 return (B_TRUE); 234da6c28aaSamw } 235da6c28aaSamw 236da6c28aaSamw /* 2376537f381Sas200622 * smb_sid_indomain 238da6c28aaSamw * 239da6c28aaSamw * Check if given SID is in given domain. 240da6c28aaSamw */ 2416537f381Sas200622 boolean_t 2426537f381Sas200622 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid) 243da6c28aaSamw { 244da6c28aaSamw int i; 245da6c28aaSamw 2466537f381Sas200622 if (sid == NULL || domain_sid == NULL) 2476537f381Sas200622 return (B_FALSE); 248da6c28aaSamw 2496537f381Sas200622 if (domain_sid->sid_revision != sid->sid_revision || 2506537f381Sas200622 sid->sid_subauthcnt < domain_sid->sid_subauthcnt) 2516537f381Sas200622 return (B_FALSE); 252da6c28aaSamw 2536537f381Sas200622 for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i) 2546537f381Sas200622 if (domain_sid->sid_subauth[i] != sid->sid_subauth[i]) 2556537f381Sas200622 return (B_FALSE); 256da6c28aaSamw 2576537f381Sas200622 if (bcmp(&domain_sid->sid_authority, &sid->sid_authority, 2586537f381Sas200622 NT_SID_AUTH_MAX)) 2596537f381Sas200622 return (B_FALSE); 260da6c28aaSamw 2616537f381Sas200622 return (B_TRUE); 262da6c28aaSamw } 263da6c28aaSamw 264da6c28aaSamw /* 2656537f381Sas200622 * smb_sid_tostr 266da6c28aaSamw * 2676537f381Sas200622 * Fill in the passed buffer with the string form of the given 2686537f381Sas200622 * binary sid. 269da6c28aaSamw */ 270da6c28aaSamw void 271148c5f43SAlan Wright smb_sid_tostr(const smb_sid_t *sid, char *strsid) 272da6c28aaSamw { 2736537f381Sas200622 char *p = strsid; 2746537f381Sas200622 int i; 275da6c28aaSamw 2766537f381Sas200622 if (sid == NULL || strsid == NULL) 277da6c28aaSamw return; 278da6c28aaSamw 2796537f381Sas200622 (void) sprintf(p, "S-%d-", sid->sid_revision); 280da6c28aaSamw while (*p) 2816537f381Sas200622 p++; 282da6c28aaSamw 283da6c28aaSamw for (i = 0; i < NT_SID_AUTH_MAX; ++i) { 2846537f381Sas200622 if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { 2856537f381Sas200622 (void) sprintf(p, "%d", sid->sid_authority[i]); 286da6c28aaSamw while (*p) 2876537f381Sas200622 p++; 288da6c28aaSamw } 289da6c28aaSamw } 290da6c28aaSamw 2916537f381Sas200622 for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) { 2926537f381Sas200622 (void) sprintf(p, "-%u", sid->sid_subauth[i]); 293da6c28aaSamw while (*p) 2946537f381Sas200622 p++; 295da6c28aaSamw } 296da6c28aaSamw } 297da6c28aaSamw 298da6c28aaSamw /* 2996537f381Sas200622 * smb_sid_fromstr 300da6c28aaSamw * 301da6c28aaSamw * Converts a SID in string form to a SID structure. There are lots of 302da6c28aaSamw * simplifying assumptions in here. The memory for the SID is allocated 303da6c28aaSamw * as if it was the largest possible SID; the caller is responsible for 304da6c28aaSamw * freeing the memory when it is no longer required. We assume that the 305da6c28aaSamw * string starts with "S-1-" and that the authority is held in the last 306da6c28aaSamw * byte, which should be okay for most situations. It also assumes the 307da6c28aaSamw * sub-authorities are in decimal format. 308da6c28aaSamw * 309da6c28aaSamw * On success, a pointer to a SID is returned. Otherwise a null pointer 310da6c28aaSamw * is returned. 311da6c28aaSamw */ 312*b819cea2SGordon Ross #if defined(_KERNEL) || defined(_FAKE_KERNEL) 3136537f381Sas200622 smb_sid_t * 314148c5f43SAlan Wright smb_sid_fromstr(const char *sidstr) 315da6c28aaSamw { 3166537f381Sas200622 smb_sid_t *sid; 3176537f381Sas200622 smb_sid_t *retsid; 318148c5f43SAlan Wright const char *p; 319da6c28aaSamw int size; 3206537f381Sas200622 uint8_t i; 3216537f381Sas200622 unsigned long sua; 322da6c28aaSamw 3236537f381Sas200622 if (sidstr == NULL) 3246537f381Sas200622 return (NULL); 325da6c28aaSamw 3266537f381Sas200622 if (strncmp(sidstr, "S-1-", 4) != 0) 3276537f381Sas200622 return (NULL); 328da6c28aaSamw 3296537f381Sas200622 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 3306537f381Sas200622 sid = kmem_zalloc(size, KM_SLEEP); 331da6c28aaSamw 3326537f381Sas200622 sid->sid_revision = NT_SID_REVISION; 333da6c28aaSamw sua = 0; 3346537f381Sas200622 (void) ddi_strtoul(&sidstr[4], 0, 10, &sua); 3356537f381Sas200622 sid->sid_authority[5] = (uint8_t)sua; 336da6c28aaSamw 337da6c28aaSamw for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 338da6c28aaSamw while (*p && *p == '-') 339da6c28aaSamw ++p; 340da6c28aaSamw 341da6c28aaSamw if (*p < '0' || *p > '9') { 3426537f381Sas200622 kmem_free(sid, size); 3436537f381Sas200622 return (NULL); 344da6c28aaSamw } 345da6c28aaSamw 346da6c28aaSamw sua = 0; 3476537f381Sas200622 (void) ddi_strtoul(p, 0, 10, &sua); 3486537f381Sas200622 sid->sid_subauth[i] = (uint32_t)sua; 349da6c28aaSamw 350da6c28aaSamw while (*p && *p != '-') 351da6c28aaSamw ++p; 352da6c28aaSamw } 353da6c28aaSamw 3546537f381Sas200622 sid->sid_subauthcnt = i; 3556537f381Sas200622 retsid = smb_sid_dup(sid); 3566537f381Sas200622 kmem_free(sid, size); 3576537f381Sas200622 3586537f381Sas200622 return (retsid); 3596537f381Sas200622 } 3606537f381Sas200622 #else /* _KERNEL */ 3616537f381Sas200622 smb_sid_t * 362148c5f43SAlan Wright smb_sid_fromstr(const char *sidstr) 3636537f381Sas200622 { 3646537f381Sas200622 smb_sid_t *sid; 365148c5f43SAlan Wright const char *p; 3666537f381Sas200622 int size; 3676537f381Sas200622 uint8_t i; 3686537f381Sas200622 3696537f381Sas200622 if (sidstr == NULL) 3706537f381Sas200622 return (NULL); 3716537f381Sas200622 3726537f381Sas200622 if (strncmp(sidstr, "S-1-", 4) != 0) 3736537f381Sas200622 return (NULL); 3746537f381Sas200622 3756537f381Sas200622 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 3766537f381Sas200622 3776537f381Sas200622 if ((sid = malloc(size)) == NULL) 3786537f381Sas200622 return (NULL); 3796537f381Sas200622 3806537f381Sas200622 bzero(sid, size); 3816537f381Sas200622 sid->sid_revision = NT_SID_REVISION; 3826537f381Sas200622 sid->sid_authority[5] = atoi(&sidstr[4]); 3836537f381Sas200622 3846537f381Sas200622 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 3856537f381Sas200622 while (*p && *p == '-') 3866537f381Sas200622 ++p; 3876537f381Sas200622 3886537f381Sas200622 if (*p < '0' || *p > '9') { 3896537f381Sas200622 free(sid); 3906537f381Sas200622 return (NULL); 391da6c28aaSamw } 392da6c28aaSamw 3936537f381Sas200622 sid->sid_subauth[i] = strtoul(p, NULL, 10); 3946537f381Sas200622 3956537f381Sas200622 while (*p && *p != '-') 3966537f381Sas200622 ++p; 3976537f381Sas200622 } 3986537f381Sas200622 3996537f381Sas200622 sid->sid_subauthcnt = i; 4006537f381Sas200622 return (sid); 4016537f381Sas200622 } 4026537f381Sas200622 #endif /* _KERNEL */ 403da6c28aaSamw 404da6c28aaSamw /* 4056537f381Sas200622 * smb_sid_type2str 406da6c28aaSamw * 407da6c28aaSamw * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE 408da6c28aaSamw * provides the context for a SID, i.e. the type of resource to which 409da6c28aaSamw * it refers. 410da6c28aaSamw */ 411da6c28aaSamw char * 4126537f381Sas200622 smb_sid_type2str(uint16_t snu_id) 413da6c28aaSamw { 414da6c28aaSamw static char *snu_name[] = { 415da6c28aaSamw "SidTypeSidPrefix", 416da6c28aaSamw "SidTypeUser", 417da6c28aaSamw "SidTypeGroup", 418da6c28aaSamw "SidTypeDomain", 419da6c28aaSamw "SidTypeAlias", 420da6c28aaSamw "SidTypeWellKnownGroup", 421da6c28aaSamw "SidTypeDeletedAccount", 422da6c28aaSamw "SidTypeInvalid", 423fe1c642dSBill Krier "SidTypeUnknown", 424fe1c642dSBill Krier "SidTypeComputer", 425fe1c642dSBill Krier "SidTypeLabel" 426da6c28aaSamw }; 427da6c28aaSamw 428da6c28aaSamw if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0])))) 429da6c28aaSamw return (snu_name[snu_id]); 4306537f381Sas200622 431da6c28aaSamw return (snu_name[SidTypeUnknown]); 432da6c28aaSamw } 4336537f381Sas200622 4346537f381Sas200622 static smb_sid_t * 4356537f381Sas200622 smb_sid_alloc(size_t size) 4366537f381Sas200622 { 4376537f381Sas200622 smb_sid_t *sid; 438*b819cea2SGordon Ross #if defined(_KERNEL) || defined(_FAKE_KERNEL) 4396537f381Sas200622 sid = kmem_alloc(size, KM_SLEEP); 4406537f381Sas200622 #else 4416537f381Sas200622 sid = malloc(size); 4426537f381Sas200622 #endif 4436537f381Sas200622 return (sid); 444da6c28aaSamw } 445da6c28aaSamw 4466537f381Sas200622 void 4476537f381Sas200622 smb_sid_free(smb_sid_t *sid) 448da6c28aaSamw { 449*b819cea2SGordon Ross #if defined(_KERNEL) || defined(_FAKE_KERNEL) 4506537f381Sas200622 if (sid == NULL) 4516537f381Sas200622 return; 452da6c28aaSamw 4536537f381Sas200622 kmem_free(sid, smb_sid_len(sid)); 4546537f381Sas200622 #else 4556537f381Sas200622 free(sid); 4566537f381Sas200622 #endif 457da6c28aaSamw } 458