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. 23b819cea2SGordon Ross * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 24da6c28aaSamw */ 25da6c28aaSamw 26b819cea2SGordon Ross #if !defined(_KERNEL) && !defined(_FAKE_KERNEL) 27da6c28aaSamw #include <stdio.h> 28da6c28aaSamw #include <strings.h> 29da6c28aaSamw #include <stdlib.h> 30da6c28aaSamw #include <syslog.h> 31b819cea2SGordon Ross #else /* !_KERNEL && !_FAKE_KERNEL */ 32da6c28aaSamw #include <sys/types.h> 33b819cea2SGordon Ross #include <sys/systm.h> 34da6c28aaSamw #include <sys/sunddi.h> 35b819cea2SGordon 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; 154b819cea2SGordon Ross int size; 155da6c28aaSamw 1567f667e74Sjose borrego if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0)) 1577f667e74Sjose borrego return (NULL); 1587f667e74Sjose borrego 159*e16c88fbSGordon Ross /* 160*e16c88fbSGordon Ross * We will reduce sid_subauthcnt by one, because 161*e16c88fbSGordon Ross * the domain SID does not include the RID. 162*e16c88fbSGordon Ross */ 163b819cea2SGordon Ross size = smb_sid_len(sid) - sizeof (uint32_t); 164b819cea2SGordon Ross if ((domsid = smb_sid_alloc(size)) == NULL) 1657f667e74Sjose borrego return (NULL); 1667f667e74Sjose borrego 167b819cea2SGordon Ross bcopy(sid, domsid, size); 168b819cea2SGordon Ross domsid->sid_subauthcnt = sid->sid_subauthcnt - 1; 169b819cea2SGordon Ross 170da6c28aaSamw if (rid) 171*e16c88fbSGordon Ross *rid = sid->sid_subauth[sid->sid_subauthcnt - 1]; 1727f667e74Sjose borrego 1737f667e74Sjose borrego return (domsid); 174da6c28aaSamw } 175da6c28aaSamw 176da6c28aaSamw /* 1776537f381Sas200622 * smb_sid_splitstr 178da6c28aaSamw * 1796537f381Sas200622 * Takes a full sid in string form and split it into a domain sid and a 1806537f381Sas200622 * relative id (rid). 1816537f381Sas200622 * 1826537f381Sas200622 * IMPORTANT: The original sid is modified in place. This function assumes 1836537f381Sas200622 * given SID is in valid string format. 184da6c28aaSamw */ 1856537f381Sas200622 int 1866537f381Sas200622 smb_sid_splitstr(char *strsid, uint32_t *rid) 187da6c28aaSamw { 1886537f381Sas200622 char *p; 189da6c28aaSamw 1906537f381Sas200622 if ((p = strrchr(strsid, '-')) == NULL) 1916537f381Sas200622 return (-1); 192da6c28aaSamw 1936537f381Sas200622 *p++ = '\0'; 1946537f381Sas200622 if (rid) { 195b819cea2SGordon Ross #if defined(_KERNEL) || defined(_FAKE_KERNEL) 1966537f381Sas200622 unsigned long sua = 0; 1976537f381Sas200622 (void) ddi_strtoul(p, NULL, 10, &sua); 1986537f381Sas200622 *rid = (uint32_t)sua; 1996537f381Sas200622 #else 2006537f381Sas200622 *rid = strtoul(p, NULL, 10); 2016537f381Sas200622 #endif 2026537f381Sas200622 } 2036537f381Sas200622 204da6c28aaSamw return (0); 205da6c28aaSamw } 206da6c28aaSamw 207da6c28aaSamw /* 2086537f381Sas200622 * smb_sid_cmp 209da6c28aaSamw * 210da6c28aaSamw * Compare two SIDs and return a boolean result. The checks are ordered 211da6c28aaSamw * such that components that are more likely to differ are checked 212da6c28aaSamw * first. For example, after checking that the SIDs contain the same 2136537f381Sas200622 * sid_subauthcnt, we check the sub-authorities in reverse order because 214da6c28aaSamw * the RID is the most likely differentiator between two SIDs, i.e. 215da6c28aaSamw * they are probably going to be in the same domain. 216da6c28aaSamw */ 2176537f381Sas200622 boolean_t 2186537f381Sas200622 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2) 219da6c28aaSamw { 220da6c28aaSamw int i; 221da6c28aaSamw 2226537f381Sas200622 if (sid1 == NULL || sid2 == NULL) 2236537f381Sas200622 return (B_FALSE); 224da6c28aaSamw 2256537f381Sas200622 if (sid1->sid_subauthcnt != sid2->sid_subauthcnt || 2266537f381Sas200622 sid1->sid_revision != sid2->sid_revision) 2276537f381Sas200622 return (B_FALSE); 228da6c28aaSamw 2296537f381Sas200622 for (i = sid1->sid_subauthcnt - 1; i >= 0; --i) 2306537f381Sas200622 if (sid1->sid_subauth[i] != sid2->sid_subauth[i]) 2316537f381Sas200622 return (B_FALSE); 232da6c28aaSamw 2336537f381Sas200622 if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX)) 2346537f381Sas200622 return (B_FALSE); 235da6c28aaSamw 2366537f381Sas200622 return (B_TRUE); 237da6c28aaSamw } 238da6c28aaSamw 239da6c28aaSamw /* 2406537f381Sas200622 * smb_sid_indomain 241da6c28aaSamw * 242da6c28aaSamw * Check if given SID is in given domain. 243da6c28aaSamw */ 2446537f381Sas200622 boolean_t 2456537f381Sas200622 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid) 246da6c28aaSamw { 247da6c28aaSamw int i; 248da6c28aaSamw 2496537f381Sas200622 if (sid == NULL || domain_sid == NULL) 2506537f381Sas200622 return (B_FALSE); 251da6c28aaSamw 2526537f381Sas200622 if (domain_sid->sid_revision != sid->sid_revision || 2536537f381Sas200622 sid->sid_subauthcnt < domain_sid->sid_subauthcnt) 2546537f381Sas200622 return (B_FALSE); 255da6c28aaSamw 2566537f381Sas200622 for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i) 2576537f381Sas200622 if (domain_sid->sid_subauth[i] != sid->sid_subauth[i]) 2586537f381Sas200622 return (B_FALSE); 259da6c28aaSamw 2606537f381Sas200622 if (bcmp(&domain_sid->sid_authority, &sid->sid_authority, 2616537f381Sas200622 NT_SID_AUTH_MAX)) 2626537f381Sas200622 return (B_FALSE); 263da6c28aaSamw 2646537f381Sas200622 return (B_TRUE); 265da6c28aaSamw } 266da6c28aaSamw 267da6c28aaSamw /* 2686537f381Sas200622 * smb_sid_tostr 269da6c28aaSamw * 2706537f381Sas200622 * Fill in the passed buffer with the string form of the given 2716537f381Sas200622 * binary sid. 272da6c28aaSamw */ 273da6c28aaSamw void 274148c5f43SAlan Wright smb_sid_tostr(const smb_sid_t *sid, char *strsid) 275da6c28aaSamw { 2766537f381Sas200622 char *p = strsid; 2776537f381Sas200622 int i; 278da6c28aaSamw 2796537f381Sas200622 if (sid == NULL || strsid == NULL) 280da6c28aaSamw return; 281da6c28aaSamw 2826537f381Sas200622 (void) sprintf(p, "S-%d-", sid->sid_revision); 283da6c28aaSamw while (*p) 2846537f381Sas200622 p++; 285da6c28aaSamw 286da6c28aaSamw for (i = 0; i < NT_SID_AUTH_MAX; ++i) { 2876537f381Sas200622 if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { 2886537f381Sas200622 (void) sprintf(p, "%d", sid->sid_authority[i]); 289da6c28aaSamw while (*p) 2906537f381Sas200622 p++; 291da6c28aaSamw } 292da6c28aaSamw } 293da6c28aaSamw 2946537f381Sas200622 for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) { 2956537f381Sas200622 (void) sprintf(p, "-%u", sid->sid_subauth[i]); 296da6c28aaSamw while (*p) 2976537f381Sas200622 p++; 298da6c28aaSamw } 299da6c28aaSamw } 300da6c28aaSamw 301da6c28aaSamw /* 3026537f381Sas200622 * smb_sid_fromstr 303da6c28aaSamw * 304da6c28aaSamw * Converts a SID in string form to a SID structure. There are lots of 305da6c28aaSamw * simplifying assumptions in here. The memory for the SID is allocated 306da6c28aaSamw * as if it was the largest possible SID; the caller is responsible for 307da6c28aaSamw * freeing the memory when it is no longer required. We assume that the 308da6c28aaSamw * string starts with "S-1-" and that the authority is held in the last 309da6c28aaSamw * byte, which should be okay for most situations. It also assumes the 310da6c28aaSamw * sub-authorities are in decimal format. 311da6c28aaSamw * 312da6c28aaSamw * On success, a pointer to a SID is returned. Otherwise a null pointer 313da6c28aaSamw * is returned. 314da6c28aaSamw */ 315b819cea2SGordon Ross #if defined(_KERNEL) || defined(_FAKE_KERNEL) 3166537f381Sas200622 smb_sid_t * 317148c5f43SAlan Wright smb_sid_fromstr(const char *sidstr) 318da6c28aaSamw { 3196537f381Sas200622 smb_sid_t *sid; 3206537f381Sas200622 smb_sid_t *retsid; 321148c5f43SAlan Wright const char *p; 322da6c28aaSamw int size; 3236537f381Sas200622 uint8_t i; 3246537f381Sas200622 unsigned long sua; 325da6c28aaSamw 3266537f381Sas200622 if (sidstr == NULL) 3276537f381Sas200622 return (NULL); 328da6c28aaSamw 3296537f381Sas200622 if (strncmp(sidstr, "S-1-", 4) != 0) 3306537f381Sas200622 return (NULL); 331da6c28aaSamw 3326537f381Sas200622 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 3336537f381Sas200622 sid = kmem_zalloc(size, KM_SLEEP); 334da6c28aaSamw 3356537f381Sas200622 sid->sid_revision = NT_SID_REVISION; 336da6c28aaSamw sua = 0; 3376537f381Sas200622 (void) ddi_strtoul(&sidstr[4], 0, 10, &sua); 3386537f381Sas200622 sid->sid_authority[5] = (uint8_t)sua; 339da6c28aaSamw 340da6c28aaSamw for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 341da6c28aaSamw while (*p && *p == '-') 342da6c28aaSamw ++p; 343da6c28aaSamw 344da6c28aaSamw if (*p < '0' || *p > '9') { 3456537f381Sas200622 kmem_free(sid, size); 3466537f381Sas200622 return (NULL); 347da6c28aaSamw } 348da6c28aaSamw 349da6c28aaSamw sua = 0; 3506537f381Sas200622 (void) ddi_strtoul(p, 0, 10, &sua); 3516537f381Sas200622 sid->sid_subauth[i] = (uint32_t)sua; 352da6c28aaSamw 353da6c28aaSamw while (*p && *p != '-') 354da6c28aaSamw ++p; 355da6c28aaSamw } 356da6c28aaSamw 3576537f381Sas200622 sid->sid_subauthcnt = i; 3586537f381Sas200622 retsid = smb_sid_dup(sid); 3596537f381Sas200622 kmem_free(sid, size); 3606537f381Sas200622 3616537f381Sas200622 return (retsid); 3626537f381Sas200622 } 3636537f381Sas200622 #else /* _KERNEL */ 3646537f381Sas200622 smb_sid_t * 365148c5f43SAlan Wright smb_sid_fromstr(const char *sidstr) 3666537f381Sas200622 { 3676537f381Sas200622 smb_sid_t *sid; 368148c5f43SAlan Wright const char *p; 3696537f381Sas200622 int size; 3706537f381Sas200622 uint8_t i; 3716537f381Sas200622 3726537f381Sas200622 if (sidstr == NULL) 3736537f381Sas200622 return (NULL); 3746537f381Sas200622 3756537f381Sas200622 if (strncmp(sidstr, "S-1-", 4) != 0) 3766537f381Sas200622 return (NULL); 3776537f381Sas200622 3786537f381Sas200622 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 3796537f381Sas200622 3806537f381Sas200622 if ((sid = malloc(size)) == NULL) 3816537f381Sas200622 return (NULL); 3826537f381Sas200622 3836537f381Sas200622 bzero(sid, size); 3846537f381Sas200622 sid->sid_revision = NT_SID_REVISION; 3856537f381Sas200622 sid->sid_authority[5] = atoi(&sidstr[4]); 3866537f381Sas200622 3876537f381Sas200622 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 3886537f381Sas200622 while (*p && *p == '-') 3896537f381Sas200622 ++p; 3906537f381Sas200622 3916537f381Sas200622 if (*p < '0' || *p > '9') { 3926537f381Sas200622 free(sid); 3936537f381Sas200622 return (NULL); 394da6c28aaSamw } 395da6c28aaSamw 3966537f381Sas200622 sid->sid_subauth[i] = strtoul(p, NULL, 10); 3976537f381Sas200622 3986537f381Sas200622 while (*p && *p != '-') 3996537f381Sas200622 ++p; 4006537f381Sas200622 } 4016537f381Sas200622 4026537f381Sas200622 sid->sid_subauthcnt = i; 4036537f381Sas200622 return (sid); 4046537f381Sas200622 } 4056537f381Sas200622 #endif /* _KERNEL */ 406da6c28aaSamw 407da6c28aaSamw /* 4086537f381Sas200622 * smb_sid_type2str 409da6c28aaSamw * 410da6c28aaSamw * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE 411da6c28aaSamw * provides the context for a SID, i.e. the type of resource to which 412da6c28aaSamw * it refers. 413da6c28aaSamw */ 414da6c28aaSamw char * 4156537f381Sas200622 smb_sid_type2str(uint16_t snu_id) 416da6c28aaSamw { 417da6c28aaSamw static char *snu_name[] = { 418da6c28aaSamw "SidTypeSidPrefix", 419da6c28aaSamw "SidTypeUser", 420da6c28aaSamw "SidTypeGroup", 421da6c28aaSamw "SidTypeDomain", 422da6c28aaSamw "SidTypeAlias", 423da6c28aaSamw "SidTypeWellKnownGroup", 424da6c28aaSamw "SidTypeDeletedAccount", 425da6c28aaSamw "SidTypeInvalid", 426fe1c642dSBill Krier "SidTypeUnknown", 427fe1c642dSBill Krier "SidTypeComputer", 428fe1c642dSBill Krier "SidTypeLabel" 429da6c28aaSamw }; 430da6c28aaSamw 431da6c28aaSamw if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0])))) 432da6c28aaSamw return (snu_name[snu_id]); 4336537f381Sas200622 434da6c28aaSamw return (snu_name[SidTypeUnknown]); 435da6c28aaSamw } 4366537f381Sas200622 4376537f381Sas200622 static smb_sid_t * 4386537f381Sas200622 smb_sid_alloc(size_t size) 4396537f381Sas200622 { 4406537f381Sas200622 smb_sid_t *sid; 441b819cea2SGordon Ross #if defined(_KERNEL) || defined(_FAKE_KERNEL) 4426537f381Sas200622 sid = kmem_alloc(size, KM_SLEEP); 4436537f381Sas200622 #else 4446537f381Sas200622 sid = malloc(size); 4456537f381Sas200622 #endif 4466537f381Sas200622 return (sid); 447da6c28aaSamw } 448da6c28aaSamw 4496537f381Sas200622 void 4506537f381Sas200622 smb_sid_free(smb_sid_t *sid) 451da6c28aaSamw { 452b819cea2SGordon Ross #if defined(_KERNEL) || defined(_FAKE_KERNEL) 4536537f381Sas200622 if (sid == NULL) 4546537f381Sas200622 return; 455da6c28aaSamw 4566537f381Sas200622 kmem_free(sid, smb_sid_len(sid)); 4576537f381Sas200622 #else 4586537f381Sas200622 free(sid); 4596537f381Sas200622 #endif 460da6c28aaSamw } 461