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