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 return (smb_sid_indomain(nt_domain_local_sid(), sid)); 269 } 270 #endif /* _KERNEL */ 271 272 /* 273 * smb_sid_tostr 274 * 275 * Fill in the passed buffer with the string form of the given 276 * binary sid. 277 */ 278 void 279 smb_sid_tostr(smb_sid_t *sid, char *strsid) 280 { 281 char *p = strsid; 282 int i; 283 284 if (sid == NULL || strsid == NULL) 285 return; 286 287 (void) sprintf(p, "S-%d-", sid->sid_revision); 288 while (*p) 289 p++; 290 291 for (i = 0; i < NT_SID_AUTH_MAX; ++i) { 292 if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { 293 (void) sprintf(p, "%d", sid->sid_authority[i]); 294 while (*p) 295 p++; 296 } 297 } 298 299 for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) { 300 (void) sprintf(p, "-%u", sid->sid_subauth[i]); 301 while (*p) 302 p++; 303 } 304 } 305 306 /* 307 * smb_sid_fromstr 308 * 309 * Converts a SID in string form to a SID structure. There are lots of 310 * simplifying assumptions in here. The memory for the SID is allocated 311 * as if it was the largest possible SID; the caller is responsible for 312 * freeing the memory when it is no longer required. We assume that the 313 * string starts with "S-1-" and that the authority is held in the last 314 * byte, which should be okay for most situations. It also assumes the 315 * sub-authorities are in decimal format. 316 * 317 * On success, a pointer to a SID is returned. Otherwise a null pointer 318 * is returned. 319 */ 320 #ifdef _KERNEL 321 smb_sid_t * 322 smb_sid_fromstr(char *sidstr) 323 { 324 smb_sid_t *sid; 325 smb_sid_t *retsid; 326 char *p; 327 int size; 328 uint8_t i; 329 unsigned long sua; 330 331 if (sidstr == NULL) 332 return (NULL); 333 334 if (strncmp(sidstr, "S-1-", 4) != 0) 335 return (NULL); 336 337 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 338 sid = kmem_zalloc(size, KM_SLEEP); 339 340 sid->sid_revision = NT_SID_REVISION; 341 sua = 0; 342 (void) ddi_strtoul(&sidstr[4], 0, 10, &sua); 343 sid->sid_authority[5] = (uint8_t)sua; 344 345 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 346 while (*p && *p == '-') 347 ++p; 348 349 if (*p < '0' || *p > '9') { 350 kmem_free(sid, size); 351 return (NULL); 352 } 353 354 sua = 0; 355 (void) ddi_strtoul(p, 0, 10, &sua); 356 sid->sid_subauth[i] = (uint32_t)sua; 357 358 while (*p && *p != '-') 359 ++p; 360 } 361 362 sid->sid_subauthcnt = i; 363 retsid = smb_sid_dup(sid); 364 kmem_free(sid, size); 365 366 return (retsid); 367 } 368 #else /* _KERNEL */ 369 smb_sid_t * 370 smb_sid_fromstr(char *sidstr) 371 { 372 smb_sid_t *sid; 373 char *p; 374 int size; 375 uint8_t i; 376 377 if (sidstr == NULL) 378 return (NULL); 379 380 if (strncmp(sidstr, "S-1-", 4) != 0) 381 return (NULL); 382 383 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 384 385 if ((sid = malloc(size)) == NULL) 386 return (NULL); 387 388 bzero(sid, size); 389 sid->sid_revision = NT_SID_REVISION; 390 sid->sid_authority[5] = atoi(&sidstr[4]); 391 392 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 393 while (*p && *p == '-') 394 ++p; 395 396 if (*p < '0' || *p > '9') { 397 free(sid); 398 return (NULL); 399 } 400 401 sid->sid_subauth[i] = strtoul(p, NULL, 10); 402 403 while (*p && *p != '-') 404 ++p; 405 } 406 407 sid->sid_subauthcnt = i; 408 return (sid); 409 } 410 #endif /* _KERNEL */ 411 412 /* 413 * smb_sid_type2str 414 * 415 * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE 416 * provides the context for a SID, i.e. the type of resource to which 417 * it refers. 418 */ 419 char * 420 smb_sid_type2str(uint16_t snu_id) 421 { 422 static char *snu_name[] = { 423 "SidTypeSidPrefix", 424 "SidTypeUser", 425 "SidTypeGroup", 426 "SidTypeDomain", 427 "SidTypeAlias", 428 "SidTypeWellKnownGroup", 429 "SidTypeDeletedAccount", 430 "SidTypeInvalid", 431 "SidTypeUnknown" 432 }; 433 434 if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0])))) 435 return (snu_name[snu_id]); 436 437 return (snu_name[SidTypeUnknown]); 438 } 439 440 static smb_sid_t * 441 smb_sid_alloc(size_t size) 442 { 443 smb_sid_t *sid; 444 #ifdef _KERNEL 445 sid = kmem_alloc(size, KM_SLEEP); 446 #else 447 sid = malloc(size); 448 #endif 449 return (sid); 450 } 451 452 void 453 smb_sid_free(smb_sid_t *sid) 454 { 455 #ifdef _KERNEL 456 if (sid == NULL) 457 return; 458 459 kmem_free(sid, smb_sid_len(sid)); 460 #else 461 free(sid); 462 #endif 463 } 464 465 #ifndef _KERNEL 466 void 467 smb_ids_free(smb_ids_t *ids) 468 { 469 smb_id_t *id; 470 int i; 471 472 if ((ids != NULL) && (ids->i_ids != NULL)) { 473 id = ids->i_ids; 474 for (i = 0; i < ids->i_cnt; i++, id++) 475 smb_sid_free(id->i_sid); 476 477 free(ids->i_ids); 478 } 479 } 480 #endif 481