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 2008 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 #include <sys/param.h> 29 #include <ldap.h> 30 #include <stdlib.h> 31 #include <gssapi/gssapi.h> 32 #include <sys/types.h> 33 #include <sys/socket.h> 34 #include <netinet/in.h> 35 #include <arpa/inet.h> 36 #include <sys/time.h> 37 #include <netdb.h> 38 #include <pthread.h> 39 #include <unistd.h> 40 #include <arpa/nameser.h> 41 #include <resolv.h> 42 #include <sys/synch.h> 43 #include <string.h> 44 #include <strings.h> 45 #include <syslog.h> 46 #include <fcntl.h> 47 #include <sys/types.h> 48 #include <sys/stat.h> 49 50 51 #include <smbsrv/libsmbns.h> 52 #include <smbns_ads.h> 53 #include <smbns_dyndns.h> 54 #include <smbns_krb.h> 55 56 #define ADS_DN_MAX 300 57 #define ADS_MAXMSGLEN 512 58 #define ADS_HOST_PREFIX "host/" 59 #define ADS_COMPUTERS_CN "Computers" 60 #define ADS_COMPUTER_NUM_ATTR 7 61 #define ADS_SHARE_NUM_ATTR 3 62 #define ADS_SITE_MAX MAXHOSTNAMELEN 63 64 /* current ADS server to communicate with */ 65 ADS_HOST_INFO *ads_host_info = NULL; 66 mutex_t ads_host_mtx; 67 char ads_site[ADS_SITE_MAX]; 68 mutex_t ads_site_mtx; 69 70 71 /* 72 * adjoin_errmsg 73 * 74 * Use the adjoin return status defined in adjoin_status_t as the index 75 * to this table. 76 */ 77 static char *adjoin_errmsg[] = { 78 "ADJOIN succeeded.", 79 "ADJOIN failed to get handle.", 80 "ADJOIN failed to generate machine password.", 81 "ADJOIN failed to add workstation trust account.", 82 "ADJOIN failed to modify workstation trust account.", 83 "ADJOIN failed to get list of encryption types.", 84 "ADJOIN failed to get host principal.", 85 "ADJOIN failed to initialize kerberos context.", 86 "ADJOIN failed to get Kerberos principal.", 87 "ADJOIN failed to set machine account password on AD.", 88 "ADJOIN failed to modify CONTROL attribute of the account.", 89 "ADJOIN failed to write Kerberos keytab file.", 90 "ADJOIN failed to configure domain_name property for idmapd.", 91 "ADJOIN failed to refresh idmap service." 92 "ADJOIN failed to refresh SMB service." 93 }; 94 95 static ADS_HANDLE *ads_open_main(char *domain, char *user, char *password); 96 static int ads_bind(ADS_HANDLE *); 97 static void ads_get_computer_dn(ADS_HANDLE *, char *, size_t); 98 static char *ads_get_host_principal(char *fqhost); 99 static char *ads_get_host_principal_w_realm(char *princ, char *domain); 100 static int ads_get_host_principals(char *fqhost, char *domain, 101 char **princ, char **princ_r); 102 static int ads_add_computer(ADS_HANDLE *ah); 103 static int ads_modify_computer(ADS_HANDLE *ah); 104 static void ads_del_computer(ADS_HANDLE *ah); 105 static int ads_computer_op(ADS_HANDLE *ah, int op); 106 static int ads_lookup_computer_n_attr(ADS_HANDLE *ah, char *attr, char **val); 107 static int ads_update_computer_cntrl_attr(ADS_HANDLE *ah, int des_only); 108 static krb5_kvno ads_lookup_computer_attr_kvno(ADS_HANDLE *ah); 109 static int ads_gen_machine_passwd(char *machine_passwd, int bufsz); 110 static ADS_HOST_INFO *ads_get_host_info(void); 111 static void ads_set_host_info(ADS_HOST_INFO *host); 112 static void ads_free_host_info(void); 113 114 /* 115 * ads_init 116 * 117 * Initializes the ads_site global variable. 118 */ 119 void 120 ads_init(void) 121 { 122 (void) mutex_lock(&ads_site_mtx); 123 (void) smb_config_getstr(SMB_CI_ADS_SITE, ads_site, sizeof (ads_site)); 124 (void) mutex_unlock(&ads_site_mtx); 125 } 126 127 /* 128 * ads_refresh 129 * 130 * If the ads_site has changed, clear the ads_host_info cache. 131 */ 132 void 133 ads_refresh(void) 134 { 135 char new_site[ADS_SITE_MAX]; 136 137 (void) smb_config_getstr(SMB_CI_ADS_SITE, new_site, sizeof (new_site)); 138 (void) mutex_lock(&ads_site_mtx); 139 if (strcasecmp(ads_site, new_site)) { 140 (void) strlcpy(ads_site, new_site, sizeof (ads_site)); 141 ads_free_host_info(); 142 } 143 (void) mutex_unlock(&ads_site_mtx); 144 } 145 146 /* 147 * ads_build_unc_name 148 * 149 * Construct the UNC name of the share object in the format of 150 * \\hostname.domain\shareUNC 151 * 152 * Returns 0 on success, -1 on error. 153 */ 154 int 155 ads_build_unc_name(char *unc_name, int maxlen, 156 const char *hostname, const char *shareUNC) 157 { 158 char my_domain[MAXHOSTNAMELEN]; 159 160 if (smb_getfqdomainname(my_domain, sizeof (my_domain)) != 0) 161 return (-1); 162 163 (void) snprintf(unc_name, maxlen, "\\\\%s.%s\\%s", 164 hostname, my_domain, shareUNC); 165 return (0); 166 } 167 168 /* 169 * ads_skip_domain_name 170 * Skip domain name format in DNS message. The format is a sequence of 171 * ascii labels with each label having a length byte at the beginning. 172 * The domain name is terminated with a NULL character. 173 * i.e. 3sun3com0 174 * Parameters: 175 * bufptr: address of pointer of buffer that contains domain name 176 * Returns: 177 * bufptr: points to the data after the domain name label 178 */ 179 static void 180 ads_skip_domain_name(char **bufptr) 181 { 182 int i = 0; 183 unsigned char c, d; 184 185 c = (*bufptr)[i++]; 186 d = c & 0xC0; 187 while (c != 0 && (d != 0xC0)) { /* do nothing */ 188 c = (*bufptr)[i++]; 189 d = c & 0xC0; 190 } 191 192 if (d == 0xC0) 193 /* skip 2nd byte in 2 byte ptr info */ 194 i++; 195 *bufptr += i; 196 } 197 198 static int 199 ads_is_ptr(char *buf, int len, char *offset_ptr, char **new_loc) 200 { 201 uint16_t offset; 202 unsigned char c; 203 204 c = len & 0xC0; 205 if (c == 0xC0) { 206 offset_ptr = dyndns_get_nshort(offset_ptr, &offset); 207 offset &= 0x3FFF; 208 if (offset > NS_PACKETSZ) { 209 return (-1); 210 } 211 *new_loc = buf + offset; 212 return (1); 213 } 214 return (0); 215 } 216 217 /* 218 * ads_get_domain_name 219 * Converts the domain name format in DNS message back to string format. 220 * The format is a sequence of ascii labels with each label having a length 221 * byte at the beginning. The domain name is terminated with a NULL 222 * character. 223 * i.e. 6procom3com0 -> procom.com 224 * Parameters: 225 * bufptr : address of pointer to buffer that contains domain name 226 * dname_len: length of domain name in label format 227 * Returns: 228 * NULL : error 229 * domain name: in string format using allocated memory 230 * bufptr : points to the data after the domain name label 231 */ 232 static char * 233 ads_get_domain_name(char *buf, char **bufptr) 234 { 235 char str[256], *ptr, *new_loc; 236 int i, j, k, len, ret; 237 int skip = 0; 238 i = 0; 239 k = 0; 240 ptr = *bufptr; 241 242 /* get len of first label */ 243 len = ptr[i++]; 244 if ((ret = ads_is_ptr(buf, len, &ptr[i-1], &new_loc)) == 1) { 245 if (skip == 0) { 246 /* skip up to first ptr */ 247 skip = i; 248 } 249 250 i = 0; 251 ptr = new_loc; 252 253 /* get len of first label */ 254 len = ptr[i++]; 255 } else { 256 if (ret == -1) { 257 return (NULL); 258 } 259 } 260 261 while (len) { 262 if ((len > 63) || (k >= 255)) 263 return (NULL); 264 265 for (j = 0; j < len; j++) 266 str[k++] = ptr[i++]; 267 268 /* get len of next label */ 269 len = ptr[i++]; 270 if ((ret = ads_is_ptr(buf, len, &ptr[i-1], &new_loc)) == 1) { 271 if (skip == 0) { 272 /* skip up to first ptr */ 273 skip = i; 274 } 275 i = 0; 276 ptr = new_loc; 277 278 /* get len of first label */ 279 len = ptr[i++]; 280 } else if (ret == -1) { 281 return (NULL); 282 } 283 284 if (len) { 285 /* replace label len or ptr with '.' */ 286 str[k++] = '.'; 287 } 288 } 289 290 str[k] = 0; 291 292 if (skip) { 293 /* skip name with ptr or just ptr */ 294 *bufptr += skip + 1; 295 } else { 296 /* skip name */ 297 *bufptr += i; 298 } 299 300 return (strdup(str)); 301 } 302 303 /* 304 * ads_ping 305 * Ping IP without displaying log. This is used to ping an ADS server to see 306 * if it is still alive before connecting to it with TCP. 307 * Taken from os/service/ping.c 308 * Parameters: 309 * hostinetaddr: 4 bytes IP address in network byte order 310 * Returns: 311 * -1: error 312 * 0: successful 313 */ 314 /*ARGSUSED*/ 315 static int 316 ads_ping(unsigned long hostinetaddr) 317 { 318 return (0); 319 } 320 321 /* 322 * ads_free_host_list 323 */ 324 static void 325 ads_free_host_list(ADS_HOST_INFO *host_list, int count) 326 { 327 int i; 328 for (i = 0; i < count; i++) { 329 free(host_list[i].name); 330 } 331 free(host_list); 332 } 333 334 /* 335 * ads_set_host_info 336 * Cache the result of the ADS discovery if the cache is empty. 337 */ 338 static void 339 ads_set_host_info(ADS_HOST_INFO *host) 340 { 341 (void) mutex_lock(&ads_host_mtx); 342 if (!ads_host_info) 343 ads_host_info = host; 344 (void) mutex_unlock(&ads_host_mtx); 345 } 346 347 /* 348 * ads_get_host_info 349 * Get the cached ADS host info. 350 */ 351 static ADS_HOST_INFO * 352 ads_get_host_info(void) 353 { 354 ADS_HOST_INFO *host; 355 356 (void) mutex_lock(&ads_host_mtx); 357 host = ads_host_info; 358 (void) mutex_unlock(&ads_host_mtx); 359 return (host); 360 } 361 /* 362 * ads_find_host 363 * This routine builds a DNS service location message and sends it to the 364 * DNS server via UDP to query it for a list of ADS server(s). Once a reply 365 * is received, the reply message is parsed to get the hostname and IP 366 * addresses of the ADS server(s). One ADS server will be selected from the 367 * list. A ping is sent to each host at a time and the one that respond will 368 * be selected. 369 * 370 * The service location of _ldap._tcp.dc.msdcs.<ADS domain> is used to 371 * guarantee that Microsoft domain controllers are returned. Microsoft domain 372 * controllers are also ADS servers. 373 * 374 * The ADS hostnames are stored in the answer section of the DNS reply message. 375 * The IP addresses are stored in the additional section. If the additional 376 * section does not contain any IP addresses then a DNS query by hostname is 377 * sent to get the IP address of the hostname. This is very unlikely. 378 * 379 * The DNS reply message may be in compress formed. The compression is done 380 * on repeating domain name label in the message. i.e hostname. 381 * Parameters: 382 * ns: Nameserver to use to find the ADS host 383 * domain: domain of ADS host. 384 * Returns: 385 * ADS host: fully qualified hostname, ip address, ldap port 386 * port : LDAP port of ADS host 387 */ 388 /*ARGSUSED*/ 389 ADS_HOST_INFO * 390 ads_find_host(char *ns, char *domain, int *port, char *service, int *go_next) 391 { 392 int s; 393 uint16_t id, rid, data_len, eport; 394 int ipaddr; 395 char buf[NS_PACKETSZ], buf2[NS_PACKETSZ]; 396 char *bufptr, *str; 397 int i, ret; 398 int queryReq; 399 uint16_t query_cnt, ans_cnt, namser_cnt, addit_cnt; 400 int quest_type, quest_class; 401 int dns_ip; 402 struct in_addr addr; 403 uint16_t flags = 0; 404 int force_recurs = 0; 405 ADS_HOST_INFO *ads_hosts_list = NULL, *ads_host; 406 ADS_HOST_INFO *ads_hosts_list2 = NULL; 407 408 *go_next = 0; 409 410 /* 411 * If we have already found an ADS server, skip the ads_find_host 412 * process. Returns the ADS host from the cache. 413 */ 414 ads_host = ads_get_host_info(); 415 if (ads_host) 416 return (ads_host); 417 418 if (ns == NULL || *ns == 0) { 419 return (NULL); 420 } 421 dns_ip = inet_addr(ns); 422 423 if ((s = dyndns_open_init_socket(SOCK_DGRAM, dns_ip, 53)) < 0) 424 return (NULL); 425 426 retry: 427 /* build query request */ 428 queryReq = REQ_QUERY; 429 query_cnt = 1; 430 ans_cnt = 0; 431 namser_cnt = 0; 432 addit_cnt = 0; 433 434 (void) memset(buf, 0, NS_PACKETSZ); 435 bufptr = buf; 436 id = dns_get_msgid(); 437 if (dyndns_build_header(&bufptr, BUFLEN_UDP(bufptr, buf), id, queryReq, 438 query_cnt, ans_cnt, namser_cnt, addit_cnt, flags) == -1) { 439 (void) close(s); 440 return (NULL); 441 } 442 443 quest_type = ns_t_srv; 444 quest_class = ns_c_in; 445 446 if (dyndns_build_quest_zone(&bufptr, BUFLEN_UDP(bufptr, buf), service, 447 quest_type, quest_class) == -1) { 448 (void) close(s); 449 return (NULL); 450 } 451 452 if (dyndns_udp_send_recv(s, buf, bufptr - buf, buf2) == -1) { 453 (void) close(s); 454 syslog(LOG_ERR, "smb_ads: send/receive error"); 455 *go_next = 1; 456 return (NULL); 457 } 458 (void) close(s); 459 460 (void) dyndns_get_nshort(buf2, &rid); 461 if (id != rid) 462 return (NULL); 463 464 /* 465 * check if query is successful by checking error 466 * field in UDP 467 */ 468 ret = buf2[3] & 0xf; 469 if (ret != NOERROR) { 470 syslog(LOG_ERR, "smb_ads: DNS query for ADS host error: %d: ", 471 ret); 472 dyndns_msg_err(ret); 473 *go_next = 1; 474 return (NULL); 475 } 476 477 bufptr = buf2; 478 bufptr += 2; /* Skip ID section */ 479 bufptr = dyndns_get_nshort(bufptr, &flags); 480 bufptr = dyndns_get_nshort(bufptr, &query_cnt); 481 bufptr = dyndns_get_nshort(bufptr, &ans_cnt); 482 bufptr = dyndns_get_nshort(bufptr, &namser_cnt); 483 bufptr = dyndns_get_nshort(bufptr, &addit_cnt); 484 485 if (ans_cnt == 0) { 486 /* Check if the server supports recursive queries */ 487 if (force_recurs++ == 0 && (flags & DNSF_RECUR_SUPP) != 0) { 488 flags = DNSF_RECUR_QRY; 489 goto retry; 490 } 491 492 syslog(LOG_DEBUG, "smb_ads: No ADS host found: " 493 "No answer section\n"); 494 return (NULL); 495 } 496 497 /* skip question section */ 498 if (query_cnt == 1) { 499 ads_skip_domain_name(&bufptr); 500 bufptr += 4; 501 } else { 502 syslog(LOG_ERR, "smb_ads: No ADS host found, malformed " 503 "question section, query_cnt: %d???\n", query_cnt); 504 return (NULL); 505 } 506 507 ads_hosts_list = (ADS_HOST_INFO *) 508 malloc(sizeof (ADS_HOST_INFO)*ans_cnt); 509 if (ads_hosts_list == NULL) 510 return (NULL); 511 512 bzero(ads_hosts_list, sizeof (ADS_HOST_INFO) * ans_cnt); 513 514 /* check answer section */ 515 for (i = 0; i < ans_cnt; i++) { 516 ads_skip_domain_name(&bufptr); 517 518 /* skip type, class, ttl */ 519 bufptr += 8; 520 521 /* len of data after this point */ 522 bufptr = dyndns_get_nshort(bufptr, &data_len); 523 524 /* skip priority, weight */ 525 bufptr += 4; 526 bufptr = dyndns_get_nshort(bufptr, &eport); 527 ads_hosts_list[i].port = eport; 528 529 if ((str = ads_get_domain_name(buf2, &bufptr)) == NULL) { 530 syslog(LOG_ERR, "smb_ads: No ADS host found, " 531 "error decoding DNS answer section\n"); 532 ads_free_host_list(ads_hosts_list, ans_cnt); 533 return (NULL); 534 } 535 ads_hosts_list[i].name = str; 536 } 537 538 /* check authority section */ 539 for (i = 0; i < namser_cnt; i++) { 540 ads_skip_domain_name(&bufptr); 541 542 /* skip type, class, ttl */ 543 bufptr += 8; 544 545 /* get len of data */ 546 bufptr = dyndns_get_nshort(bufptr, &data_len); 547 548 /* skip data */ 549 bufptr += data_len; 550 } 551 552 /* check additional section to get IP address of ads host */ 553 if (addit_cnt > 0) { 554 int j; 555 556 ads_hosts_list2 = (ADS_HOST_INFO *) 557 malloc(sizeof (ADS_HOST_INFO) * addit_cnt); 558 if (ads_hosts_list2 == NULL) { 559 ads_free_host_list(ads_hosts_list, ans_cnt); 560 return (NULL); 561 } 562 563 bzero(ads_hosts_list2, sizeof (ADS_HOST_INFO) * addit_cnt); 564 565 for (i = 0; i < addit_cnt; i++) { 566 567 if ((str = ads_get_domain_name(buf2, 568 &bufptr)) == NULL) { 569 syslog(LOG_ERR, "smb_ads: No ADS host found, " 570 "error decoding DNS additional section\n"); 571 ads_free_host_list(ads_hosts_list, ans_cnt); 572 ads_free_host_list(ads_hosts_list2, addit_cnt); 573 return (NULL); 574 } 575 576 ads_hosts_list2[i].name = str; 577 bufptr += 10; 578 bufptr = dyndns_get_int(bufptr, &ipaddr); 579 ads_hosts_list2[i].ip_addr = ipaddr; 580 } 581 582 /* pick a host that is up */ 583 for (i = 0; i < addit_cnt; i++) { 584 if (ads_ping(ads_hosts_list2[i].ip_addr) != 0) { 585 continue; 586 } 587 for (j = 0; j < ans_cnt; j++) 588 if (strcmp(ads_hosts_list2[i].name, 589 ads_hosts_list[j].name) == 0) 590 break; 591 if (j == ans_cnt) { 592 ads_free_host_list(ads_hosts_list, ans_cnt); 593 ads_free_host_list(ads_hosts_list2, addit_cnt); 594 return (NULL); 595 } 596 ads_host = (ADS_HOST_INFO *) 597 malloc(sizeof (ADS_HOST_INFO)); 598 if (ads_host == NULL) { 599 ads_free_host_list(ads_hosts_list, ans_cnt); 600 ads_free_host_list(ads_hosts_list2, addit_cnt); 601 return (NULL); 602 } 603 bzero(ads_host, sizeof (ADS_HOST_INFO)); 604 ads_host->name = strdup(ads_hosts_list[j].name); 605 if (ads_host->name == NULL) { 606 ads_free_host_list(ads_hosts_list, ans_cnt); 607 ads_free_host_list(ads_hosts_list2, addit_cnt); 608 return (NULL); 609 } 610 ads_host->ip_addr = ads_hosts_list2[i].ip_addr; 611 ads_host->port = ads_hosts_list[j].port; 612 *port = ads_host->port; 613 addr.s_addr = ads_host->ip_addr; 614 syslog(LOG_DEBUG, "smb_ads: Found ADS server: %s (%s)" 615 " from %s\n", ads_host->name, inet_ntoa(addr), ns); 616 ads_free_host_list(ads_hosts_list, ans_cnt); 617 ads_free_host_list(ads_hosts_list2, addit_cnt); 618 ads_set_host_info(ads_host); 619 return (ads_host); 620 } 621 ads_free_host_list(ads_hosts_list2, addit_cnt); 622 } 623 624 syslog(LOG_ERR, "smb_ads: Can't get IP for " 625 "ADS host or ADS host is down.\n"); 626 ads_free_host_list(ads_hosts_list, ans_cnt); 627 628 *go_next = 1; 629 return (NULL); 630 } 631 632 /* 633 * ads_convert_domain 634 * Converts a domain string into its distinguished name i.e. a unique 635 * name for an entry in the Directory Service. 636 * Memory is allocated 637 * for the new string. 638 * i.e. procom.com -> dc=procom,dc=com 639 * Parameters: 640 * s: fully qualified DNS domain string 641 * Returns: 642 * NULL if error 643 * DNS domain in LDAP DN string format 644 */ 645 static char * 646 ads_convert_domain(char *s) 647 { 648 char *t, *s2, *t2; 649 int len, cnt; 650 651 if (s == NULL || *s == 0) 652 return (NULL); 653 654 cnt = 0; 655 t = s; 656 while (*t) { 657 if (*t++ == '.') { 658 cnt++; 659 } 660 } 661 662 len = 3 + strlen(s) + cnt*3 + 1; 663 664 s2 = (char *)malloc(len); 665 if (s2 == NULL) 666 return (NULL); 667 668 bzero(s2, len); 669 670 t = s2; 671 (void) strncpy(t, "dc=", 3); 672 t += 3; 673 t2 = s; 674 while (*s) { 675 if (*s == '.') { 676 if (t + 3 >= s2 + len - 1) { 677 syslog(LOG_ERR, "[ads_convert_domain] " 678 "buffer overrun for string " 679 "conversion of %s: tot buf " 680 "sz alloc: %d, last " 681 "written buf offset: %d\n", 682 t2, len, t+3-s2); 683 free(s2); 684 return (NULL); 685 } 686 (void) strncpy(t, ",dc=", 4); 687 t += 4; 688 s++; 689 } else { 690 if (t >= s2 + len - 1) { 691 syslog(LOG_ERR, "[ads_convert_domain] " 692 "buffer overrun for string " 693 "conversion of %s: tot buf " 694 "sz alloc: %d, last " 695 "written buf offset: %d\n", 696 t2, len, t-s2); 697 free(s2); 698 return (NULL); 699 } 700 *t++ = *s++; 701 } 702 } 703 *t = '\0'; 704 return (s2); 705 } 706 707 /* 708 * ads_free_host_info 709 * Free the memory use by the global ads_host_info and set it to NULL. 710 */ 711 static void 712 ads_free_host_info(void) 713 { 714 (void) mutex_lock(&ads_host_mtx); 715 if (ads_host_info) { 716 free(ads_host_info->name); 717 free(ads_host_info); 718 ads_host_info = NULL; 719 } 720 (void) mutex_unlock(&ads_host_mtx); 721 } 722 723 /* 724 * ads_open 725 * Open a LDAP connection to an ADS server if the system is in domain mode. 726 * Acquire both Kerberos TGT and LDAP service tickets for the host principal. 727 * 728 * This function should only be called after the system is successfully joined 729 * to a domain. 730 */ 731 ADS_HANDLE * 732 ads_open(void) 733 { 734 char domain[MAXHOSTNAMELEN]; 735 736 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) 737 return (NULL); 738 739 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) 740 return (NULL); 741 742 return (ads_open_main(domain, NULL, NULL)); 743 } 744 745 /* 746 * ads_open_main 747 * Open a LDAP connection to an ADS server. 748 * If ADS is enabled and the administrative username, password, and 749 * ADS domain are defined then query DNS to find an ADS server if this is the 750 * very first call to this routine. After an ADS server is found then this 751 * server will be used everytime this routine is called until the system is 752 * rebooted or the ADS server becomes unavailable then an ADS server will 753 * be queried again. The ADS server is always ping before an LDAP connection 754 * is made to it. If the pings fail then DNS is used once more to find an 755 * available ADS server. If the ping is successful then an LDAP connection 756 * is made to the ADS server. After the connection is made then an ADS handle 757 * is created to be returned. 758 * 759 * After the LDAP connection, the LDAP version will be set to 3 using 760 * ldap_set_option(). 761 * 762 * The ads_bind() routine is also called before the ADS handle is returned. 763 * Parameters: 764 * domain - fully-qualified domain name 765 * user - the user account for whom the Kerberos TGT ticket and ADS 766 * service tickets are acquired. 767 * password - password of the specified user 768 * 769 * Returns: 770 * NULL : can't connect to ADS server or other errors 771 * ADS_HANDLE* : handle to ADS server 772 */ 773 static ADS_HANDLE * 774 ads_open_main(char *domain, char *user, char *password) 775 { 776 ADS_HANDLE *ah; 777 LDAP *ld; 778 int version = 3, ads_port, find_ads_retry; 779 ADS_HOST_INFO *ads_host = NULL; 780 struct in_addr addr; 781 char site[ADS_SITE_MAX]; 782 char service[MAXHOSTNAMELEN]; 783 char site_service[MAXHOSTNAMELEN]; 784 struct in_addr ns_list[MAXNS]; 785 int i, cnt, go_next; 786 787 788 (void) mutex_lock(&ads_site_mtx); 789 (void) strlcpy(site, ads_site, sizeof (site)); 790 (void) mutex_unlock(&ads_site_mtx); 791 792 find_ads_retry = 0; 793 find_ads_host: 794 795 ads_host = ads_get_host_info(); 796 if (!ads_host) { 797 if (*site != '\0') { 798 (void) snprintf(site_service, sizeof (site_service), 799 "_ldap._tcp.%s._sites.dc._msdcs.%s", site, domain); 800 } else { 801 *site_service = '\0'; 802 } 803 (void) snprintf(service, sizeof (service), 804 "_ldap._tcp.dc._msdcs.%s", domain); 805 806 cnt = smb_get_nameservers(ns_list, MAXNS); 807 808 ads_host = NULL; 809 go_next = 0; 810 for (i = 0; i < cnt; i++) { 811 if (*site_service != '\0') { 812 ads_host = ads_find_host(inet_ntoa(ns_list[i]), 813 domain, &ads_port, site_service, &go_next); 814 } 815 if (ads_host == NULL) { 816 ads_host = ads_find_host(inet_ntoa(ns_list[i]), 817 domain, &ads_port, service, &go_next); 818 } 819 if (ads_host != NULL) 820 break; 821 if (go_next == 0) 822 break; 823 } 824 } 825 826 if (ads_host == NULL) { 827 syslog(LOG_ERR, "smb_ads: No ADS host found from " 828 "configured nameservers"); 829 return (NULL); 830 } 831 832 if (ads_ping(ads_host->ip_addr) != 0) { 833 ads_free_host_info(); 834 ads_host = NULL; 835 if (find_ads_retry == 0) { 836 find_ads_retry = 1; 837 goto find_ads_host; 838 } 839 return (NULL); 840 } 841 842 ah = (ADS_HANDLE *)malloc(sizeof (ADS_HANDLE)); 843 if (ah == NULL) { 844 return (NULL); 845 } 846 (void) memset(ah, 0, sizeof (ADS_HANDLE)); 847 848 addr.s_addr = ads_host->ip_addr; 849 if ((ld = ldap_init((char *)inet_ntoa(addr), ads_host->port)) == NULL) { 850 syslog(LOG_ERR, "smb_ads: Could not open connection " 851 "to host: %s\n", ads_host->name); 852 ads_free_host_info(); 853 ads_host = NULL; 854 free(ah); 855 if (find_ads_retry == 0) { 856 find_ads_retry = 1; 857 goto find_ads_host; 858 } 859 return (NULL); 860 } 861 862 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) 863 != LDAP_SUCCESS) { 864 syslog(LOG_ERR, "smb_ads: Could not set " 865 "LDAP_OPT_PROTOCOL_VERSION %d\n", version); 866 ads_free_host_info(); 867 free(ah); 868 (void) ldap_unbind(ld); 869 return (NULL); 870 } 871 872 ah->ld = ld; 873 ah->user = (user) ? strdup(user) : NULL; 874 ah->pwd = (password) ? strdup(password) : NULL; 875 ah->domain = strdup(domain); 876 877 if (ah->domain == NULL) { 878 ads_close(ah); 879 return (NULL); 880 } 881 882 ah->domain_dn = ads_convert_domain(domain); 883 if (ah->domain_dn == NULL) { 884 ads_close(ah); 885 return (NULL); 886 } 887 888 ah->hostname = strdup(ads_host->name); 889 if (ah->hostname == NULL) { 890 ads_close(ah); 891 return (NULL); 892 } 893 if (site) { 894 ah->site = strdup(site); 895 if (ah->site == NULL) { 896 ads_close(ah); 897 return (NULL); 898 } 899 } else { 900 ah->site = NULL; 901 } 902 903 if (ads_bind(ah) == -1) { 904 ads_close(ah); 905 return (NULL); 906 } 907 908 return (ah); 909 } 910 911 /* 912 * ads_close 913 * Close connection to ADS server and free memory allocated for ADS handle. 914 * LDAP unbind is called here. 915 * Parameters: 916 * ah: handle to ADS server 917 * Returns: 918 * void 919 */ 920 void 921 ads_close(ADS_HANDLE *ah) 922 { 923 int len; 924 925 if (ah == NULL) 926 return; 927 /* close and free connection resources */ 928 if (ah->ld) 929 (void) ldap_unbind(ah->ld); 930 931 free(ah->user); 932 if (ah->pwd) { 933 len = strlen(ah->pwd); 934 /* zero out the memory that contains user's password */ 935 if (len > 0) 936 bzero(ah->pwd, len); 937 free(ah->pwd); 938 } 939 free(ah->domain); 940 free(ah->domain_dn); 941 free(ah->hostname); 942 free(ah->site); 943 free(ah); 944 } 945 946 /* 947 * ads_display_stat 948 * Display error message for GSS-API routines. 949 * Parameters: 950 * maj: GSS major status 951 * min: GSS minor status 952 * Returns: 953 * None 954 */ 955 static void 956 ads_display_stat(OM_uint32 maj, OM_uint32 min) 957 { 958 gss_buffer_desc msg; 959 OM_uint32 msg_ctx = 0; 960 OM_uint32 min2; 961 (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, 962 &msg_ctx, &msg); 963 syslog(LOG_ERR, "smb_ads: major status error: %s\n", (char *)msg.value); 964 (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, 965 &msg_ctx, &msg); 966 syslog(LOG_ERR, "smb_ads: minor status error: %s\n", (char *)msg.value); 967 } 968 969 /* 970 * free_attr 971 * Free memory allocated when publishing a share. 972 * Parameters: 973 * attrs: an array of LDAPMod pointers 974 * Returns: 975 * None 976 */ 977 static void 978 free_attr(LDAPMod *attrs[]) 979 { 980 int i; 981 for (i = 0; attrs[i]; i++) { 982 free(attrs[i]); 983 } 984 } 985 986 /* 987 * ads_acquire_cred 988 * Called by ads_bind() to get a handle to administrative user's credential 989 * stored locally on the system. The credential is the TGT. If the attempt at 990 * getting handle fails then a second attempt will be made after getting a 991 * new TGT. 992 * Please look at ads_bind() for more information. 993 * 994 * Paramters: 995 * ah : handle to ADS server 996 * kinit_retry: if 0 then a second attempt will be made to get handle to the 997 * credential if the first attempt fails 998 * Returns: 999 * cred_handle: handle to the administrative user's credential (TGT) 1000 * oid : contains Kerberos 5 object identifier 1001 * kinit_retry: A 1 indicates that a second attempt has been made to get 1002 * handle to the credential and no further attempts can be made 1003 * -1 : error 1004 * 0 : success 1005 */ 1006 static int 1007 ads_acquire_cred(ADS_HANDLE *ah, gss_cred_id_t *cred_handle, gss_OID *oid, 1008 int *kinit_retry) 1009 { 1010 return (krb5_acquire_cred_kinit(ah->user, ah->pwd, cred_handle, oid, 1011 kinit_retry, "ads")); 1012 } 1013 1014 /* 1015 * ads_establish_sec_context 1016 * Called by ads_bind() to establish a security context to an LDAP service on 1017 * an ADS server. If the attempt at establishing the security context fails 1018 * then a second attempt will be made by ads_bind() if a new TGT has not been 1019 * already obtained in ads_acquire_cred. The second attempt, if allowed, will 1020 * obtained a new TGT here and a new handle to the credential will also be 1021 * obtained in ads_acquire_cred. LDAP SASL bind is used to send and receive 1022 * the GSS tokens to and from the ADS server. 1023 * Please look at ads_bind for more information. 1024 * Paramters: 1025 * ah : handle to ADS server 1026 * cred_handle : handle to administrative user's credential (TGT) 1027 * oid : Kerberos 5 object identifier 1028 * kinit_retry : if 0 then a second attempt can be made to establish a 1029 * security context with ADS server if first attempt fails 1030 * Returns: 1031 * gss_context : security context to ADS server 1032 * sercred : encrypted ADS server's supported security layers 1033 * do_acquire_cred: if 1 then a second attempt will be made to establish a 1034 * security context with ADS server after getting a new 1035 * handle to the user's credential 1036 * kinit_retry : if 1 then a second attempt will be made to establish a 1037 * a security context and no further attempts can be made 1038 * -1 : error 1039 * 0 : success 1040 */ 1041 static int 1042 ads_establish_sec_context(ADS_HANDLE *ah, gss_ctx_id_t *gss_context, 1043 gss_cred_id_t cred_handle, gss_OID oid, struct berval **sercred, 1044 int *kinit_retry, int *do_acquire_cred) 1045 { 1046 OM_uint32 maj, min, time_rec; 1047 char service_name[ADS_MAXBUFLEN]; 1048 gss_buffer_desc send_tok, service_buf; 1049 gss_name_t target_name; 1050 gss_buffer_desc input; 1051 gss_buffer_desc *inputptr; 1052 struct berval cred; 1053 OM_uint32 ret_flags; 1054 int stat; 1055 int gss_flags; 1056 1057 (void) snprintf(service_name, ADS_MAXBUFLEN, "ldap@%s", ah->hostname); 1058 service_buf.value = service_name; 1059 service_buf.length = strlen(service_name)+1; 1060 if ((maj = gss_import_name(&min, &service_buf, 1061 (gss_OID) gss_nt_service_name, 1062 &target_name)) != GSS_S_COMPLETE) { 1063 ads_display_stat(maj, min); 1064 if (oid != GSS_C_NO_OID) 1065 (void) gss_release_oid(&min, &oid); 1066 return (-1); 1067 } 1068 1069 *gss_context = GSS_C_NO_CONTEXT; 1070 *sercred = NULL; 1071 inputptr = GSS_C_NO_BUFFER; 1072 gss_flags = GSS_C_MUTUAL_FLAG; 1073 do { 1074 if (krb5_establish_sec_ctx_kinit(ah->user, ah->pwd, 1075 cred_handle, gss_context, target_name, oid, 1076 gss_flags, inputptr, &send_tok, 1077 &ret_flags, &time_rec, kinit_retry, 1078 do_acquire_cred, &maj, "ads") == -1) { 1079 if (oid != GSS_C_NO_OID) 1080 (void) gss_release_oid(&min, &oid); 1081 (void) gss_release_name(&min, &target_name); 1082 return (-1); 1083 } 1084 1085 cred.bv_val = send_tok.value; 1086 cred.bv_len = send_tok.length; 1087 if (*sercred) { 1088 ber_bvfree(*sercred); 1089 *sercred = NULL; 1090 } 1091 stat = ldap_sasl_bind_s(ah->ld, NULL, "GSSAPI", 1092 &cred, NULL, NULL, sercred); 1093 if (stat != LDAP_SUCCESS && 1094 stat != LDAP_SASL_BIND_IN_PROGRESS) { 1095 /* LINTED - E_SEC_PRINTF_VAR_FMT */ 1096 syslog(LOG_ERR, ldap_err2string(stat)); 1097 if (oid != GSS_C_NO_OID) 1098 (void) gss_release_oid(&min, &oid); 1099 (void) gss_release_name(&min, &target_name); 1100 (void) gss_release_buffer(&min, &send_tok); 1101 return (-1); 1102 } 1103 input.value = (*sercred)->bv_val; 1104 input.length = (*sercred)->bv_len; 1105 inputptr = &input; 1106 if (send_tok.length > 0) 1107 (void) gss_release_buffer(&min, &send_tok); 1108 } while (maj != GSS_S_COMPLETE); 1109 1110 if (oid != GSS_C_NO_OID) 1111 (void) gss_release_oid(&min, &oid); 1112 (void) gss_release_name(&min, &target_name); 1113 1114 return (0); 1115 } 1116 1117 /* 1118 * ads_negotiate_sec_layer 1119 * Call by ads_bind() to negotiate additional security layer for further 1120 * communication after security context establishment. No additional security 1121 * is needed so a "no security layer" is negotiated. The security layer is 1122 * described in the SASL RFC 2478 and this step is needed for secure LDAP 1123 * binding. LDAP SASL bind is used to send and receive the GSS tokens to and 1124 * from the ADS server. 1125 * Please look at ads_bind for more information. 1126 * 1127 * Paramters: 1128 * ah : handle to ADS server 1129 * gss_context: security context to ADS server 1130 * sercred : encrypted ADS server's supported security layers 1131 * Returns: 1132 * -1 : error 1133 * 0 : success 1134 */ 1135 static int 1136 ads_negotiate_sec_layer(ADS_HANDLE *ah, gss_ctx_id_t gss_context, 1137 struct berval *sercred) 1138 { 1139 OM_uint32 maj, min; 1140 gss_buffer_desc unwrap_inbuf, unwrap_outbuf; 1141 gss_buffer_desc wrap_inbuf, wrap_outbuf; 1142 int conf_state, sec_layer; 1143 char auth_id[5]; 1144 struct berval cred; 1145 int stat; 1146 gss_qop_t qt; 1147 1148 /* check for server supported security layer */ 1149 unwrap_inbuf.value = sercred->bv_val; 1150 unwrap_inbuf.length = sercred->bv_len; 1151 if ((maj = gss_unwrap(&min, gss_context, 1152 &unwrap_inbuf, &unwrap_outbuf, 1153 &conf_state, &qt)) != GSS_S_COMPLETE) { 1154 ads_display_stat(maj, min); 1155 if (sercred) 1156 ber_bvfree(sercred); 1157 return (-1); 1158 } 1159 sec_layer = *((char *)unwrap_outbuf.value); 1160 (void) gss_release_buffer(&min, &unwrap_outbuf); 1161 if (!(sec_layer & 1)) { 1162 syslog(LOG_ERR, "smb_ads: ADS server does not support " 1163 "no security layer!\n"); 1164 if (sercred) ber_bvfree(sercred); 1165 return (-1); 1166 } 1167 if (sercred) ber_bvfree(sercred); 1168 1169 /* no security layer needed after successful binding */ 1170 auth_id[0] = 0x01; 1171 1172 /* byte 2-4: max client recv size in network byte order */ 1173 auth_id[1] = 0x00; 1174 auth_id[2] = 0x40; 1175 auth_id[3] = 0x00; 1176 wrap_inbuf.value = auth_id; 1177 wrap_inbuf.length = 4; 1178 conf_state = 0; 1179 if ((maj = gss_wrap(&min, gss_context, conf_state, 0, &wrap_inbuf, 1180 &conf_state, &wrap_outbuf)) != GSS_S_COMPLETE) { 1181 ads_display_stat(maj, min); 1182 return (-1); 1183 } 1184 1185 cred.bv_val = wrap_outbuf.value; 1186 cred.bv_len = wrap_outbuf.length; 1187 sercred = NULL; 1188 stat = ldap_sasl_bind_s(ah->ld, NULL, "GSSAPI", &cred, NULL, NULL, 1189 &sercred); 1190 if (stat != LDAP_SUCCESS && stat != LDAP_SASL_BIND_IN_PROGRESS) { 1191 /* LINTED - E_SEC_PRINTF_VAR_FMT */ 1192 syslog(LOG_ERR, ldap_err2string(stat)); 1193 (void) gss_release_buffer(&min, &wrap_outbuf); 1194 return (-1); 1195 } 1196 1197 (void) gss_release_buffer(&min, &wrap_outbuf); 1198 if (sercred) 1199 ber_bvfree(sercred); 1200 1201 return (0); 1202 } 1203 1204 /* 1205 * ads_bind 1206 * Use secure binding to bind to ADS server. 1207 * Use GSS-API with Kerberos 5 as the security mechanism and LDAP SASL with 1208 * Kerberos 5 as the security mechanisn to authenticate, obtain a security 1209 * context, and securely bind an administrative user so that other LDAP 1210 * commands can be used, i.e. add and delete. 1211 * 1212 * To obtain the security context, a Kerberos ticket-granting ticket (TGT) 1213 * for the user is needed to obtain a ticket for the LDAP service. To get 1214 * a TGT for the user, the username and password is needed. Once a TGT is 1215 * obtained then it will be stored locally and used until it is expired. 1216 * This routine will automatically obtained a TGT for the first time or when 1217 * it expired. LDAP SASL bind is then finally used to send GSS tokens to 1218 * obtain a security context for the LDAP service on the ADS server. If 1219 * there is any problem getting the security context then a new TGT will be 1220 * obtain to try getting the security context once more. 1221 * 1222 * After the security context is obtain and established, the LDAP SASL bind 1223 * is used to negotiate an additional security layer. No further security is 1224 * needed so a "no security layer" is negotiated. After this the security 1225 * context can be deleted and further LDAP commands can be sent to the ADS 1226 * server until a LDAP unbind command is issued to the ADS server. 1227 * Paramaters: 1228 * ah: handle to ADS server 1229 * Returns: 1230 * -1: error 1231 * 0: success 1232 */ 1233 static int 1234 ads_bind(ADS_HANDLE *ah) 1235 { 1236 OM_uint32 min; 1237 gss_cred_id_t cred_handle; 1238 gss_ctx_id_t gss_context; 1239 gss_OID oid; 1240 struct berval *sercred; 1241 int kinit_retry, do_acquire_cred; 1242 int rc = 0; 1243 1244 kinit_retry = 0; 1245 do_acquire_cred = 0; 1246 1247 acquire_cred: 1248 1249 if (ads_acquire_cred(ah, &cred_handle, &oid, &kinit_retry)) 1250 return (-1); 1251 1252 if (ads_establish_sec_context(ah, &gss_context, cred_handle, 1253 oid, &sercred, &kinit_retry, &do_acquire_cred)) { 1254 (void) gss_release_cred(&min, &cred_handle); 1255 if (do_acquire_cred) { 1256 do_acquire_cred = 0; 1257 goto acquire_cred; 1258 } 1259 return (-1); 1260 } 1261 rc = ads_negotiate_sec_layer(ah, gss_context, sercred); 1262 1263 if (cred_handle != GSS_C_NO_CREDENTIAL) 1264 (void) gss_release_cred(&min, &cred_handle); 1265 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1266 1267 return ((rc) ? -1 : 0); 1268 } 1269 1270 /* 1271 * ads_add_share 1272 * Call by ads_publish_share to create share object in ADS. 1273 * This routine specifies the attributes of an ADS LDAP share object. The first 1274 * attribute and values define the type of ADS object, the share object. The 1275 * second attribute and value define the UNC of the share data for the share 1276 * object. The LDAP synchronous add command is used to add the object into ADS. 1277 * The container location to add the object needs to specified. 1278 * Parameters: 1279 * ah : handle to ADS server 1280 * adsShareName: name of share object to be created in ADS 1281 * shareUNC : share name on NetForce 1282 * adsContainer: location in ADS to create share object 1283 * 1284 * Returns: 1285 * -1 : error 1286 * 0 : success 1287 */ 1288 int 1289 ads_add_share(ADS_HANDLE *ah, const char *adsShareName, 1290 const char *unc_name, const char *adsContainer) 1291 { 1292 LDAPMod *attrs[ADS_SHARE_NUM_ATTR]; 1293 char *tmp1[5], *tmp2[5]; 1294 int j = 0; 1295 char *share_dn; 1296 char buf[ADS_MAXMSGLEN]; 1297 int len, ret; 1298 1299 len = 5 + strlen(adsShareName) + strlen(adsContainer) + 1300 strlen(ah->domain_dn) + 1; 1301 1302 share_dn = (char *)malloc(len); 1303 if (share_dn == NULL) 1304 return (-1); 1305 1306 (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, 1307 adsContainer, ah->domain_dn); 1308 1309 for (j = 0; j < (ADS_SHARE_NUM_ATTR - 1); j++) { 1310 attrs[j] = (LDAPMod *)malloc(sizeof (LDAPMod)); 1311 if (attrs[j] == NULL) { 1312 free_attr(attrs); 1313 free(share_dn); 1314 return (-1); 1315 } 1316 } 1317 1318 j = 0; 1319 attrs[j]->mod_op = LDAP_MOD_ADD; 1320 attrs[j]->mod_type = "objectClass"; 1321 tmp1[0] = "top"; 1322 tmp1[1] = "leaf"; 1323 tmp1[2] = "connectionPoint"; 1324 tmp1[3] = "volume"; 1325 tmp1[4] = 0; 1326 attrs[j]->mod_values = tmp1; 1327 1328 attrs[++j]->mod_op = LDAP_MOD_ADD; 1329 attrs[j]->mod_type = "uNCName"; 1330 tmp2[0] = (char *)unc_name; 1331 tmp2[1] = 0; 1332 attrs[j]->mod_values = tmp2; 1333 1334 attrs[++j] = 0; 1335 1336 if ((ret = ldap_add_s(ah->ld, share_dn, attrs)) != LDAP_SUCCESS) { 1337 (void) snprintf(buf, ADS_MAXMSGLEN, 1338 "ads_add_share: %s:", share_dn); 1339 /* LINTED - E_SEC_PRINTF_VAR_FMT */ 1340 syslog(LOG_ERR, ldap_err2string(ret)); 1341 free_attr(attrs); 1342 free(share_dn); 1343 return (ret); 1344 } 1345 free(share_dn); 1346 free_attr(attrs); 1347 1348 (void) snprintf(buf, ADS_MAXMSGLEN, 1349 "Share %s has been added to ADS container: %s.\n", adsShareName, 1350 adsContainer); 1351 syslog(LOG_DEBUG, "smb_ads: %s", buf); 1352 1353 return (0); 1354 } 1355 1356 /* 1357 * ads_del_share 1358 * Call by ads_remove_share to remove share object from ADS. The container 1359 * location to remove the object needs to specified. The LDAP synchronous 1360 * delete command is used. 1361 * Parameters: 1362 * ah : handle to ADS server 1363 * adsShareName: name of share object in ADS to be removed 1364 * adsContainer: location of share object in ADS 1365 * Returns: 1366 * -1 : error 1367 * 0 : success 1368 */ 1369 static int 1370 ads_del_share(ADS_HANDLE *ah, const char *adsShareName, 1371 const char *adsContainer) 1372 { 1373 char *share_dn, buf[ADS_MAXMSGLEN]; 1374 int len, ret; 1375 1376 len = 5 + strlen(adsShareName) + strlen(adsContainer) + 1377 strlen(ah->domain_dn) + 1; 1378 1379 share_dn = (char *)malloc(len); 1380 if (share_dn == NULL) 1381 return (-1); 1382 1383 (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, 1384 adsContainer, ah->domain_dn); 1385 if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) { 1386 /* LINTED - E_SEC_PRINTF_VAR_FMT */ 1387 syslog(LOG_ERR, ldap_err2string(ret)); 1388 free(share_dn); 1389 return (-1); 1390 } 1391 free(share_dn); 1392 1393 (void) snprintf(buf, ADS_MAXMSGLEN, 1394 "Share %s has been removed from ADS container: %s.\n", 1395 adsShareName, adsContainer); 1396 syslog(LOG_DEBUG, "smb_ads: %s", buf); 1397 1398 return (0); 1399 } 1400 1401 1402 /* 1403 * ads_escape_search_filter_chars 1404 * 1405 * This routine will escape the special characters found in a string 1406 * that will later be passed to the ldap search filter. 1407 * 1408 * RFC 1960 - A String Representation of LDAP Search Filters 1409 * 3. String Search Filter Definition 1410 * If a value must contain one of the characters '*' OR '(' OR ')', 1411 * these characters 1412 * should be escaped by preceding them with the backslash '\' character. 1413 * 1414 * RFC 2252 - LDAP Attribute Syntax Definitions 1415 * a backslash quoting mechanism is used to escape 1416 * the following separator symbol character (such as "'", "$" or "#") if 1417 * it should occur in that string. 1418 */ 1419 static int 1420 ads_escape_search_filter_chars(const char *src, char *dst) 1421 { 1422 int avail = ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */ 1423 1424 if (src == NULL || dst == NULL) 1425 return (-1); 1426 1427 while (*src) { 1428 if (!avail) { 1429 *dst = 0; 1430 return (-1); 1431 } 1432 1433 switch (*src) { 1434 case '\\': 1435 case '\'': 1436 case '$': 1437 case '#': 1438 case '*': 1439 case '(': 1440 case ')': 1441 *dst++ = '\\'; 1442 avail--; 1443 /* fall through */ 1444 1445 default: 1446 *dst++ = *src++; 1447 avail--; 1448 } 1449 } 1450 1451 *dst = 0; 1452 1453 return (0); 1454 } 1455 1456 /* 1457 * ads_lookup_share 1458 * The search filter is set to search for a specific share name in the 1459 * specified ADS container. The LDSAP synchronous search command is used. 1460 * Parameters: 1461 * ah : handle to ADS server 1462 * adsShareName: name of share object in ADS to be searched 1463 * adsContainer: location of share object in ADS 1464 * Returns: 1465 * -1 : error 1466 * 0 : not found 1467 * 1 : found 1468 */ 1469 int 1470 ads_lookup_share(ADS_HANDLE *ah, const char *adsShareName, 1471 const char *adsContainer, char *unc_name) 1472 { 1473 char *attrs[4], filter[ADS_MAXBUFLEN]; 1474 char *share_dn; 1475 int len, ret; 1476 LDAPMessage *res; 1477 char tmpbuf[ADS_MAXBUFLEN]; 1478 1479 if (adsShareName == NULL || adsContainer == NULL) 1480 return (-1); 1481 1482 len = 5 + strlen(adsShareName) + strlen(adsContainer) + 1483 strlen(ah->domain_dn) + 1; 1484 1485 share_dn = (char *)malloc(len); 1486 if (share_dn == NULL) 1487 return (-1); 1488 1489 (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, 1490 adsContainer, ah->domain_dn); 1491 1492 res = NULL; 1493 attrs[0] = "cn"; 1494 attrs[1] = "objectClass"; 1495 attrs[2] = "uNCName"; 1496 attrs[3] = NULL; 1497 1498 if (ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) { 1499 free(share_dn); 1500 return (-1); 1501 } 1502 1503 (void) snprintf(filter, sizeof (filter), 1504 "(&(objectClass=volume)(uNCName=%s))", tmpbuf); 1505 1506 if ((ret = ldap_search_s(ah->ld, share_dn, 1507 LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) { 1508 /* LINTED - E_SEC_PRINTF_VAR_FMT */ 1509 syslog(LOG_ERR, ldap_err2string(ret)); 1510 (void) ldap_msgfree(res); 1511 free(share_dn); 1512 return (0); 1513 } 1514 1515 (void) free(share_dn); 1516 1517 /* no match is found */ 1518 if (ldap_count_entries(ah->ld, res) == 0) { 1519 (void) ldap_msgfree(res); 1520 return (0); 1521 } 1522 1523 /* free the search results */ 1524 (void) ldap_msgfree(res); 1525 1526 return (1); 1527 } 1528 1529 /* 1530 * ads_convert_directory 1531 * Convert relative share directory to UNC to be appended to hostname. 1532 * i.e. cvol/a/b -> cvol\a\b 1533 */ 1534 char * 1535 ads_convert_directory(char *rel_dir) 1536 { 1537 char *t, *s2; 1538 int len; 1539 1540 if (rel_dir == NULL) 1541 return (NULL); 1542 1543 len = strlen(rel_dir) + 1; 1544 s2 = (char *)malloc(len); 1545 if (s2 == NULL) 1546 return (NULL); 1547 1548 t = s2; 1549 while (*rel_dir) { 1550 if (*rel_dir == '/') { 1551 *t++ = '\\'; 1552 rel_dir++; 1553 } else { 1554 *t++ = *rel_dir++; 1555 } 1556 } 1557 *t = '\0'; 1558 return (s2); 1559 } 1560 1561 /* 1562 * ads_publish_share 1563 * Publish share into ADS. If a share name already exist in ADS in the same 1564 * container then the existing share object is removed before adding the new 1565 * share object. 1566 * Parameters: 1567 * ah : handle return from ads_open 1568 * adsShareName: name of share to be added to ADS directory 1569 * shareUNC : name of share on client, can be NULL to use the same name 1570 * as adsShareName 1571 * adsContainer: location for share to be added in ADS directory, ie 1572 * ou=share_folder 1573 * uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR 1574 * to use host ip addr for UNC. 1575 * Returns: 1576 * -1 : error 1577 * 0 : success 1578 */ 1579 int 1580 ads_publish_share(ADS_HANDLE *ah, const char *adsShareName, 1581 const char *shareUNC, const char *adsContainer, const char *hostname) 1582 { 1583 int ret; 1584 char unc_name[ADS_MAXBUFLEN]; 1585 1586 if (adsShareName == NULL || adsContainer == NULL) 1587 return (-1); 1588 1589 if (shareUNC == 0 || *shareUNC == 0) 1590 shareUNC = adsShareName; 1591 1592 if (ads_build_unc_name(unc_name, sizeof (unc_name), 1593 hostname, shareUNC) < 0) { 1594 syslog(LOG_DEBUG, "smb_ads: Cannot publish share '%s' " 1595 "[missing UNC name]", shareUNC); 1596 return (-1); 1597 } 1598 1599 ret = ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1600 1601 switch (ret) { 1602 case 1: 1603 (void) ads_del_share(ah, adsShareName, adsContainer); 1604 ret = ads_add_share(ah, adsShareName, unc_name, adsContainer); 1605 break; 1606 1607 case 0: 1608 ret = ads_add_share(ah, adsShareName, unc_name, adsContainer); 1609 if (ret == LDAP_ALREADY_EXISTS) { 1610 syslog(LOG_DEBUG, "smb_ads: Cannot publish share '%s' " 1611 "[name is already in use]", adsShareName); 1612 ret = -1; 1613 } 1614 break; 1615 1616 case -1: 1617 default: 1618 /* return with error code */ 1619 ret = -1; 1620 } 1621 1622 return (ret); 1623 } 1624 1625 /* 1626 * ads_remove_share 1627 * Remove share from ADS. A search is done first before explicitly removing 1628 * the share. 1629 * Parameters: 1630 * ah : handle return from ads_open 1631 * adsShareName: name of share to be removed from ADS directory 1632 * adsContainer: location for share to be removed from ADS directory, ie 1633 * ou=share_folder 1634 * Returns: 1635 * -1 : error 1636 * 0 : success 1637 */ 1638 int 1639 ads_remove_share(ADS_HANDLE *ah, const char *adsShareName, const char *shareUNC, 1640 const char *adsContainer, const char *hostname) 1641 { 1642 int ret; 1643 char unc_name[ADS_MAXBUFLEN]; 1644 1645 if (adsShareName == NULL || adsContainer == NULL) 1646 return (-1); 1647 if (shareUNC == 0 || *shareUNC == 0) 1648 shareUNC = adsShareName; 1649 1650 if (ads_build_unc_name(unc_name, sizeof (unc_name), 1651 hostname, shareUNC) < 0) { 1652 syslog(LOG_DEBUG, "smb_ads: Unable to remove share '%s' from " 1653 "ADS [missing UNC name]", shareUNC); 1654 return (-1); 1655 } 1656 1657 ret = ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1658 if (ret == 0) 1659 return (0); 1660 if (ret == -1) 1661 return (-1); 1662 1663 return (ads_del_share(ah, adsShareName, adsContainer)); 1664 } 1665 1666 /* 1667 * ads_get_computer_dn 1668 * 1669 * Build the distinguish name for this system. 1670 */ 1671 static void 1672 ads_get_computer_dn(ADS_HANDLE *ah, char *buf, size_t buflen) 1673 { 1674 char hostname[MAXHOSTNAMELEN]; 1675 1676 (void) smb_gethostname(hostname, MAXHOSTNAMELEN, 0); 1677 (void) snprintf(buf, buflen, "cn=%s,cn=%s,%s", 1678 hostname, ADS_COMPUTERS_CN, ah->domain_dn); 1679 } 1680 1681 static char * 1682 ads_get_host_principal(char *fqhost) 1683 { 1684 int len; 1685 char *princ; 1686 1687 if (!fqhost) 1688 return (NULL); 1689 1690 len = strlen(ADS_HOST_PREFIX) + strlen(fqhost) + 1; 1691 princ = (char *)malloc(len); 1692 1693 if (!princ) { 1694 syslog(LOG_ERR, "ads_get_host_principal: resource shortage"); 1695 return (NULL); 1696 } 1697 (void) snprintf(princ, len, "%s%s", ADS_HOST_PREFIX, 1698 fqhost); 1699 1700 return (princ); 1701 } 1702 1703 static char * 1704 ads_get_host_principal_w_realm(char *princ, char *domain) 1705 { 1706 int len; 1707 char *realm; 1708 char *princ_r; 1709 1710 if (!princ || !domain) 1711 return (NULL); 1712 1713 realm = strdup(domain); 1714 if (!realm) 1715 return (NULL); 1716 1717 (void) utf8_strupr(realm); 1718 1719 len = strlen(princ) + 1 + strlen(realm) + 1; 1720 princ_r = (char *)malloc(len); 1721 if (!princ_r) { 1722 syslog(LOG_ERR, "ads_get_host_principal_w_realm: resource" 1723 " shortage"); 1724 free(realm); 1725 return (NULL); 1726 } 1727 1728 (void) snprintf(princ_r, len, "%s@%s", princ, realm); 1729 free(realm); 1730 1731 return (princ_r); 1732 } 1733 1734 /* 1735 * ads_get_host_principals 1736 * 1737 * If fqhost is NULL, this function will attempt to obtain fully qualified 1738 * hostname prior to generating the host principals. If caller is not 1739 * interested in getting the principal name without the Kerberos realm 1740 * info, princ can be set to NULL. 1741 */ 1742 static int 1743 ads_get_host_principals(char *fqhost, char *domain, char **princ, 1744 char **princ_r) 1745 { 1746 char hostname[MAXHOSTNAMELEN]; 1747 char *p; 1748 1749 if (princ != NULL) 1750 *princ = NULL; 1751 1752 *princ_r = NULL; 1753 1754 if (fqhost) { 1755 (void) strlcpy(hostname, fqhost, MAXHOSTNAMELEN); 1756 } else { 1757 if (smb_gethostname(hostname, MAXHOSTNAMELEN, 0) != 0) 1758 return (-1); 1759 1760 (void) snprintf(hostname, MAXHOSTNAMELEN, "%s.%s", hostname, 1761 domain); 1762 } 1763 1764 if ((p = ads_get_host_principal(hostname)) == NULL) { 1765 return (-1); 1766 } 1767 1768 *princ_r = ads_get_host_principal_w_realm(p, domain); 1769 if (*princ_r == NULL) { 1770 free(p); 1771 return (-1); 1772 } 1773 1774 if (princ != NULL) 1775 *princ = p; 1776 else 1777 free(p); 1778 1779 return (0); 1780 } 1781 1782 /* 1783 * ads_add_computer 1784 * 1785 * Returns 0 upon success. Otherwise, returns -1. 1786 */ 1787 static int 1788 ads_add_computer(ADS_HANDLE *ah) 1789 { 1790 return (ads_computer_op(ah, LDAP_MOD_ADD)); 1791 } 1792 1793 /* 1794 * ads_modify_computer 1795 * 1796 * Returns 0 upon success. Otherwise, returns -1. 1797 */ 1798 static int 1799 ads_modify_computer(ADS_HANDLE *ah) 1800 { 1801 return (ads_computer_op(ah, LDAP_MOD_REPLACE)); 1802 } 1803 1804 static int 1805 ads_computer_op(ADS_HANDLE *ah, int op) 1806 { 1807 LDAPMod *attrs[ADS_COMPUTER_NUM_ATTR]; 1808 char *oc_vals[6], *sam_val[2], *usr_val[2]; 1809 char *svc_val[2], *ctl_val[2], *fqh_val[2]; 1810 int j = 0; 1811 int ret, usrctl_flags = 0; 1812 char sam_acct[MAXHOSTNAMELEN + 1]; 1813 char fqhost[MAXHOSTNAMELEN]; 1814 char dn[ADS_DN_MAX]; 1815 char *user_principal, *svc_principal; 1816 char usrctl_buf[16]; 1817 int max; 1818 1819 if (smb_gethostname(fqhost, MAXHOSTNAMELEN, 0) != 0) 1820 return (-1); 1821 1822 (void) strlcpy(sam_acct, fqhost, MAXHOSTNAMELEN + 1); 1823 (void) strlcat(sam_acct, "$", MAXHOSTNAMELEN + 1); 1824 (void) snprintf(fqhost, MAXHOSTNAMELEN, "%s.%s", fqhost, 1825 ah->domain); 1826 1827 if (ads_get_host_principals(fqhost, ah->domain, &svc_principal, 1828 &user_principal) == -1) { 1829 syslog(LOG_ERR, 1830 "ads_computer_op: unable to get host principal"); 1831 return (-1); 1832 } 1833 1834 ads_get_computer_dn(ah, dn, ADS_DN_MAX); 1835 1836 max = (ADS_COMPUTER_NUM_ATTR - ((op != LDAP_MOD_ADD) ? 1 : 0)); 1837 for (j = 0; j < (max - 1); j++) { 1838 attrs[j] = (LDAPMod *)malloc(sizeof (LDAPMod)); 1839 if (attrs[j] == NULL) { 1840 free_attr(attrs); 1841 free(user_principal); 1842 free(svc_principal); 1843 return (-1); 1844 } 1845 } 1846 1847 j = -1; 1848 /* objectClass attribute is not modifiable. */ 1849 if (op == LDAP_MOD_ADD) { 1850 attrs[++j]->mod_op = op; 1851 attrs[j]->mod_type = "objectClass"; 1852 oc_vals[0] = "top"; 1853 oc_vals[1] = "person"; 1854 oc_vals[2] = "organizationalPerson"; 1855 oc_vals[3] = "user"; 1856 oc_vals[4] = "computer"; 1857 oc_vals[5] = 0; 1858 attrs[j]->mod_values = oc_vals; 1859 } 1860 1861 attrs[++j]->mod_op = op; 1862 attrs[j]->mod_type = "sAMAccountName"; 1863 sam_val[0] = sam_acct; 1864 sam_val[1] = 0; 1865 attrs[j]->mod_values = sam_val; 1866 1867 attrs[++j]->mod_op = op; 1868 attrs[j]->mod_type = "userPrincipalName"; 1869 usr_val[0] = user_principal; 1870 usr_val[1] = 0; 1871 attrs[j]->mod_values = usr_val; 1872 1873 attrs[++j]->mod_op = op; 1874 attrs[j]->mod_type = "servicePrincipalName"; 1875 svc_val[0] = svc_principal; 1876 svc_val[1] = 0; 1877 attrs[j]->mod_values = svc_val; 1878 1879 attrs[++j]->mod_op = op; 1880 attrs[j]->mod_type = "userAccountControl"; 1881 usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 1882 ADS_USER_ACCT_CTL_PASSWD_NOTREQD | 1883 ADS_USER_ACCT_CTL_ACCOUNTDISABLE); 1884 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 1885 ctl_val[0] = usrctl_buf; 1886 ctl_val[1] = 0; 1887 attrs[j]->mod_values = ctl_val; 1888 1889 attrs[++j]->mod_op = op; 1890 attrs[j]->mod_type = "dNSHostName"; 1891 fqh_val[0] = fqhost; 1892 fqh_val[1] = 0; 1893 attrs[j]->mod_values = fqh_val; 1894 1895 attrs[++j] = 0; 1896 1897 switch (op) { 1898 case LDAP_MOD_ADD: 1899 if ((ret = ldap_add_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1900 syslog(LOG_ERR, "ads_add_computer: %s", 1901 ldap_err2string(ret)); 1902 ret = -1; 1903 } 1904 break; 1905 1906 case LDAP_MOD_REPLACE: 1907 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1908 syslog(LOG_ERR, "ads_modify_computer: %s", 1909 ldap_err2string(ret)); 1910 ret = -1; 1911 } 1912 break; 1913 1914 default: 1915 ret = -1; 1916 1917 } 1918 1919 free_attr(attrs); 1920 free(user_principal); 1921 free(svc_principal); 1922 1923 return (ret); 1924 } 1925 1926 /* 1927 * Delete an ADS computer account. 1928 */ 1929 static void 1930 ads_del_computer(ADS_HANDLE *ah) 1931 { 1932 char dn[ADS_DN_MAX]; 1933 int rc; 1934 1935 ads_get_computer_dn(ah, dn, ADS_DN_MAX); 1936 1937 if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) { 1938 syslog(LOG_DEBUG, "ads_del_computer: %s", 1939 ldap_err2string(rc)); 1940 } 1941 } 1942 1943 /* 1944 * ads_lookup_computer_n_attr 1945 * 1946 * Lookup the value of the specified attribute on the computer 1947 * object. If the specified attribute can be found, its value is returned 1948 * via 'val' parameter. 1949 * 1950 * 'attr' parameter can be set to NULL if you only attempt to 1951 * see whether the computer object exists on AD or not. 1952 * 1953 * Return: 1954 * 1 if both the computer and the specified attribute is found. 1955 * 0 if either the computer or the specified attribute is not found. 1956 * -1 on error. 1957 */ 1958 static int 1959 ads_lookup_computer_n_attr(ADS_HANDLE *ah, char *attr, char **val) 1960 { 1961 char *attrs[2], filter[ADS_MAXBUFLEN]; 1962 LDAPMessage *res, *entry; 1963 char **vals; 1964 char tmpbuf[ADS_MAXBUFLEN]; 1965 char my_hostname[MAXHOSTNAMELEN], sam_acct[MAXHOSTNAMELEN + 1]; 1966 char dn[ADS_DN_MAX]; 1967 1968 if (smb_gethostname(my_hostname, MAXHOSTNAMELEN, 0) != 0) 1969 return (-1); 1970 1971 (void) snprintf(sam_acct, sizeof (sam_acct), "%s$", my_hostname); 1972 ads_get_computer_dn(ah, dn, ADS_DN_MAX); 1973 1974 res = NULL; 1975 attrs[0] = attr; 1976 attrs[1] = NULL; 1977 1978 if (ads_escape_search_filter_chars(sam_acct, tmpbuf) != 0) { 1979 return (-1); 1980 } 1981 1982 (void) snprintf(filter, sizeof (filter), "objectClass=computer", 1983 tmpbuf); 1984 1985 if (ldap_search_s(ah->ld, dn, LDAP_SCOPE_BASE, filter, attrs, 0, 1986 &res) != LDAP_SUCCESS) { 1987 (void) ldap_msgfree(res); 1988 return (0); 1989 } 1990 1991 if (attr) { 1992 /* no match for the specified attribute is found */ 1993 if (ldap_count_entries(ah->ld, res) == 0) { 1994 if (val) 1995 *val = NULL; 1996 1997 (void) ldap_msgfree(res); 1998 return (0); 1999 } 2000 2001 entry = ldap_first_entry(ah->ld, res); 2002 if (entry) { 2003 vals = ldap_get_values(ah->ld, entry, attr); 2004 if (!vals && val) { 2005 *val = NULL; 2006 (void) ldap_msgfree(res); 2007 return (0); 2008 } 2009 2010 if (vals[0] != NULL && val) 2011 *val = strdup(vals[0]); 2012 2013 ldap_value_free(vals); 2014 } 2015 } 2016 2017 /* free the search results */ 2018 (void) ldap_msgfree(res); 2019 return (1); 2020 } 2021 2022 /* 2023 * ads_find_computer 2024 * 2025 * Return: 2026 * 1 if found. 2027 * 0 if not found or encounters error. 2028 */ 2029 static int 2030 ads_find_computer(ADS_HANDLE *ah) 2031 { 2032 return (ads_lookup_computer_n_attr(ah, NULL, NULL) == 1); 2033 } 2034 2035 /* 2036 * ads_update_computer_cntrl_attr 2037 * 2038 * Modify the user account control attribute of an existing computer 2039 * object on AD. 2040 * 2041 * Returns 0 on success. Otherwise, returns -1. 2042 */ 2043 static int 2044 ads_update_computer_cntrl_attr(ADS_HANDLE *ah, int des_only) 2045 { 2046 LDAPMod *attrs[6]; 2047 char *ctl_val[2]; 2048 int j = -1; 2049 int ret, usrctl_flags = 0; 2050 char dn[ADS_DN_MAX]; 2051 char usrctl_buf[16]; 2052 2053 ads_get_computer_dn(ah, dn, ADS_DN_MAX); 2054 2055 attrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod)); 2056 attrs[j]->mod_op = LDAP_MOD_REPLACE; 2057 attrs[j]->mod_type = "userAccountControl"; 2058 2059 usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 2060 ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | 2061 ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); 2062 2063 if (des_only) 2064 usrctl_flags |= ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY; 2065 2066 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 2067 ctl_val[0] = usrctl_buf; 2068 ctl_val[1] = 0; 2069 attrs[j]->mod_values = ctl_val; 2070 2071 attrs[++j] = 0; 2072 2073 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2074 syslog(LOG_ERR, "ads_modify_computer: %s", 2075 ldap_err2string(ret)); 2076 ret = -1; 2077 } 2078 2079 free_attr(attrs); 2080 return (ret); 2081 } 2082 2083 /* 2084 * ads_lookup_computer_attr_kvno 2085 * 2086 * Lookup the value of the Kerberos version number attribute of the computer 2087 * account. 2088 */ 2089 static krb5_kvno 2090 ads_lookup_computer_attr_kvno(ADS_HANDLE *ah) 2091 { 2092 char *val = NULL; 2093 int kvno = 1; 2094 2095 if (ads_lookup_computer_n_attr(ah, "msDS-KeyVersionNumber", 2096 &val) == 1) { 2097 if (val) { 2098 kvno = atoi(val); 2099 free(val); 2100 } 2101 } 2102 2103 return (kvno); 2104 } 2105 2106 /* 2107 * ads_gen_machine_passwd 2108 * 2109 * Returned a null-terminated machine password generated randomly 2110 * from [0-9a-zA-Z] character set. In order to pass the password 2111 * quality check (three character classes), an uppercase letter is 2112 * used as the first character of the machine password. 2113 */ 2114 static int 2115 ads_gen_machine_passwd(char *machine_passwd, int bufsz) 2116 { 2117 char *data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK" 2118 "LMNOPQRSTUVWXYZ"; 2119 int datalen = strlen(data); 2120 int i, data_idx; 2121 2122 if (!machine_passwd || bufsz == 0) 2123 return (-1); 2124 2125 /* 2126 * The decimal value of upper case 'A' is 65. Randomly pick 2127 * an upper-case letter from the ascii table. 2128 */ 2129 machine_passwd[0] = (random() % 26) + 65; 2130 for (i = 1; i < bufsz - 1; i++) { 2131 data_idx = random() % datalen; 2132 machine_passwd[i] = data[data_idx]; 2133 } 2134 2135 machine_passwd[bufsz - 1] = 0; 2136 return (0); 2137 } 2138 2139 /* 2140 * ads_domain_change_cleanup 2141 * 2142 * If we're attempting to join the system to a new domain, the keys for 2143 * the host principal regarding the old domain should be removed from 2144 * Kerberos keytab. Also, the ads_host_info cache should be cleared. 2145 * 2146 * newdom is fully-qualified domain name. It can be set to empty string 2147 * if user attempts to switch to workgroup mode. 2148 */ 2149 int 2150 ads_domain_change_cleanup(char *newdom) 2151 { 2152 char origdom[MAXHOSTNAMELEN]; 2153 char *princ_r; 2154 krb5_context ctx = NULL; 2155 krb5_principal krb5princ; 2156 int rc; 2157 2158 if (smb_getfqdomainname(origdom, MAXHOSTNAMELEN)) 2159 return (0); 2160 2161 if (strcasecmp(origdom, newdom) == 0) 2162 return (0); 2163 2164 ads_free_host_info(); 2165 if (ads_get_host_principals(NULL, origdom, NULL, &princ_r) == -1) 2166 return (-1); 2167 2168 if (smb_krb5_ctx_init(&ctx) != 0) { 2169 free(princ_r); 2170 return (-1); 2171 } 2172 2173 if (smb_krb5_get_principal(ctx, princ_r, &krb5princ) != 0) { 2174 free(princ_r); 2175 smb_krb5_ctx_fini(ctx); 2176 return (-1); 2177 2178 } 2179 2180 rc = smb_krb5_remove_keytab_entries(ctx, krb5princ, SMBNS_KRB5_KEYTAB); 2181 free(princ_r); 2182 smb_krb5_ctx_fini(ctx); 2183 2184 return (rc); 2185 } 2186 2187 /* 2188 * ads_join 2189 * 2190 * Besides the NT-4 style domain join (using MS-RPC), CIFS server also 2191 * provides the domain join using Kerberos Authentication, Keberos 2192 * Change & Set password, and LDAP protocols. Basically, AD join 2193 * operation would require the following tickets to be acquired for the 2194 * the user account that is provided for the domain join. 2195 * 2196 * 1) a Keberos TGT ticket, 2197 * 2) a ldap service ticket, and 2198 * 3) kadmin/changpw service ticket 2199 * 2200 * The ADS client first sends a ldap search request to find out whether 2201 * or not the workstation trust account already exists in the Active Directory. 2202 * The existing computer object for this workstation will be removed and 2203 * a new one will be added. The machine account password is randomly 2204 * generated and set for the newly created computer object using KPASSD 2205 * protocol (See RFC 3244). Once the password is set, our ADS client 2206 * finalizes the machine account by modifying the user acount control 2207 * attribute of the computer object. Kerberos keys derived from the machine 2208 * account password will be stored locally in /etc/krb5/krb5.keytab file. 2209 * That would be needed while acquiring Kerberos TGT ticket for the host 2210 * principal after the domain join operation. 2211 */ 2212 adjoin_status_t 2213 ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd, 2214 int len) 2215 { 2216 ADS_HANDLE *ah = NULL; 2217 krb5_context ctx = NULL; 2218 krb5_principal krb5princ; 2219 krb5_kvno kvno; 2220 char *princ_r; 2221 boolean_t des_only, delete = B_TRUE; 2222 adjoin_status_t rc = ADJOIN_SUCCESS; 2223 boolean_t new_acct; 2224 /* 2225 * Call library functions that can be used to get 2226 * the list of encryption algorithms available on the system. 2227 * (similar to what 'encrypt -l' CLI does). For now, 2228 * unless someone has modified the configuration of the 2229 * cryptographic framework (very unlikely), the following is the 2230 * list of algorithms available on any system running Nevada 2231 * by default. 2232 */ 2233 krb5_enctype enctypes[] = {ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 2234 ENCTYPE_ARCFOUR_HMAC, ENCTYPE_AES128_CTS_HMAC_SHA1_96}; 2235 2236 if ((ah = ads_open_main(domain, user, usr_passwd)) == NULL) { 2237 (void) smb_config_refresh(); 2238 return (ADJOIN_ERR_GET_HANDLE); 2239 } 2240 2241 if (ads_gen_machine_passwd(machine_passwd, len) != 0) { 2242 ads_close(ah); 2243 (void) smb_config_refresh(); 2244 return (ADJOIN_ERR_GEN_PASSWD); 2245 } 2246 2247 if (ads_find_computer(ah)) { 2248 new_acct = B_FALSE; 2249 if (ads_modify_computer(ah) != 0) { 2250 ads_close(ah); 2251 (void) smb_config_refresh(); 2252 return (ADJOIN_ERR_MOD_TRUST_ACCT); 2253 } 2254 } else { 2255 new_acct = B_TRUE; 2256 if (ads_add_computer(ah) != 0) { 2257 ads_close(ah); 2258 (void) smb_config_refresh(); 2259 return (ADJOIN_ERR_ADD_TRUST_ACCT); 2260 } 2261 } 2262 2263 des_only = B_FALSE; 2264 2265 /* 2266 * If we are talking to a Longhorn server, we need to set up 2267 * the msDS-SupportedEncryptionTypes attribute of the computer 2268 * object accordingly 2269 * 2270 * The code to modify the msDS-SupportedEncryptionTypes can be 2271 * added once we figure out why the Longhorn server rejects the 2272 * SmbSessionSetup request sent by SMB redirector. 2273 */ 2274 2275 if (ads_get_host_principals(NULL, ah->domain, NULL, &princ_r) == -1) { 2276 if (new_acct) 2277 ads_del_computer(ah); 2278 ads_close(ah); 2279 (void) smb_config_refresh(); 2280 return (ADJOIN_ERR_GET_HOST_PRINC); 2281 } 2282 2283 if (smb_krb5_ctx_init(&ctx) != 0) { 2284 rc = ADJOIN_ERR_INIT_KRB_CTX; 2285 goto adjoin_cleanup; 2286 } 2287 2288 if (smb_krb5_get_principal(ctx, princ_r, &krb5princ) != 0) { 2289 rc = ADJOIN_ERR_GET_KRB_PRINC; 2290 goto adjoin_cleanup; 2291 } 2292 2293 if (smb_krb5_setpwd(ctx, krb5princ, machine_passwd) != 0) { 2294 rc = ADJOIN_ERR_KSETPWD; 2295 goto adjoin_cleanup; 2296 } 2297 2298 kvno = ads_lookup_computer_attr_kvno(ah); 2299 2300 if (ads_update_computer_cntrl_attr(ah, des_only) != 0) { 2301 rc = ADJOIN_ERR_UPDATE_CNTRL_ATTR; 2302 goto adjoin_cleanup; 2303 } 2304 2305 if (smb_krb5_update_keytab_entries(ctx, krb5princ, SMBNS_KRB5_KEYTAB, 2306 kvno, machine_passwd, enctypes, 2307 (sizeof (enctypes) / sizeof (krb5_enctype))) != 0) { 2308 rc = ADJOIN_ERR_WRITE_KEYTAB; 2309 goto adjoin_cleanup; 2310 } 2311 2312 /* Set IDMAP config */ 2313 if (smb_config_set_idmap_domain(ah->domain) != 0) { 2314 rc = ADJOIN_ERR_IDMAP_SET_DOMAIN; 2315 goto adjoin_cleanup; 2316 } 2317 2318 /* Refresh IDMAP service */ 2319 if (smb_config_refresh_idmap() != 0) { 2320 rc = ADJOIN_ERR_IDMAP_REFRESH; 2321 goto adjoin_cleanup; 2322 } 2323 2324 delete = B_FALSE; 2325 adjoin_cleanup: 2326 if (new_acct && delete) 2327 ads_del_computer(ah); 2328 2329 if (rc != ADJOIN_ERR_INIT_KRB_CTX) { 2330 if (rc != ADJOIN_ERR_GET_KRB_PRINC) 2331 krb5_free_principal(ctx, krb5princ); 2332 smb_krb5_ctx_fini(ctx); 2333 } 2334 2335 ads_close(ah); 2336 free(princ_r); 2337 2338 /* 2339 * Don't mask other failure. Only reports SMF refresh 2340 * failure if no other domain join failure. 2341 */ 2342 if ((smb_config_refresh() != 0) && (rc == ADJOIN_SUCCESS)) 2343 rc = ADJOIN_ERR_SMB_REFRESH; 2344 2345 return (rc); 2346 } 2347 2348 /* 2349 * adjoin_report_err 2350 * 2351 * Display error message for the specific adjoin error code. 2352 */ 2353 char * 2354 adjoin_report_err(adjoin_status_t status) 2355 { 2356 if (status < 0 || status >= ADJOIN_NUM_STATUS) 2357 return ("ADJOIN: unknown status"); 2358 2359 return (adjoin_errmsg[status]); 2360 } 2361