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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Utility functions to support the RPC interface library. 30 */ 31 32 #include <stdio.h> 33 #include <stdarg.h> 34 #include <strings.h> 35 #include <unistd.h> 36 #include <netdb.h> 37 #include <stdlib.h> 38 #include <pwd.h> 39 #include <grp.h> 40 41 #include <sys/time.h> 42 #include <sys/systm.h> 43 44 #include <smbsrv/libsmb.h> 45 #include <smbsrv/libsmbrdr.h> 46 #include <smbsrv/libsmbns.h> 47 #include <smbsrv/libmlsvc.h> 48 49 #include <smbsrv/smbinfo.h> 50 #include <smbsrv/ntsid.h> 51 #include <smbsrv/lsalib.h> 52 #include <smbsrv/samlib.h> 53 #include <smbsrv/mlsvc_util.h> 54 #include <smbsrv/mlsvc.h> 55 56 extern int netr_open(char *, char *, mlsvc_handle_t *); 57 extern int netr_close(mlsvc_handle_t *); 58 extern DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD); 59 extern int mlsvc_user_getauth(char *, char *, smb_auth_info_t *); 60 61 static int mlsvc_lookup_local_name(char *name, nt_sid_t **sid); 62 static int mlsvc_lookup_nt_name(char *name, nt_sid_t **sid); 63 static int mlsvc_lookup_nt_sid(nt_sid_t *sid, char *buf, int bufsize); 64 65 /* 66 * Compare the supplied domain name with the local hostname. 67 * We need to deal with both server names and fully-qualified 68 * domain names. 69 * 70 * Returns: 71 * 0 The specified domain is not the local domain, 72 * 1 The Specified domain is the local domain. 73 * -1 Invalid parameter or unable to get the local 74 * system information. 75 */ 76 int 77 mlsvc_is_local_domain(const char *domain) 78 { 79 char hostname[MAXHOSTNAMELEN]; 80 uint32_t mode; 81 int rc; 82 83 if (strchr(domain, '.') != NULL) 84 rc = smb_getfqhostname(hostname, MAXHOSTNAMELEN); 85 else 86 rc = smb_gethostname(hostname, MAXHOSTNAMELEN, 1); 87 88 if (rc != 0) 89 return (-1); 90 91 rc = strcasecmp(domain, hostname); 92 mode = smb_get_security_mode(); 93 94 if ((rc == 0) || (mode == SMB_SECMODE_WORKGRP)) 95 return (1); 96 97 return (0); 98 } 99 100 /* 101 * mlsvc_lookup_name 102 * 103 * Lookup a name in the specified domain and translate it to a SID. 104 * If the name is in the NT domain, it may refer to a user, group or 105 * alias. Otherwise it must refer to a UNIX username. The memory for 106 * the sid is allocated using malloc so the caller should call free 107 * when it is no longer required. 108 * 109 * On success, 0 will be returned and sid will point to a local domain 110 * user SID. Otherwise -1 will be returned. 111 */ 112 int 113 mlsvc_lookup_name(char *domain, char *name, nt_sid_t **sid) 114 { 115 if (domain == NULL || name == NULL || sid == NULL) 116 return (-1); 117 118 if (mlsvc_is_local_domain(domain) == 1) 119 return (mlsvc_lookup_local_name(name, sid)); 120 else 121 return (mlsvc_lookup_nt_name(name, sid)); 122 } 123 124 /* 125 * mlsvc_lookup_local_name 126 * 127 * Lookup a name in the local password file and translate it to a SID. 128 * The name must refer to a user. This is a private function intended 129 * to support mlsvc_lookup_name so it doesn't perform any parameter 130 * validation. The memory for the sid is allocated using malloc so the 131 * caller must call free when it is no longer required. 132 * 133 * On success, 0 will be returned and sid will point to a local domain 134 * user SID. Otherwise -1 will be returned. 135 */ 136 static int 137 mlsvc_lookup_local_name(char *name, nt_sid_t **sid) 138 { 139 struct passwd *pw; 140 nt_sid_t *domain_sid; 141 142 if ((pw = getpwnam(name)) == NULL) 143 return (-1); 144 145 if ((domain_sid = nt_domain_local_sid()) == NULL) 146 return (-1); 147 148 *sid = nt_sid_splice(domain_sid, pw->pw_uid); 149 return (0); 150 } 151 152 /* 153 * mlsvc_lookup_nt_name 154 * 155 * Lookup a name in the specified NT domain and translate it to a SID. 156 * The name may refer to a user, group or alias. This is a private 157 * function intended to support mlsvc_lookup_name so it doesn't do any 158 * parameter validation. The memory for the sid is allocated using 159 * malloc so the caller should call free when it is no longer required. 160 * 161 * On success, 0 will be returned and sid will point to an NT domain 162 * user SID. Otherwise -1 will be returned. 163 */ 164 static int 165 mlsvc_lookup_nt_name(char *name, nt_sid_t **sid) 166 { 167 smb_userinfo_t *user_info; 168 169 if ((user_info = mlsvc_alloc_user_info()) == NULL) 170 return (-1); 171 172 if (lsa_lookup_name(0, 0, name, user_info) != 0) 173 return (-1); 174 175 *sid = nt_sid_splice(user_info->domain_sid, user_info->rid); 176 mlsvc_free_user_info(user_info); 177 return (0); 178 } 179 180 /* 181 * mlsvc_lookup_sid 182 * 183 * Lookup a SID and translate it to a name. The name returned may refer 184 * to a domain, user, group or alias dependent on the SID. On success 0 185 * will be returned. Otherwise -1 will be returned. 186 */ 187 int 188 mlsvc_lookup_sid(nt_sid_t *sid, char *buf, int bufsize) 189 { 190 struct passwd *pw; 191 struct group *gr; 192 nt_group_t *grp; 193 DWORD rid; 194 195 if (sid == NULL || buf == NULL) 196 return (-1); 197 198 if (nt_sid_is_local(sid)) { 199 (void) nt_sid_get_rid(sid, &rid); 200 201 switch (SAM_RID_TYPE(rid)) { 202 case SAM_RT_NT_UID: 203 break; 204 205 case SAM_RT_NT_GID: 206 if ((grp = nt_groups_lookup_rid(rid)) == NULL) 207 return (-1); 208 209 (void) strlcpy(buf, grp->name, bufsize); 210 break; 211 212 case SAM_RT_UNIX_UID: 213 if ((pw = getpwuid(SAM_DECODE_RID(rid))) == NULL) 214 return (-1); 215 216 (void) strlcpy(buf, pw->pw_name, bufsize); 217 break; 218 219 case SAM_RT_UNIX_GID: 220 if ((gr = getgrgid(SAM_DECODE_RID(rid))) == NULL) 221 return (-1); 222 223 (void) strlcpy(buf, gr->gr_name, bufsize); 224 break; 225 } 226 227 return (0); 228 } 229 230 return (mlsvc_lookup_nt_sid(sid, buf, bufsize)); 231 } 232 233 /* 234 * mlsvc_lookup_nt_sid 235 * 236 * Lookup an NT SID and translate it to a name. This is a private 237 * function intended to support mlsvc_lookup_sid so it doesn't do any 238 * parameter validation. The input account_name specifies the logon/ 239 * session to be used for the lookup. It doesn't need to have any 240 * association with the SID being looked up. The name returned may 241 * refer to a domain, user, group or alias dependent on the SID. 242 * 243 * On success the name will be copied into buf and 0 will be returned. 244 * Otherwise -1 will be returned. 245 */ 246 static int 247 mlsvc_lookup_nt_sid(nt_sid_t *sid, char *buf, int bufsize) 248 { 249 smb_userinfo_t *user_info; 250 int rc; 251 252 if ((user_info = mlsvc_alloc_user_info()) == NULL) 253 return (-1); 254 255 if ((rc = lsa_lookup_sid(sid, user_info)) == 0) 256 (void) strlcpy(buf, user_info->name, bufsize); 257 258 mlsvc_free_user_info(user_info); 259 return (rc); 260 } 261 262 /* 263 * mlsvc_alloc_user_info 264 * 265 * Allocate a user_info structure and set the contents to zero. A 266 * pointer to the user_info structure is returned. 267 */ 268 smb_userinfo_t * 269 mlsvc_alloc_user_info(void) 270 { 271 smb_userinfo_t *user_info; 272 273 user_info = (smb_userinfo_t *)malloc(sizeof (smb_userinfo_t)); 274 if (user_info == NULL) 275 return (NULL); 276 277 bzero(user_info, sizeof (smb_userinfo_t)); 278 return (user_info); 279 } 280 281 /* 282 * mlsvc_free_user_info 283 * 284 * Free a user_info structure. This function ensures that the contents 285 * of the user_info are freed as well as the user_info itself. 286 */ 287 void 288 mlsvc_free_user_info(smb_userinfo_t *user_info) 289 { 290 if (user_info) { 291 mlsvc_release_user_info(user_info); 292 free(user_info); 293 } 294 } 295 296 /* 297 * mlsvc_release_user_info 298 * 299 * Release the contents of a user_info structure and zero out the 300 * elements but do not free the user_info structure itself. This 301 * function cleans out the structure so that it can be reused without 302 * worrying about stale contents. 303 */ 304 void 305 mlsvc_release_user_info(smb_userinfo_t *user_info) 306 { 307 int i; 308 309 if (user_info == NULL) 310 return; 311 312 free(user_info->name); 313 free(user_info->domain_sid); 314 free(user_info->domain_name); 315 free(user_info->groups); 316 317 if (user_info->n_other_grps) { 318 for (i = 0; i < user_info->n_other_grps; i++) 319 free(user_info->other_grps[i].sid); 320 321 free(user_info->other_grps); 322 } 323 324 free(user_info->user_sid); 325 free(user_info->pgrp_sid); 326 bzero(user_info, sizeof (smb_userinfo_t)); 327 } 328 329 /* 330 * mlsvc_setadmin_user_info 331 * 332 * Determines if the given user is the domain Administrator or a 333 * member of Domain Admins or Administrators group and set the 334 * user_info->flags accordingly. 335 */ 336 void 337 mlsvc_setadmin_user_info(smb_userinfo_t *user_info) 338 { 339 nt_domain_t *domain; 340 nt_group_t *grp; 341 int i; 342 343 if ((domain = nt_domain_lookupbytype(NT_DOMAIN_PRIMARY)) == NULL) 344 return; 345 346 if (!nt_sid_is_equal((nt_sid_t *)user_info->domain_sid, domain->sid)) 347 return; 348 349 if (user_info->rid == DOMAIN_USER_RID_ADMIN) 350 user_info->flags |= SMB_UINFO_FLAG_DADMIN; 351 else if (user_info->primary_group_rid == DOMAIN_GROUP_RID_ADMINS) 352 user_info->flags |= SMB_UINFO_FLAG_DADMIN; 353 else { 354 for (i = 0; i < user_info->n_groups; i++) 355 if (user_info->groups[i].rid == DOMAIN_GROUP_RID_ADMINS) 356 user_info->flags |= SMB_UINFO_FLAG_DADMIN; 357 } 358 359 grp = nt_group_getinfo("Administrators", RWLOCK_READER); 360 if (grp) { 361 i = nt_group_is_member(grp, user_info->user_sid); 362 nt_group_putinfo(grp); 363 if (i) 364 user_info->flags |= SMB_UINFO_FLAG_LADMIN; 365 } 366 } 367 368 /* 369 * mlsvc_string_save 370 * 371 * This is a convenience function to prepare strings for an RPC call. 372 * An ms_string_t is set up with the appropriate lengths and str is 373 * set up to point to a copy of the original string on the heap. The 374 * macro MLRPC_HEAP_STRSAVE is an alias for mlrpc_heap_strsave, which 375 * extends the heap and copies the string into the new area. 376 */ 377 int 378 mlsvc_string_save(ms_string_t *ms, char *str, struct mlrpc_xaction *mxa) 379 { 380 int length; 381 char *p; 382 383 if (ms == NULL || str == NULL || mxa == NULL) 384 return (0); 385 386 /* 387 * Windows NT expects the name length to exclude the 388 * terminating wchar null but doesn't care whether or 389 * not the allosize includes it. Windows 2000 insists 390 * that both the length and the allosize include the 391 * wchar null. 392 */ 393 length = mts_wcequiv_strlen(str); 394 ms->allosize = length + sizeof (mts_wchar_t); 395 396 if (mxa->context->user_ctx->du_native_os == NATIVE_OS_WIN2000) 397 ms->length = ms->allosize; 398 else 399 ms->length = length; 400 401 if ((p = MLRPC_HEAP_STRSAVE(mxa, str)) == NULL) { 402 return (0); 403 } 404 405 ms->str = (LPTSTR)p; 406 return (1); 407 } 408 409 /* 410 * mlsvc_sid_save 411 * 412 * Expand the heap and copy the sid into the new area. 413 * Returns a pointer to the copy of the sid on the heap. 414 */ 415 nt_sid_t * 416 mlsvc_sid_save(nt_sid_t *sid, struct mlrpc_xaction *mxa) 417 { 418 nt_sid_t *heap_sid; 419 unsigned size; 420 421 if (sid == NULL) 422 return (NULL); 423 424 size = nt_sid_length(sid); 425 426 if ((heap_sid = (nt_sid_t *)MLRPC_HEAP_MALLOC(mxa, size)) == NULL) 427 return (0); 428 429 bcopy(sid, heap_sid, size); 430 return (heap_sid); 431 } 432 433 /* 434 * mlsvc_is_null_handle 435 * 436 * Check a handle against a null handle. Returns 1 if the handle is 437 * null. Otherwise returns 0. 438 */ 439 int 440 mlsvc_is_null_handle(mlsvc_handle_t *handle) 441 { 442 static ms_handle_t zero_handle; 443 444 if (handle == NULL || handle->context == NULL) 445 return (1); 446 447 if (!memcmp(&handle->handle, &zero_handle, sizeof (ms_handle_t))) 448 return (1); 449 450 return (0); 451 } 452 453 /* 454 * mlsvc_validate_user 455 * 456 * Returns NT status codes. 457 */ 458 DWORD 459 mlsvc_validate_user(char *server, char *domain, char *plain_user, 460 char *plain_text) 461 { 462 smb_auth_info_t auth; 463 smb_ntdomain_t *di; 464 int erc; 465 DWORD status; 466 mlsvc_handle_t netr_handle; 467 char machine_passwd[MLSVC_MACHINE_ACCT_PASSWD_MAX]; 468 469 machine_passwd[0] = '\0'; 470 471 /* 472 * Ensure that the domain name is uppercase. 473 */ 474 (void) utf8_strupr(domain); 475 476 /* 477 * There is no point continuing if the domain information is 478 * not available. Wait for up to 10 seconds and then give up. 479 */ 480 if ((di = smb_getdomaininfo(10)) == 0) { 481 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 482 return (status); 483 } 484 485 if (strcasecmp(domain, di->domain) != 0) { 486 status = NT_STATUS_INVALID_PARAMETER; 487 return (status); 488 } 489 490 erc = mlsvc_user_logon(server, domain, plain_user, plain_text); 491 492 if (erc == AUTH_USER_GRANT) { 493 int isenabled; 494 495 smb_config_rdlock(); 496 isenabled = smb_config_getyorn(SMB_CI_ADS_ENABLE); 497 smb_config_unlock(); 498 if (isenabled) { 499 if (adjoin(machine_passwd, 500 sizeof (machine_passwd)) == ADJOIN_SUCCESS) { 501 status = NT_STATUS_SUCCESS; 502 } else { 503 status = NT_STATUS_UNSUCCESSFUL; 504 } 505 } else { 506 /* 507 * Ensure that we don't have an old account in 508 * this domain. There's no need to check the 509 * return status. 510 */ 511 (void) sam_remove_trust_account(server, domain); 512 513 if (mlsvc_user_getauth(server, plain_user, &auth) 514 != 0) { 515 status = NT_STATUS_INVALID_PARAMETER; 516 return (status); 517 } 518 519 status = sam_create_trust_account(server, domain, 520 &auth); 521 if (status == NT_STATUS_SUCCESS) { 522 (void) smb_gethostname(machine_passwd, 523 sizeof (machine_passwd), 0); 524 (void) utf8_strlwr(machine_passwd); 525 } 526 } 527 528 if (status == NT_STATUS_SUCCESS) { 529 smb_config_wrlock(); 530 if (smb_config_set(SMB_CI_MACHINE_PASSWD, 531 machine_passwd) != 0) { 532 smb_config_unlock(); 533 return (NT_STATUS_UNSUCCESSFUL); 534 } 535 smb_config_unlock(); 536 537 /* 538 * If we successfully create a trust account, we mark 539 * ourselves as a domain member in the environment so 540 * that we use the SAMLOGON version of the NETLOGON 541 * PDC location protocol. 542 */ 543 smb_set_domain_member(1); 544 545 if (netr_open(server, domain, &netr_handle) == 0) { 546 status = netlogon_auth(server, &netr_handle, 547 NETR_FLG_INIT); 548 (void) netr_close(&netr_handle); 549 } else { 550 status = NT_STATUS_OPEN_FAILED; 551 } 552 } 553 } else { 554 status = NT_STATUS_LOGON_FAILURE; 555 } 556 557 return (status); 558 } 559 560 /*ARGSUSED*/ 561 void 562 nt_group_ht_lock(krwmode_t locktype) 563 { 564 } 565 566 void 567 nt_group_ht_unlock(void) 568 { 569 } 570 571 int 572 nt_group_num_groups(void) 573 { 574 return (0); 575 } 576 577 /*ARGSUSED*/ 578 uint32_t 579 nt_group_add(char *gname, char *comment) 580 { 581 return (NT_STATUS_NOT_SUPPORTED); 582 } 583 584 /*ARGSUSED*/ 585 uint32_t 586 nt_group_modify(char *gname, char *new_gname, char *comment) 587 { 588 return (NT_STATUS_NOT_SUPPORTED); 589 } 590 591 /*ARGSUSED*/ 592 uint32_t 593 nt_group_delete(char *gname) 594 { 595 return (NT_STATUS_NOT_SUPPORTED); 596 } 597 598 /*ARGSUSED*/ 599 nt_group_t * 600 nt_group_getinfo(char *gname, krwmode_t locktype) 601 { 602 return (NULL); 603 } 604 605 /*ARGSUSED*/ 606 void 607 nt_group_putinfo(nt_group_t *grp) 608 { 609 } 610 611 /*ARGSUSED*/ 612 int 613 nt_group_getpriv(nt_group_t *grp, uint32_t priv_id) 614 { 615 return (SE_PRIVILEGE_DISABLED); 616 } 617 618 /*ARGSUSED*/ 619 uint32_t 620 nt_group_setpriv(nt_group_t *grp, uint32_t priv_id, uint32_t new_attr) 621 { 622 return (NT_STATUS_NOT_SUPPORTED); 623 } 624 625 /*ARGSUSED*/ 626 int 627 nt_group_is_member(nt_group_t *grp, nt_sid_t *sid) 628 { 629 return (0); 630 } 631 632 /*ARGSUSED*/ 633 uint32_t 634 nt_group_add_member(nt_group_t *grp, nt_sid_t *msid, uint16_t sid_name_use, 635 char *account) 636 { 637 return (NT_STATUS_NOT_SUPPORTED); 638 } 639 640 /*ARGSUSED*/ 641 uint32_t 642 nt_group_del_member(nt_group_t *grp, void *key, int keytype) 643 { 644 return (NT_STATUS_NOT_SUPPORTED); 645 } 646 647 /*ARGSUSED*/ 648 int 649 nt_group_num_members(nt_group_t *grp) 650 { 651 return (0); 652 } 653 654 nt_group_iterator_t * 655 nt_group_open_iterator(void) 656 { 657 return (NULL); 658 } 659 660 /*ARGSUSED*/ 661 void 662 nt_group_close_iterator(nt_group_iterator_t *gi) 663 { 664 } 665 666 /*ARGSUSED*/ 667 nt_group_t * 668 nt_group_iterate(nt_group_iterator_t *gi) 669 { 670 return (NULL); 671 } 672 673 int 674 nt_group_cache_size(void) 675 { 676 return (0); 677 } 678 679 uint32_t 680 sam_init(void) 681 { 682 return (NT_STATUS_SUCCESS); 683 } 684 685 /*ARGSUSED*/ 686 uint32_t 687 nt_group_add_member_byname(char *gname, char *account) 688 { 689 return (NT_STATUS_NOT_SUPPORTED); 690 } 691 692 /*ARGSUSED*/ 693 uint32_t 694 nt_group_del_member_byname(nt_group_t *grp, char *member_name) 695 { 696 return (NT_STATUS_NOT_SUPPORTED); 697 } 698 699 /*ARGSUSED*/ 700 void 701 nt_group_add_groupprivs(nt_group_t *grp, smb_privset_t *priv) 702 { 703 } 704 705 /*ARGSUSED*/ 706 uint32_t 707 nt_groups_member_privs(nt_sid_t *sid, smb_privset_t *priv) 708 { 709 return (NT_STATUS_SUCCESS); 710 } 711 712 /*ARGSUSED*/ 713 int 714 nt_groups_member_ngroups(nt_sid_t *sid) 715 { 716 return (0); 717 } 718 719 /*ARGSUSED*/ 720 uint32_t 721 nt_groups_member_groups(nt_sid_t *sid, smb_id_t *grps, int ngrps) 722 { 723 return (NT_STATUS_SUCCESS); 724 } 725 726 /*ARGSUSED*/ 727 nt_group_t * 728 nt_groups_lookup_rid(uint32_t rid) 729 { 730 return (NULL); 731 } 732 733 /*ARGSUSED*/ 734 int 735 nt_groups_count(int cnt_opt) 736 { 737 return (0); 738 } 739 740 /*ARGSUSED*/ 741 int 742 nt_group_member_list(int offset, nt_group_t *grp, 743 ntgrp_member_list_t *rmembers) 744 { 745 return (0); 746 } 747 748 /*ARGSUSED*/ 749 void 750 nt_group_list(int offset, char *pattern, ntgrp_list_t *list) 751 { 752 } 753