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