1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * NT Security Identifier (SID) library functions. 30 */ 31 32 #ifndef _KERNEL 33 #include <stdio.h> 34 #include <strings.h> 35 #include <stdlib.h> 36 #include <syslog.h> 37 #include <smbsrv/libsmb.h> 38 #else /* _KERNEL */ 39 #include <sys/types.h> 40 #include <sys/sunddi.h> 41 #endif /* _KERNEL */ 42 43 #include <smbsrv/smb_sid.h> 44 45 static smb_sid_t *smb_sid_alloc(size_t); 46 47 /* 48 * smb_sid_isvalid 49 * 50 * Performs a minimal SID validation. 51 */ 52 boolean_t 53 smb_sid_isvalid(smb_sid_t *sid) 54 { 55 if (sid == NULL) 56 return (B_FALSE); 57 58 return ((sid->sid_revision == NT_SID_REVISION) && 59 (sid->sid_subauthcnt < NT_SID_SUBAUTH_MAX)); 60 } 61 62 /* 63 * smb_sid_len 64 * 65 * Returns the number of bytes required to hold the sid. 66 */ 67 int 68 smb_sid_len(smb_sid_t *sid) 69 { 70 if (sid == NULL) 71 return (0); 72 73 return (sizeof (smb_sid_t) - sizeof (uint32_t) 74 + (sid->sid_subauthcnt * sizeof (uint32_t))); 75 } 76 77 /* 78 * smb_sid_dup 79 * 80 * Make a duplicate of the specified sid. The memory for the new sid 81 * should be freed by calling smb_sid_free(). 82 * A pointer to the new sid is returned. 83 */ 84 smb_sid_t * 85 smb_sid_dup(smb_sid_t *sid) 86 { 87 smb_sid_t *new_sid; 88 int size; 89 90 if (sid == NULL) 91 return (NULL); 92 93 size = smb_sid_len(sid); 94 if ((new_sid = smb_sid_alloc(size)) == NULL) 95 return (NULL); 96 97 bcopy(sid, new_sid, size); 98 return (new_sid); 99 } 100 101 102 /* 103 * smb_sid_splice 104 * 105 * Make a full sid from a domain sid and a relative id (rid). 106 * The memory for the result sid should be freed by calling 107 * smb_sid_free(). A pointer to the new sid is returned. 108 */ 109 smb_sid_t * 110 smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid) 111 { 112 smb_sid_t *sid; 113 int size; 114 115 if (domain_sid == NULL) 116 return (NULL); 117 118 size = smb_sid_len(domain_sid); 119 if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL) 120 return (NULL); 121 122 bcopy(domain_sid, sid, size); 123 124 sid->sid_subauth[domain_sid->sid_subauthcnt] = rid; 125 ++sid->sid_subauthcnt; 126 127 return (sid); 128 } 129 130 /* 131 * smb_sid_getrid 132 * 133 * Return the Relative Id (RID) of the specified SID. It is the 134 * caller's responsibility to ensure that this is an appropriate SID. 135 * All we do here is return the last sub-authority from the SID. 136 */ 137 int 138 smb_sid_getrid(smb_sid_t *sid, uint32_t *rid) 139 { 140 if (!smb_sid_isvalid(sid) || (rid == NULL) || 141 (sid->sid_subauthcnt == 0)) 142 return (-1); 143 144 *rid = sid->sid_subauth[sid->sid_subauthcnt - 1]; 145 return (0); 146 } 147 148 /* 149 * smb_sid_split 150 * 151 * Take a full sid and split it into a domain sid and a relative id (rid). 152 * 153 * IMPORTANT: The original sid is modified in place - use smb_sid_dup before 154 * calling this function to preserve the original SID. Be careful if you're 155 * using this function in kernel code, after calling this function 'sid' 156 * cannot be passed to smb_sid_free() because the size won't match the original 157 * allocated size. Use smb_sid_ksplit() instead. 158 */ 159 int 160 smb_sid_split(smb_sid_t *sid, uint32_t *rid) 161 { 162 if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0)) 163 return (-1); 164 165 --sid->sid_subauthcnt; 166 if (rid) 167 *rid = sid->sid_subauth[sid->sid_subauthcnt]; 168 return (0); 169 } 170 171 /* 172 * smb_sid_splitstr 173 * 174 * Takes a full sid in string form and split it into a domain sid and a 175 * relative id (rid). 176 * 177 * IMPORTANT: The original sid is modified in place. This function assumes 178 * given SID is in valid string format. 179 */ 180 int 181 smb_sid_splitstr(char *strsid, uint32_t *rid) 182 { 183 char *p; 184 185 if ((p = strrchr(strsid, '-')) == NULL) 186 return (-1); 187 188 *p++ = '\0'; 189 if (rid) { 190 #ifdef _KERNEL 191 unsigned long sua = 0; 192 (void) ddi_strtoul(p, NULL, 10, &sua); 193 *rid = (uint32_t)sua; 194 #else 195 *rid = strtoul(p, NULL, 10); 196 #endif 197 } 198 199 return (0); 200 } 201 202 /* 203 * smb_sid_cmp 204 * 205 * Compare two SIDs and return a boolean result. The checks are ordered 206 * such that components that are more likely to differ are checked 207 * first. For example, after checking that the SIDs contain the same 208 * sid_subauthcnt, we check the sub-authorities in reverse order because 209 * the RID is the most likely differentiator between two SIDs, i.e. 210 * they are probably going to be in the same domain. 211 */ 212 boolean_t 213 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2) 214 { 215 int i; 216 217 if (sid1 == NULL || sid2 == NULL) 218 return (B_FALSE); 219 220 if (sid1->sid_subauthcnt != sid2->sid_subauthcnt || 221 sid1->sid_revision != sid2->sid_revision) 222 return (B_FALSE); 223 224 for (i = sid1->sid_subauthcnt - 1; i >= 0; --i) 225 if (sid1->sid_subauth[i] != sid2->sid_subauth[i]) 226 return (B_FALSE); 227 228 if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX)) 229 return (B_FALSE); 230 231 return (B_TRUE); 232 } 233 234 /* 235 * smb_sid_indomain 236 * 237 * Check if given SID is in given domain. 238 */ 239 boolean_t 240 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid) 241 { 242 int i; 243 244 if (sid == NULL || domain_sid == NULL) 245 return (B_FALSE); 246 247 if (domain_sid->sid_revision != sid->sid_revision || 248 sid->sid_subauthcnt < domain_sid->sid_subauthcnt) 249 return (B_FALSE); 250 251 for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i) 252 if (domain_sid->sid_subauth[i] != sid->sid_subauth[i]) 253 return (B_FALSE); 254 255 if (bcmp(&domain_sid->sid_authority, &sid->sid_authority, 256 NT_SID_AUTH_MAX)) 257 return (B_FALSE); 258 259 return (B_TRUE); 260 } 261 262 #ifndef _KERNEL 263 /* 264 * smb_sid_islocal 265 * 266 * Check a SID to see if it belongs to the local domain. 267 */ 268 boolean_t 269 smb_sid_islocal(smb_sid_t *sid) 270 { 271 return (smb_sid_indomain(nt_domain_local_sid(), sid)); 272 } 273 #endif /* _KERNEL */ 274 275 /* 276 * smb_sid_tostr 277 * 278 * Fill in the passed buffer with the string form of the given 279 * binary sid. 280 */ 281 void 282 smb_sid_tostr(smb_sid_t *sid, char *strsid) 283 { 284 char *p = strsid; 285 int i; 286 287 if (sid == NULL || strsid == NULL) 288 return; 289 290 (void) sprintf(p, "S-%d-", sid->sid_revision); 291 while (*p) 292 p++; 293 294 for (i = 0; i < NT_SID_AUTH_MAX; ++i) { 295 if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { 296 (void) sprintf(p, "%d", sid->sid_authority[i]); 297 while (*p) 298 p++; 299 } 300 } 301 302 for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) { 303 (void) sprintf(p, "-%u", sid->sid_subauth[i]); 304 while (*p) 305 p++; 306 } 307 } 308 309 /* 310 * smb_sid_fromstr 311 * 312 * Converts a SID in string form to a SID structure. There are lots of 313 * simplifying assumptions in here. The memory for the SID is allocated 314 * as if it was the largest possible SID; the caller is responsible for 315 * freeing the memory when it is no longer required. We assume that the 316 * string starts with "S-1-" and that the authority is held in the last 317 * byte, which should be okay for most situations. It also assumes the 318 * sub-authorities are in decimal format. 319 * 320 * On success, a pointer to a SID is returned. Otherwise a null pointer 321 * is returned. 322 */ 323 #ifdef _KERNEL 324 smb_sid_t * 325 smb_sid_fromstr(char *sidstr) 326 { 327 smb_sid_t *sid; 328 smb_sid_t *retsid; 329 char *p; 330 int size; 331 uint8_t i; 332 unsigned long sua; 333 334 if (sidstr == NULL) 335 return (NULL); 336 337 if (strncmp(sidstr, "S-1-", 4) != 0) 338 return (NULL); 339 340 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 341 sid = kmem_zalloc(size, KM_SLEEP); 342 343 sid->sid_revision = NT_SID_REVISION; 344 sua = 0; 345 (void) ddi_strtoul(&sidstr[4], 0, 10, &sua); 346 sid->sid_authority[5] = (uint8_t)sua; 347 348 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 349 while (*p && *p == '-') 350 ++p; 351 352 if (*p < '0' || *p > '9') { 353 kmem_free(sid, size); 354 return (NULL); 355 } 356 357 sua = 0; 358 (void) ddi_strtoul(p, 0, 10, &sua); 359 sid->sid_subauth[i] = (uint32_t)sua; 360 361 while (*p && *p != '-') 362 ++p; 363 } 364 365 sid->sid_subauthcnt = i; 366 retsid = smb_sid_dup(sid); 367 kmem_free(sid, size); 368 369 return (retsid); 370 } 371 #else /* _KERNEL */ 372 smb_sid_t * 373 smb_sid_fromstr(char *sidstr) 374 { 375 smb_sid_t *sid; 376 char *p; 377 int size; 378 uint8_t i; 379 380 if (sidstr == NULL) 381 return (NULL); 382 383 if (strncmp(sidstr, "S-1-", 4) != 0) 384 return (NULL); 385 386 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 387 388 if ((sid = malloc(size)) == NULL) 389 return (NULL); 390 391 bzero(sid, size); 392 sid->sid_revision = NT_SID_REVISION; 393 sid->sid_authority[5] = atoi(&sidstr[4]); 394 395 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 396 while (*p && *p == '-') 397 ++p; 398 399 if (*p < '0' || *p > '9') { 400 free(sid); 401 return (NULL); 402 } 403 404 sid->sid_subauth[i] = strtoul(p, NULL, 10); 405 406 while (*p && *p != '-') 407 ++p; 408 } 409 410 sid->sid_subauthcnt = i; 411 return (sid); 412 } 413 #endif /* _KERNEL */ 414 415 /* 416 * smb_sid_type2str 417 * 418 * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE 419 * provides the context for a SID, i.e. the type of resource to which 420 * it refers. 421 */ 422 char * 423 smb_sid_type2str(uint16_t snu_id) 424 { 425 static char *snu_name[] = { 426 "SidTypeSidPrefix", 427 "SidTypeUser", 428 "SidTypeGroup", 429 "SidTypeDomain", 430 "SidTypeAlias", 431 "SidTypeWellKnownGroup", 432 "SidTypeDeletedAccount", 433 "SidTypeInvalid", 434 "SidTypeUnknown" 435 }; 436 437 if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0])))) 438 return (snu_name[snu_id]); 439 440 return (snu_name[SidTypeUnknown]); 441 } 442 443 static smb_sid_t * 444 smb_sid_alloc(size_t size) 445 { 446 smb_sid_t *sid; 447 #ifdef _KERNEL 448 sid = kmem_alloc(size, KM_SLEEP); 449 #else 450 sid = malloc(size); 451 #endif 452 return (sid); 453 } 454 455 void 456 smb_sid_free(smb_sid_t *sid) 457 { 458 #ifdef _KERNEL 459 if (sid == NULL) 460 return; 461 462 kmem_free(sid, smb_sid_len(sid)); 463 #else 464 free(sid); 465 #endif 466 } 467