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