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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 26 */ 27 28 #include <strings.h> 29 #include <smbsrv/libsmb.h> 30 31 extern int smb_pwd_num(void); 32 extern int smb_lgrp_numbydomain(smb_domain_type_t, int *); 33 34 static uint32_t smb_sam_lookup_user(char *, smb_sid_t **); 35 static uint32_t smb_sam_lookup_group(char *, smb_sid_t **); 36 37 /* 38 * Local well-known accounts data structure table and prototypes 39 */ 40 typedef struct smb_lwka { 41 uint32_t lwka_rid; 42 char *lwka_name; 43 uint16_t lwka_type; 44 } smb_lwka_t; 45 46 static smb_lwka_t lwka_tbl[] = { 47 { 500, "Administrator", SidTypeUser }, 48 { 501, "Guest", SidTypeUser }, 49 { 502, "KRBTGT", SidTypeUser }, 50 { 512, "Domain Admins", SidTypeGroup }, 51 { 513, "Domain Users", SidTypeGroup }, 52 { 514, "Domain Guests", SidTypeGroup }, 53 { 516, "Domain Controllers", SidTypeGroup }, 54 { 517, "Cert Publishers", SidTypeGroup }, 55 { 518, "Schema Admins", SidTypeGroup }, 56 { 519, "Enterprise Admins", SidTypeGroup }, 57 { 520, "Global Policy Creator Owners", SidTypeGroup }, 58 { 533, "RAS and IAS Servers", SidTypeGroup } 59 }; 60 61 #define SMB_LWKA_NUM (sizeof (lwka_tbl)/sizeof (lwka_tbl[0])) 62 63 static smb_lwka_t *smb_lwka_lookup_name(char *); 64 static smb_lwka_t *smb_lwka_lookup_sid(smb_sid_t *); 65 66 /* 67 * Looks up the given name in local account databases: 68 * 69 * SMB Local users are looked up in /var/smb/smbpasswd 70 * SMB Local groups are looked up in /var/smb/smbgroup.db 71 * 72 * If the account is found, its information is populated 73 * in the passed smb_account_t structure. Caller must free 74 * allocated memories by calling smb_account_free() upon 75 * successful return. 76 * 77 * The type of account is specified by 'type', which can be user, 78 * alias (local group) or unknown. If the caller doesn't know 79 * whether the name is a user or group name then SidTypeUnknown 80 * should be passed. 81 * 82 * If a local user and group have the same name, the user will 83 * always be picked. Note that this situation cannot happen on 84 * Windows systems. 85 * 86 * If a SMB local user/group is found but it turns out that 87 * it'll be mapped to a domain user/group the lookup is considered 88 * failed and NT_STATUS_NONE_MAPPED is returned. 89 * 90 * Return status: 91 * 92 * NT_STATUS_NOT_FOUND This is not a local account 93 * NT_STATUS_NONE_MAPPED It's a local account but cannot be 94 * translated. 95 * other error status codes. 96 */ 97 uint32_t 98 smb_sam_lookup_name(char *domain, char *name, uint16_t type, 99 smb_account_t *account) 100 { 101 smb_domain_t di; 102 smb_sid_t *sid; 103 uint32_t status; 104 smb_lwka_t *lwka; 105 106 bzero(account, sizeof (smb_account_t)); 107 108 if (domain != NULL) { 109 if (!smb_domain_lookup_name(domain, &di) || 110 (di.di_type != SMB_DOMAIN_LOCAL)) 111 return (NT_STATUS_NOT_FOUND); 112 113 /* Only Netbios hostname is accepted */ 114 if (smb_strcasecmp(domain, di.di_nbname, 0) != 0) 115 return (NT_STATUS_NONE_MAPPED); 116 } else { 117 if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di)) 118 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 119 } 120 121 if (smb_strcasecmp(name, di.di_nbname, 0) == 0) { 122 /* This is the local domain name */ 123 account->a_type = SidTypeDomain; 124 account->a_name = strdup(""); 125 account->a_domain = strdup(di.di_nbname); 126 account->a_sid = smb_sid_dup(di.di_binsid); 127 account->a_domsid = smb_sid_dup(di.di_binsid); 128 account->a_rid = (uint32_t)-1; 129 130 if (!smb_account_validate(account)) { 131 smb_account_free(account); 132 return (NT_STATUS_NO_MEMORY); 133 } 134 135 return (NT_STATUS_SUCCESS); 136 } 137 138 if ((lwka = smb_lwka_lookup_name(name)) != NULL) { 139 sid = smb_sid_splice(di.di_binsid, lwka->lwka_rid); 140 type = lwka->lwka_type; 141 } else { 142 switch (type) { 143 case SidTypeUser: 144 status = smb_sam_lookup_user(name, &sid); 145 if (status != NT_STATUS_SUCCESS) 146 return (status); 147 break; 148 149 case SidTypeAlias: 150 status = smb_sam_lookup_group(name, &sid); 151 if (status != NT_STATUS_SUCCESS) 152 return (status); 153 break; 154 155 case SidTypeUnknown: 156 type = SidTypeUser; 157 status = smb_sam_lookup_user(name, &sid); 158 if (status == NT_STATUS_SUCCESS) 159 break; 160 161 if (status == NT_STATUS_NONE_MAPPED) 162 return (status); 163 164 type = SidTypeAlias; 165 status = smb_sam_lookup_group(name, &sid); 166 if (status != NT_STATUS_SUCCESS) 167 return (status); 168 break; 169 170 default: 171 return (NT_STATUS_INVALID_PARAMETER); 172 } 173 } 174 175 account->a_name = strdup(name); 176 account->a_sid = sid; 177 account->a_domain = strdup(di.di_nbname); 178 account->a_domsid = smb_sid_split(sid, &account->a_rid); 179 account->a_type = type; 180 181 if (!smb_account_validate(account)) { 182 smb_account_free(account); 183 return (NT_STATUS_NO_MEMORY); 184 } 185 186 return (NT_STATUS_SUCCESS); 187 } 188 189 /* 190 * Looks up the given SID in local account databases: 191 * 192 * SMB Local users are looked up in /var/smb/smbpasswd 193 * SMB Local groups are looked up in /var/smb/smbgroup.db 194 * 195 * If the account is found, its information is populated 196 * in the passed smb_account_t structure. Caller must free 197 * allocated memories by calling smb_account_free() upon 198 * successful return. 199 * 200 * Return status: 201 * 202 * NT_STATUS_NOT_FOUND This is not a local account 203 * NT_STATUS_NONE_MAPPED It's a local account but cannot be 204 * translated. 205 * other error status codes. 206 */ 207 uint32_t 208 smb_sam_lookup_sid(smb_sid_t *sid, smb_account_t *account) 209 { 210 char hostname[MAXHOSTNAMELEN]; 211 smb_passwd_t smbpw; 212 smb_group_t grp; 213 smb_lwka_t *lwka; 214 smb_domain_t di; 215 uint32_t rid; 216 uid_t id; 217 int id_type; 218 int rc; 219 220 bzero(account, sizeof (smb_account_t)); 221 222 if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di)) 223 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 224 225 if (smb_sid_cmp(sid, di.di_binsid)) { 226 /* This is the local domain SID */ 227 account->a_type = SidTypeDomain; 228 account->a_name = strdup(""); 229 account->a_domain = strdup(di.di_nbname); 230 account->a_sid = smb_sid_dup(sid); 231 account->a_domsid = smb_sid_dup(sid); 232 account->a_rid = (uint32_t)-1; 233 234 if (!smb_account_validate(account)) { 235 smb_account_free(account); 236 return (NT_STATUS_NO_MEMORY); 237 } 238 239 return (NT_STATUS_SUCCESS); 240 } 241 242 if (!smb_sid_indomain(di.di_binsid, sid)) { 243 /* This is not a local SID */ 244 return (NT_STATUS_NOT_FOUND); 245 } 246 247 if ((lwka = smb_lwka_lookup_sid(sid)) != NULL) { 248 account->a_type = lwka->lwka_type; 249 account->a_name = strdup(lwka->lwka_name); 250 } else { 251 id_type = SMB_IDMAP_UNKNOWN; 252 if (smb_idmap_getid(sid, &id, &id_type) != IDMAP_SUCCESS) 253 return (NT_STATUS_NONE_MAPPED); 254 255 switch (id_type) { 256 case SMB_IDMAP_USER: 257 account->a_type = SidTypeUser; 258 if (smb_pwd_getpwuid(id, &smbpw) == NULL) 259 return (NT_STATUS_NO_SUCH_USER); 260 261 account->a_name = strdup(smbpw.pw_name); 262 break; 263 264 case SMB_IDMAP_GROUP: 265 account->a_type = SidTypeAlias; 266 (void) smb_sid_getrid(sid, &rid); 267 rc = smb_lgrp_getbyrid(rid, SMB_DOMAIN_LOCAL, &grp); 268 if (rc != SMB_LGRP_SUCCESS) 269 return (NT_STATUS_NO_SUCH_ALIAS); 270 271 account->a_name = strdup(grp.sg_name); 272 smb_lgrp_free(&grp); 273 break; 274 275 default: 276 return (NT_STATUS_NONE_MAPPED); 277 } 278 } 279 280 if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) == 0) 281 account->a_domain = strdup(hostname); 282 account->a_sid = smb_sid_dup(sid); 283 account->a_domsid = smb_sid_split(sid, &account->a_rid); 284 285 if (!smb_account_validate(account)) { 286 smb_account_free(account); 287 return (NT_STATUS_NO_MEMORY); 288 } 289 290 return (NT_STATUS_SUCCESS); 291 } 292 293 /* 294 * Returns number of SMB users, i.e. users who have entry 295 * in /var/smb/smbpasswd 296 */ 297 int 298 smb_sam_usr_cnt(void) 299 { 300 return (smb_pwd_num()); 301 } 302 303 /* 304 * Updates a list of groups in which the given user is a member 305 * by adding any local (SAM) groups. 306 * 307 * We are a member of local groups where the local group 308 * contains either the user's primary SID, or any of their 309 * other SIDs such as from domain groups, SID history, etc. 310 * We can have indirect membership via domain groups. 311 */ 312 uint32_t 313 smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids) 314 { 315 smb_ids_t new_gids; 316 smb_id_t *ids, *new_ids; 317 smb_giter_t gi; 318 smb_group_t lgrp; 319 int i, gcnt, total_cnt; 320 uint32_t ret; 321 boolean_t member; 322 323 /* 324 * First pass: count groups to be added (gcnt) 325 */ 326 gcnt = 0; 327 if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) 328 return (NT_STATUS_INTERNAL_ERROR); 329 330 while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) { 331 member = B_FALSE; 332 if (smb_lgrp_is_member(&lgrp, user_sid)) 333 member = B_TRUE; 334 else for (i = 0, ids = gids->i_ids; 335 i < gids->i_cnt; i++, ids++) { 336 if (smb_lgrp_is_member(&lgrp, ids->i_sid)) { 337 member = B_TRUE; 338 break; 339 } 340 } 341 /* Careful: only count lgrp once */ 342 if (member) 343 gcnt++; 344 smb_lgrp_free(&lgrp); 345 } 346 smb_lgrp_iterclose(&gi); 347 348 if (gcnt == 0) 349 return (NT_STATUS_SUCCESS); 350 351 /* 352 * Second pass: add to groups list. 353 * Do not modify gcnt after here. 354 */ 355 if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) 356 return (NT_STATUS_INTERNAL_ERROR); 357 358 /* 359 * Expand the list (copy to a new, larger one) 360 * Note: were're copying pointers from the old 361 * array to the new (larger) array, and then 362 * adding new pointers after what we copied. 363 */ 364 ret = 0; 365 new_gids.i_cnt = gids->i_cnt; 366 total_cnt = gids->i_cnt + gcnt; 367 new_gids.i_ids = malloc(total_cnt * sizeof (smb_id_t)); 368 if (new_gids.i_ids == NULL) { 369 ret = NT_STATUS_NO_MEMORY; 370 goto out; 371 } 372 (void) memcpy(new_gids.i_ids, gids->i_ids, 373 gids->i_cnt * sizeof (smb_id_t)); 374 new_ids = new_gids.i_ids + gids->i_cnt; 375 (void) memset(new_ids, 0, gcnt * sizeof (smb_id_t)); 376 377 /* 378 * Add group SIDs starting at the end of the 379 * previous list. (new_ids) 380 */ 381 while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) { 382 member = B_FALSE; 383 if (smb_lgrp_is_member(&lgrp, user_sid)) 384 member = B_TRUE; 385 else for (i = 0, ids = gids->i_ids; 386 i < gids->i_cnt; i++, ids++) { 387 if (smb_lgrp_is_member(&lgrp, ids->i_sid)) { 388 member = B_TRUE; 389 break; 390 } 391 } 392 if (member && (new_gids.i_cnt < (gids->i_cnt + gcnt))) { 393 new_ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid); 394 if (new_ids->i_sid == NULL) { 395 smb_lgrp_free(&lgrp); 396 ret = NT_STATUS_NO_MEMORY; 397 goto out; 398 } 399 new_ids->i_attrs = lgrp.sg_attr; 400 new_ids++; 401 new_gids.i_cnt++; 402 } 403 smb_lgrp_free(&lgrp); 404 } 405 406 out: 407 smb_lgrp_iterclose(&gi); 408 409 if (ret != 0) { 410 if (new_gids.i_ids != NULL) { 411 /* 412 * Free only the new sids we added. 413 * The old ones were copied ptrs. 414 */ 415 ids = new_gids.i_ids + gids->i_cnt; 416 for (i = 0; i < gcnt; i++, ids++) { 417 smb_sid_free(ids->i_sid); 418 } 419 free(new_gids.i_ids); 420 } 421 return (ret); 422 } 423 424 /* 425 * Success! Update passed gids and 426 * free the old array. 427 */ 428 free(gids->i_ids); 429 *gids = new_gids; 430 431 return (NT_STATUS_SUCCESS); 432 } 433 434 /* 435 * Returns the number of built-in or local groups stored 436 * in /var/smb/smbgroup.db 437 */ 438 int 439 smb_sam_grp_cnt(smb_domain_type_t dtype) 440 { 441 int grpcnt; 442 int rc; 443 444 switch (dtype) { 445 case SMB_DOMAIN_BUILTIN: 446 rc = smb_lgrp_numbydomain(SMB_DOMAIN_BUILTIN, &grpcnt); 447 break; 448 449 case SMB_DOMAIN_LOCAL: 450 rc = smb_lgrp_numbydomain(SMB_DOMAIN_LOCAL, &grpcnt); 451 break; 452 453 default: 454 rc = SMB_LGRP_INVALID_ARG; 455 } 456 457 return ((rc == SMB_LGRP_SUCCESS) ? grpcnt : 0); 458 } 459 460 /* 461 * Determines whether the given SID is a member of the group 462 * specified by gname. 463 */ 464 boolean_t 465 smb_sam_grp_ismember(const char *gname, smb_sid_t *sid) 466 { 467 smb_group_t grp; 468 boolean_t ismember = B_FALSE; 469 470 if (smb_lgrp_getbyname((char *)gname, &grp) == SMB_LGRP_SUCCESS) { 471 ismember = smb_lgrp_is_member(&grp, sid); 472 smb_lgrp_free(&grp); 473 } 474 475 return (ismember); 476 } 477 478 /* 479 * Frees memories allocated for the passed account fields. 480 */ 481 void 482 smb_account_free(smb_account_t *account) 483 { 484 free(account->a_name); 485 free(account->a_domain); 486 smb_sid_free(account->a_sid); 487 smb_sid_free(account->a_domsid); 488 } 489 490 /* 491 * Validates the given account. 492 */ 493 boolean_t 494 smb_account_validate(smb_account_t *account) 495 { 496 return ((account->a_name != NULL) && (account->a_sid != NULL) && 497 (account->a_domain != NULL) && (account->a_domsid != NULL)); 498 } 499 500 /* 501 * Lookup local SMB user account database (/var/smb/smbpasswd) 502 * if there's a match query its SID from idmap service and make 503 * sure the SID is a local SID. 504 * 505 * The memory for the returned SID must be freed by the caller. 506 */ 507 static uint32_t 508 smb_sam_lookup_user(char *name, smb_sid_t **sid) 509 { 510 smb_passwd_t smbpw; 511 512 if (smb_pwd_getpwnam(name, &smbpw) == NULL) 513 return (NT_STATUS_NO_SUCH_USER); 514 515 if (smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, sid) 516 != IDMAP_SUCCESS) 517 return (NT_STATUS_NONE_MAPPED); 518 519 if (!smb_sid_islocal(*sid)) { 520 smb_sid_free(*sid); 521 return (NT_STATUS_NONE_MAPPED); 522 } 523 524 return (NT_STATUS_SUCCESS); 525 } 526 527 /* 528 * Lookup local SMB group account database (/var/smb/smbgroup.db) 529 * The memory for the returned SID must be freed by the caller. 530 */ 531 static uint32_t 532 smb_sam_lookup_group(char *name, smb_sid_t **sid) 533 { 534 smb_group_t grp; 535 536 if (smb_lgrp_getbyname(name, &grp) != SMB_LGRP_SUCCESS) 537 return (NT_STATUS_NO_SUCH_ALIAS); 538 539 *sid = smb_sid_dup(grp.sg_id.gs_sid); 540 smb_lgrp_free(&grp); 541 542 return ((*sid == NULL) ? NT_STATUS_NO_MEMORY : NT_STATUS_SUCCESS); 543 } 544 545 static smb_lwka_t * 546 smb_lwka_lookup_name(char *name) 547 { 548 int i; 549 550 for (i = 0; i < SMB_LWKA_NUM; i++) { 551 if (smb_strcasecmp(name, lwka_tbl[i].lwka_name, 0) == 0) 552 return (&lwka_tbl[i]); 553 } 554 555 return (NULL); 556 } 557 558 static smb_lwka_t * 559 smb_lwka_lookup_sid(smb_sid_t *sid) 560 { 561 uint32_t rid; 562 int i; 563 564 (void) smb_sid_getrid(sid, &rid); 565 if (rid > 999) 566 return (NULL); 567 568 for (i = 0; i < SMB_LWKA_NUM; i++) { 569 if (rid == lwka_tbl[i].lwka_rid) 570 return (&lwka_tbl[i]); 571 } 572 573 return (NULL); 574 } 575 576 /* 577 * smb_sid_islocal 578 * 579 * Check a SID to see if it belongs to the local domain. 580 */ 581 boolean_t 582 smb_sid_islocal(smb_sid_t *sid) 583 { 584 smb_domain_t di; 585 boolean_t islocal = B_FALSE; 586 587 if (smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di)) 588 islocal = smb_sid_indomain(di.di_binsid, sid); 589 590 return (islocal); 591 } 592 593 void 594 smb_ids_free(smb_ids_t *ids) 595 { 596 smb_id_t *id; 597 int i; 598 599 if ((ids != NULL) && (ids->i_ids != NULL)) { 600 id = ids->i_ids; 601 for (i = 0; i < ids->i_cnt; i++, id++) 602 smb_sid_free(id->i_sid); 603 604 free(ids->i_ids); 605 } 606 } 607