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