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 * 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 break; 264 265 case SMB_IDMAP_GROUP: 266 account->a_type = SidTypeAlias; 267 (void) smb_sid_getrid(sid, &rid); 268 rc = smb_lgrp_getbyrid(rid, SMB_DOMAIN_LOCAL, &grp); 269 if (rc != SMB_LGRP_SUCCESS) 270 return (NT_STATUS_NO_SUCH_ALIAS); 271 272 account->a_name = strdup(grp.sg_name); 273 smb_lgrp_free(&grp); 274 break; 275 276 default: 277 return (NT_STATUS_NONE_MAPPED); 278 } 279 } 280 281 if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) == 0) 282 account->a_domain = strdup(hostname); 283 account->a_sid = smb_sid_dup(sid); 284 account->a_domsid = smb_sid_split(sid, &account->a_rid); 285 286 if (!smb_account_validate(account)) { 287 smb_account_free(account); 288 return (NT_STATUS_NO_MEMORY); 289 } 290 291 return (NT_STATUS_SUCCESS); 292 } 293 294 /* 295 * Returns number of SMB users, i.e. users who have entry 296 * in /var/smb/smbpasswd 297 */ 298 int 299 smb_sam_usr_cnt(void) 300 { 301 return (smb_pwd_num()); 302 } 303 304 /* 305 * Updates a list of groups in which the given user is a member 306 * by adding any local (SAM) groups. 307 * 308 * We are a member of local groups where the local group 309 * contains either the user's primary SID, or any of their 310 * other SIDs such as from domain groups, SID history, etc. 311 * We can have indirect membership via domain groups. 312 */ 313 uint32_t 314 smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids) 315 { 316 smb_ids_t new_gids; 317 smb_id_t *ids, *new_ids; 318 smb_giter_t gi; 319 smb_group_t lgrp; 320 int i, gcnt, total_cnt; 321 uint32_t ret; 322 boolean_t member; 323 324 /* 325 * First pass: count groups to be added (gcnt) 326 */ 327 gcnt = 0; 328 if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) 329 return (NT_STATUS_INTERNAL_ERROR); 330 331 while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) { 332 member = B_FALSE; 333 if (smb_lgrp_is_member(&lgrp, user_sid)) 334 member = B_TRUE; 335 else for (i = 0, ids = gids->i_ids; 336 i < gids->i_cnt; i++, ids++) { 337 if (smb_lgrp_is_member(&lgrp, ids->i_sid)) { 338 member = B_TRUE; 339 break; 340 } 341 } 342 /* Careful: only count lgrp once */ 343 if (member) 344 gcnt++; 345 smb_lgrp_free(&lgrp); 346 } 347 smb_lgrp_iterclose(&gi); 348 349 if (gcnt == 0) 350 return (NT_STATUS_SUCCESS); 351 352 /* 353 * Second pass: add to groups list. 354 * Do not modify gcnt after here. 355 */ 356 if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS) 357 return (NT_STATUS_INTERNAL_ERROR); 358 359 /* 360 * Expand the list (copy to a new, larger one) 361 * Note: were're copying pointers from the old 362 * array to the new (larger) array, and then 363 * adding new pointers after what we copied. 364 */ 365 ret = 0; 366 new_gids.i_cnt = gids->i_cnt; 367 total_cnt = gids->i_cnt + gcnt; 368 new_gids.i_ids = malloc(total_cnt * sizeof (smb_id_t)); 369 if (new_gids.i_ids == NULL) { 370 ret = NT_STATUS_NO_MEMORY; 371 goto out; 372 } 373 (void) memcpy(new_gids.i_ids, gids->i_ids, 374 gids->i_cnt * sizeof (smb_id_t)); 375 new_ids = new_gids.i_ids + gids->i_cnt; 376 (void) memset(new_ids, 0, gcnt * sizeof (smb_id_t)); 377 378 /* 379 * Add group SIDs starting at the end of the 380 * previous list. (new_ids) 381 */ 382 while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) { 383 member = B_FALSE; 384 if (smb_lgrp_is_member(&lgrp, user_sid)) 385 member = B_TRUE; 386 else for (i = 0, ids = gids->i_ids; 387 i < gids->i_cnt; i++, ids++) { 388 if (smb_lgrp_is_member(&lgrp, ids->i_sid)) { 389 member = B_TRUE; 390 break; 391 } 392 } 393 if (member && (new_gids.i_cnt < (gids->i_cnt + gcnt))) { 394 new_ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid); 395 if (new_ids->i_sid == NULL) { 396 smb_lgrp_free(&lgrp); 397 ret = NT_STATUS_NO_MEMORY; 398 goto out; 399 } 400 new_ids->i_attrs = lgrp.sg_attr; 401 new_ids++; 402 new_gids.i_cnt++; 403 } 404 smb_lgrp_free(&lgrp); 405 } 406 407 out: 408 smb_lgrp_iterclose(&gi); 409 410 if (ret != 0) { 411 if (new_gids.i_ids != NULL) { 412 /* 413 * Free only the new sids we added. 414 * The old ones were copied ptrs. 415 */ 416 ids = new_gids.i_ids + gids->i_cnt; 417 for (i = 0; i < gcnt; i++, ids++) { 418 smb_sid_free(ids->i_sid); 419 } 420 free(new_gids.i_ids); 421 } 422 return (ret); 423 } 424 425 /* 426 * Success! Update passed gids and 427 * free the old array. 428 */ 429 free(gids->i_ids); 430 *gids = new_gids; 431 432 return (NT_STATUS_SUCCESS); 433 } 434 435 /* 436 * Returns the number of built-in or local groups stored 437 * in /var/smb/smbgroup.db 438 */ 439 int 440 smb_sam_grp_cnt(smb_domain_type_t dtype) 441 { 442 int grpcnt; 443 int rc; 444 445 switch (dtype) { 446 case SMB_DOMAIN_BUILTIN: 447 rc = smb_lgrp_numbydomain(SMB_DOMAIN_BUILTIN, &grpcnt); 448 break; 449 450 case SMB_DOMAIN_LOCAL: 451 rc = smb_lgrp_numbydomain(SMB_DOMAIN_LOCAL, &grpcnt); 452 break; 453 454 default: 455 rc = SMB_LGRP_INVALID_ARG; 456 } 457 458 return ((rc == SMB_LGRP_SUCCESS) ? grpcnt : 0); 459 } 460 461 /* 462 * Determines whether the given SID is a member of the group 463 * specified by gname. 464 */ 465 boolean_t 466 smb_sam_grp_ismember(const char *gname, smb_sid_t *sid) 467 { 468 smb_group_t grp; 469 boolean_t ismember = B_FALSE; 470 471 if (smb_lgrp_getbyname((char *)gname, &grp) == SMB_LGRP_SUCCESS) { 472 ismember = smb_lgrp_is_member(&grp, sid); 473 smb_lgrp_free(&grp); 474 } 475 476 return (ismember); 477 } 478 479 /* 480 * Frees memories allocated for the passed account fields. 481 * Initializes @account after all. 482 */ 483 void 484 smb_account_free(smb_account_t *account) 485 { 486 free(account->a_name); 487 free(account->a_domain); 488 smb_sid_free(account->a_sid); 489 smb_sid_free(account->a_domsid); 490 491 bzero(account, sizeof (smb_account_t)); 492 } 493 494 /* 495 * Validates the given account. 496 */ 497 boolean_t 498 smb_account_validate(smb_account_t *account) 499 { 500 return ((account->a_name != NULL) && (account->a_sid != NULL) && 501 (account->a_domain != NULL) && (account->a_domsid != NULL)); 502 } 503 504 /* 505 * Lookup local SMB user account database (/var/smb/smbpasswd) 506 * if there's a match query its SID from idmap service and make 507 * sure the SID is a local SID. 508 * 509 * The memory for the returned SID must be freed by the caller. 510 */ 511 static uint32_t 512 smb_sam_lookup_user(char *name, smb_sid_t **sid) 513 { 514 smb_passwd_t smbpw; 515 516 if (smb_pwd_getpwnam(name, &smbpw) == NULL) 517 return (NT_STATUS_NO_SUCH_USER); 518 519 if (smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, sid) 520 != IDMAP_SUCCESS) 521 return (NT_STATUS_NONE_MAPPED); 522 523 if (!smb_sid_islocal(*sid)) { 524 smb_sid_free(*sid); 525 return (NT_STATUS_NONE_MAPPED); 526 } 527 528 return (NT_STATUS_SUCCESS); 529 } 530 531 /* 532 * Lookup local SMB group account database (/var/smb/smbgroup.db) 533 * The memory for the returned SID must be freed by the caller. 534 */ 535 static uint32_t 536 smb_sam_lookup_group(char *name, smb_sid_t **sid) 537 { 538 smb_group_t grp; 539 540 if (smb_lgrp_getbyname(name, &grp) != SMB_LGRP_SUCCESS) 541 return (NT_STATUS_NO_SUCH_ALIAS); 542 543 *sid = smb_sid_dup(grp.sg_id.gs_sid); 544 smb_lgrp_free(&grp); 545 546 return ((*sid == NULL) ? NT_STATUS_NO_MEMORY : NT_STATUS_SUCCESS); 547 } 548 549 static smb_lwka_t * 550 smb_lwka_lookup_name(char *name) 551 { 552 int i; 553 554 for (i = 0; i < SMB_LWKA_NUM; i++) { 555 if (smb_strcasecmp(name, lwka_tbl[i].lwka_name, 0) == 0) 556 return (&lwka_tbl[i]); 557 } 558 559 return (NULL); 560 } 561 562 static smb_lwka_t * 563 smb_lwka_lookup_sid(smb_sid_t *sid) 564 { 565 uint32_t rid; 566 int i; 567 568 (void) smb_sid_getrid(sid, &rid); 569 if (rid > 999) 570 return (NULL); 571 572 for (i = 0; i < SMB_LWKA_NUM; i++) { 573 if (rid == lwka_tbl[i].lwka_rid) 574 return (&lwka_tbl[i]); 575 } 576 577 return (NULL); 578 } 579 580 /* 581 * smb_sid_islocal 582 * 583 * Check a SID to see if it belongs to the local domain. 584 */ 585 boolean_t 586 smb_sid_islocal(smb_sid_t *sid) 587 { 588 smb_domain_t di; 589 boolean_t islocal = B_FALSE; 590 591 if (smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di)) 592 islocal = smb_sid_indomain(di.di_binsid, sid); 593 594 return (islocal); 595 } 596 597 void 598 smb_ids_free(smb_ids_t *ids) 599 { 600 smb_id_t *id; 601 int i; 602 603 if ((ids != NULL) && (ids->i_ids != NULL)) { 604 id = ids->i_ids; 605 for (i = 0; i < ids->i_cnt; i++, id++) 606 smb_sid_free(id->i_sid); 607 608 free(ids->i_ids); 609 } 610 } 611