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 2014 Nexenta Systems, 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/types.h> 33 #include <sys/systm.h> 34 #include <sys/sunddi.h> 35 #endif /* !_KERNEL && !_FAKE_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 int size; 155 156 if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0)) 157 return (NULL); 158 159 /* 160 * We will reduce sid_subauthcnt by one, because 161 * the domain SID does not include the RID. 162 */ 163 size = smb_sid_len(sid) - sizeof (uint32_t); 164 if ((domsid = smb_sid_alloc(size)) == NULL) 165 return (NULL); 166 167 bcopy(sid, domsid, size); 168 domsid->sid_subauthcnt = sid->sid_subauthcnt - 1; 169 170 if (rid) 171 *rid = sid->sid_subauth[sid->sid_subauthcnt - 1]; 172 173 return (domsid); 174 } 175 176 /* 177 * smb_sid_splitstr 178 * 179 * Takes a full sid in string form and split it into a domain sid and a 180 * relative id (rid). 181 * 182 * IMPORTANT: The original sid is modified in place. This function assumes 183 * given SID is in valid string format. 184 */ 185 int 186 smb_sid_splitstr(char *strsid, uint32_t *rid) 187 { 188 char *p; 189 190 if ((p = strrchr(strsid, '-')) == NULL) 191 return (-1); 192 193 *p++ = '\0'; 194 if (rid) { 195 #if defined(_KERNEL) || defined(_FAKE_KERNEL) 196 unsigned long sua = 0; 197 (void) ddi_strtoul(p, NULL, 10, &sua); 198 *rid = (uint32_t)sua; 199 #else 200 *rid = strtoul(p, NULL, 10); 201 #endif 202 } 203 204 return (0); 205 } 206 207 /* 208 * smb_sid_cmp 209 * 210 * Compare two SIDs and return a boolean result. The checks are ordered 211 * such that components that are more likely to differ are checked 212 * first. For example, after checking that the SIDs contain the same 213 * sid_subauthcnt, we check the sub-authorities in reverse order because 214 * the RID is the most likely differentiator between two SIDs, i.e. 215 * they are probably going to be in the same domain. 216 */ 217 boolean_t 218 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2) 219 { 220 int i; 221 222 if (sid1 == NULL || sid2 == NULL) 223 return (B_FALSE); 224 225 if (sid1->sid_subauthcnt != sid2->sid_subauthcnt || 226 sid1->sid_revision != sid2->sid_revision) 227 return (B_FALSE); 228 229 for (i = sid1->sid_subauthcnt - 1; i >= 0; --i) 230 if (sid1->sid_subauth[i] != sid2->sid_subauth[i]) 231 return (B_FALSE); 232 233 if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX)) 234 return (B_FALSE); 235 236 return (B_TRUE); 237 } 238 239 /* 240 * smb_sid_indomain 241 * 242 * Check if given SID is in given domain. 243 */ 244 boolean_t 245 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid) 246 { 247 int i; 248 249 if (sid == NULL || domain_sid == NULL) 250 return (B_FALSE); 251 252 if (domain_sid->sid_revision != sid->sid_revision || 253 sid->sid_subauthcnt < domain_sid->sid_subauthcnt) 254 return (B_FALSE); 255 256 for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i) 257 if (domain_sid->sid_subauth[i] != sid->sid_subauth[i]) 258 return (B_FALSE); 259 260 if (bcmp(&domain_sid->sid_authority, &sid->sid_authority, 261 NT_SID_AUTH_MAX)) 262 return (B_FALSE); 263 264 return (B_TRUE); 265 } 266 267 /* 268 * smb_sid_tostr 269 * 270 * Fill in the passed buffer with the string form of the given 271 * binary sid. 272 */ 273 void 274 smb_sid_tostr(const smb_sid_t *sid, char *strsid) 275 { 276 char *p = strsid; 277 int i; 278 279 if (sid == NULL || strsid == NULL) 280 return; 281 282 (void) sprintf(p, "S-%d-", sid->sid_revision); 283 while (*p) 284 p++; 285 286 for (i = 0; i < NT_SID_AUTH_MAX; ++i) { 287 if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) { 288 (void) sprintf(p, "%d", sid->sid_authority[i]); 289 while (*p) 290 p++; 291 } 292 } 293 294 for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) { 295 (void) sprintf(p, "-%u", sid->sid_subauth[i]); 296 while (*p) 297 p++; 298 } 299 } 300 301 /* 302 * smb_sid_fromstr 303 * 304 * Converts a SID in string form to a SID structure. There are lots of 305 * simplifying assumptions in here. The memory for the SID is allocated 306 * as if it was the largest possible SID; the caller is responsible for 307 * freeing the memory when it is no longer required. We assume that the 308 * string starts with "S-1-" and that the authority is held in the last 309 * byte, which should be okay for most situations. It also assumes the 310 * sub-authorities are in decimal format. 311 * 312 * On success, a pointer to a SID is returned. Otherwise a null pointer 313 * is returned. 314 */ 315 #if defined(_KERNEL) || defined(_FAKE_KERNEL) 316 smb_sid_t * 317 smb_sid_fromstr(const char *sidstr) 318 { 319 smb_sid_t *sid; 320 smb_sid_t *retsid; 321 const char *p; 322 int size; 323 uint8_t i; 324 unsigned long sua; 325 326 if (sidstr == NULL) 327 return (NULL); 328 329 if (strncmp(sidstr, "S-1-", 4) != 0) 330 return (NULL); 331 332 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 333 sid = kmem_zalloc(size, KM_SLEEP); 334 335 sid->sid_revision = NT_SID_REVISION; 336 sua = 0; 337 (void) ddi_strtoul(&sidstr[4], 0, 10, &sua); 338 sid->sid_authority[5] = (uint8_t)sua; 339 340 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 341 while (*p && *p == '-') 342 ++p; 343 344 if (*p < '0' || *p > '9') { 345 kmem_free(sid, size); 346 return (NULL); 347 } 348 349 sua = 0; 350 (void) ddi_strtoul(p, 0, 10, &sua); 351 sid->sid_subauth[i] = (uint32_t)sua; 352 353 while (*p && *p != '-') 354 ++p; 355 } 356 357 sid->sid_subauthcnt = i; 358 retsid = smb_sid_dup(sid); 359 kmem_free(sid, size); 360 361 return (retsid); 362 } 363 #else /* _KERNEL */ 364 smb_sid_t * 365 smb_sid_fromstr(const char *sidstr) 366 { 367 smb_sid_t *sid; 368 const char *p; 369 int size; 370 uint8_t i; 371 372 if (sidstr == NULL) 373 return (NULL); 374 375 if (strncmp(sidstr, "S-1-", 4) != 0) 376 return (NULL); 377 378 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t)); 379 380 if ((sid = malloc(size)) == NULL) 381 return (NULL); 382 383 bzero(sid, size); 384 sid->sid_revision = NT_SID_REVISION; 385 sid->sid_authority[5] = atoi(&sidstr[4]); 386 387 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) { 388 while (*p && *p == '-') 389 ++p; 390 391 if (*p < '0' || *p > '9') { 392 free(sid); 393 return (NULL); 394 } 395 396 sid->sid_subauth[i] = strtoul(p, NULL, 10); 397 398 while (*p && *p != '-') 399 ++p; 400 } 401 402 sid->sid_subauthcnt = i; 403 return (sid); 404 } 405 #endif /* _KERNEL */ 406 407 /* 408 * smb_sid_type2str 409 * 410 * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE 411 * provides the context for a SID, i.e. the type of resource to which 412 * it refers. 413 */ 414 char * 415 smb_sid_type2str(uint16_t snu_id) 416 { 417 static char *snu_name[] = { 418 "SidTypeSidPrefix", 419 "SidTypeUser", 420 "SidTypeGroup", 421 "SidTypeDomain", 422 "SidTypeAlias", 423 "SidTypeWellKnownGroup", 424 "SidTypeDeletedAccount", 425 "SidTypeInvalid", 426 "SidTypeUnknown", 427 "SidTypeComputer", 428 "SidTypeLabel" 429 }; 430 431 if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0])))) 432 return (snu_name[snu_id]); 433 434 return (snu_name[SidTypeUnknown]); 435 } 436 437 static smb_sid_t * 438 smb_sid_alloc(size_t size) 439 { 440 smb_sid_t *sid; 441 #if defined(_KERNEL) || defined(_FAKE_KERNEL) 442 sid = kmem_alloc(size, KM_SLEEP); 443 #else 444 sid = malloc(size); 445 #endif 446 return (sid); 447 } 448 449 void 450 smb_sid_free(smb_sid_t *sid) 451 { 452 #if defined(_KERNEL) || defined(_FAKE_KERNEL) 453 if (sid == NULL) 454 return; 455 456 kmem_free(sid, smb_sid_len(sid)); 457 #else 458 free(sid); 459 #endif 460 } 461