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