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