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 1777 return (0); 1778 } 1779 1780 /* 1781 * ads_add_computer 1782 * 1783 * Returns 0 upon success. Otherwise, returns -1. 1784 */ 1785 static int 1786 ads_add_computer(ADS_HANDLE *ah) 1787 { 1788 return (ads_computer_op(ah, LDAP_MOD_ADD)); 1789 } 1790 1791 /* 1792 * ads_modify_computer 1793 * 1794 * Returns 0 upon success. Otherwise, returns -1. 1795 */ 1796 static int 1797 ads_modify_computer(ADS_HANDLE *ah) 1798 { 1799 return (ads_computer_op(ah, LDAP_MOD_REPLACE)); 1800 } 1801 1802 static int 1803 ads_computer_op(ADS_HANDLE *ah, int op) 1804 { 1805 LDAPMod *attrs[ADS_COMPUTER_NUM_ATTR]; 1806 char *oc_vals[6], *sam_val[2], *usr_val[2]; 1807 char *svc_val[2], *ctl_val[2], *fqh_val[2]; 1808 int j = 0; 1809 int ret, usrctl_flags = 0; 1810 char sam_acct[MAXHOSTNAMELEN + 1]; 1811 char fqhost[MAXHOSTNAMELEN]; 1812 char dn[ADS_DN_MAX]; 1813 char *user_principal, *svc_principal; 1814 char usrctl_buf[16]; 1815 int max; 1816 1817 if (smb_gethostname(fqhost, MAXHOSTNAMELEN, 0) != 0) 1818 return (-1); 1819 1820 (void) strlcpy(sam_acct, fqhost, MAXHOSTNAMELEN + 1); 1821 (void) strlcat(sam_acct, "$", MAXHOSTNAMELEN + 1); 1822 (void) snprintf(fqhost, MAXHOSTNAMELEN, "%s.%s", fqhost, 1823 ah->domain); 1824 1825 if (ads_get_host_principals(fqhost, ah->domain, &svc_principal, 1826 &user_principal) == -1) { 1827 syslog(LOG_ERR, 1828 "ads_computer_op: unable to get host principal"); 1829 return (-1); 1830 } 1831 1832 ads_get_computer_dn(ah, dn, ADS_DN_MAX); 1833 1834 max = (ADS_COMPUTER_NUM_ATTR - ((op != LDAP_MOD_ADD) ? 1 : 0)); 1835 for (j = 0; j < (max - 1); j++) { 1836 attrs[j] = (LDAPMod *)malloc(sizeof (LDAPMod)); 1837 if (attrs[j] == NULL) { 1838 free_attr(attrs); 1839 free(user_principal); 1840 free(svc_principal); 1841 return (-1); 1842 } 1843 } 1844 1845 j = -1; 1846 /* objectClass attribute is not modifiable. */ 1847 if (op == LDAP_MOD_ADD) { 1848 attrs[++j]->mod_op = op; 1849 attrs[j]->mod_type = "objectClass"; 1850 oc_vals[0] = "top"; 1851 oc_vals[1] = "person"; 1852 oc_vals[2] = "organizationalPerson"; 1853 oc_vals[3] = "user"; 1854 oc_vals[4] = "computer"; 1855 oc_vals[5] = 0; 1856 attrs[j]->mod_values = oc_vals; 1857 } 1858 1859 attrs[++j]->mod_op = op; 1860 attrs[j]->mod_type = "sAMAccountName"; 1861 sam_val[0] = sam_acct; 1862 sam_val[1] = 0; 1863 attrs[j]->mod_values = sam_val; 1864 1865 attrs[++j]->mod_op = op; 1866 attrs[j]->mod_type = "userPrincipalName"; 1867 usr_val[0] = user_principal; 1868 usr_val[1] = 0; 1869 attrs[j]->mod_values = usr_val; 1870 1871 attrs[++j]->mod_op = op; 1872 attrs[j]->mod_type = "servicePrincipalName"; 1873 svc_val[0] = svc_principal; 1874 svc_val[1] = 0; 1875 attrs[j]->mod_values = svc_val; 1876 1877 attrs[++j]->mod_op = op; 1878 attrs[j]->mod_type = "userAccountControl"; 1879 usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 1880 ADS_USER_ACCT_CTL_PASSWD_NOTREQD | 1881 ADS_USER_ACCT_CTL_ACCOUNTDISABLE); 1882 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 1883 ctl_val[0] = usrctl_buf; 1884 ctl_val[1] = 0; 1885 attrs[j]->mod_values = ctl_val; 1886 1887 attrs[++j]->mod_op = op; 1888 attrs[j]->mod_type = "dNSHostName"; 1889 fqh_val[0] = fqhost; 1890 fqh_val[1] = 0; 1891 attrs[j]->mod_values = fqh_val; 1892 1893 attrs[++j] = 0; 1894 1895 switch (op) { 1896 case LDAP_MOD_ADD: 1897 if ((ret = ldap_add_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1898 syslog(LOG_ERR, "ads_add_computer: %s", 1899 ldap_err2string(ret)); 1900 ret = -1; 1901 } 1902 break; 1903 1904 case LDAP_MOD_REPLACE: 1905 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1906 syslog(LOG_ERR, "ads_modify_computer: %s", 1907 ldap_err2string(ret)); 1908 ret = -1; 1909 } 1910 break; 1911 1912 default: 1913 ret = -1; 1914 1915 } 1916 1917 free_attr(attrs); 1918 free(user_principal); 1919 free(svc_principal); 1920 1921 return (ret); 1922 } 1923 1924 /* 1925 * Delete an ADS computer account. 1926 */ 1927 static void 1928 ads_del_computer(ADS_HANDLE *ah) 1929 { 1930 char dn[ADS_DN_MAX]; 1931 int rc; 1932 1933 ads_get_computer_dn(ah, dn, ADS_DN_MAX); 1934 1935 if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) { 1936 syslog(LOG_DEBUG, "ads_del_computer: %s", 1937 ldap_err2string(rc)); 1938 } 1939 } 1940 1941 /* 1942 * ads_lookup_computer_n_attr 1943 * 1944 * Lookup the value of the specified attribute on the computer 1945 * object. If the specified attribute can be found, its value is returned 1946 * via 'val' parameter. 1947 * 1948 * 'attr' parameter can be set to NULL if you only attempt to 1949 * see whether the computer object exists on AD or not. 1950 * 1951 * Return: 1952 * 1 if both the computer and the specified attribute is found. 1953 * 0 if either the computer or the specified attribute is not found. 1954 * -1 on error. 1955 */ 1956 static int 1957 ads_lookup_computer_n_attr(ADS_HANDLE *ah, char *attr, char **val) 1958 { 1959 char *attrs[2], filter[ADS_MAXBUFLEN]; 1960 LDAPMessage *res, *entry; 1961 char **vals; 1962 char tmpbuf[ADS_MAXBUFLEN]; 1963 char my_hostname[MAXHOSTNAMELEN], sam_acct[MAXHOSTNAMELEN + 1]; 1964 char dn[ADS_DN_MAX]; 1965 1966 if (smb_gethostname(my_hostname, MAXHOSTNAMELEN, 0) != 0) 1967 return (-1); 1968 1969 (void) snprintf(sam_acct, sizeof (sam_acct), "%s$", my_hostname); 1970 ads_get_computer_dn(ah, dn, ADS_DN_MAX); 1971 1972 res = NULL; 1973 attrs[0] = attr; 1974 attrs[1] = NULL; 1975 1976 if (ads_escape_search_filter_chars(sam_acct, tmpbuf) != 0) { 1977 return (-1); 1978 } 1979 1980 (void) snprintf(filter, sizeof (filter), "objectClass=computer", 1981 tmpbuf); 1982 1983 if (ldap_search_s(ah->ld, dn, LDAP_SCOPE_BASE, filter, attrs, 0, 1984 &res) != LDAP_SUCCESS) { 1985 (void) ldap_msgfree(res); 1986 return (0); 1987 } 1988 1989 if (attr) { 1990 /* no match for the specified attribute is found */ 1991 if (ldap_count_entries(ah->ld, res) == 0) { 1992 if (val) 1993 *val = NULL; 1994 1995 (void) ldap_msgfree(res); 1996 return (0); 1997 } 1998 1999 entry = ldap_first_entry(ah->ld, res); 2000 if (entry) { 2001 vals = ldap_get_values(ah->ld, entry, attr); 2002 if (!vals && val) { 2003 *val = NULL; 2004 (void) ldap_msgfree(res); 2005 return (0); 2006 } 2007 2008 if (vals[0] != NULL && val) 2009 *val = strdup(vals[0]); 2010 } 2011 } 2012 2013 /* free the search results */ 2014 (void) ldap_msgfree(res); 2015 return (1); 2016 } 2017 2018 /* 2019 * ads_find_computer 2020 * 2021 * Return: 2022 * 1 if found. 2023 * 0 if not found or encounters error. 2024 */ 2025 static int 2026 ads_find_computer(ADS_HANDLE *ah) 2027 { 2028 return (ads_lookup_computer_n_attr(ah, NULL, NULL) == 1); 2029 } 2030 2031 /* 2032 * ads_update_computer_cntrl_attr 2033 * 2034 * Modify the user account control attribute of an existing computer 2035 * object on AD. 2036 * 2037 * Returns 0 on success. Otherwise, returns -1. 2038 */ 2039 static int 2040 ads_update_computer_cntrl_attr(ADS_HANDLE *ah, int des_only) 2041 { 2042 LDAPMod *attrs[6]; 2043 char *ctl_val[2]; 2044 int j = -1; 2045 int ret, usrctl_flags = 0; 2046 char dn[ADS_DN_MAX]; 2047 char usrctl_buf[16]; 2048 2049 ads_get_computer_dn(ah, dn, ADS_DN_MAX); 2050 2051 attrs[++j] = (LDAPMod *) malloc(sizeof (LDAPMod)); 2052 attrs[j]->mod_op = LDAP_MOD_REPLACE; 2053 attrs[j]->mod_type = "userAccountControl"; 2054 2055 usrctl_flags |= (ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 2056 ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | 2057 ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); 2058 2059 if (des_only) 2060 usrctl_flags |= ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY; 2061 2062 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 2063 ctl_val[0] = usrctl_buf; 2064 ctl_val[1] = 0; 2065 attrs[j]->mod_values = ctl_val; 2066 2067 attrs[++j] = 0; 2068 2069 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2070 syslog(LOG_ERR, "ads_modify_computer: %s", 2071 ldap_err2string(ret)); 2072 ret = -1; 2073 } 2074 2075 free_attr(attrs); 2076 return (ret); 2077 } 2078 2079 /* 2080 * ads_lookup_computer_attr_kvno 2081 * 2082 * Lookup the value of the Kerberos version number attribute of the computer 2083 * account. 2084 */ 2085 static krb5_kvno 2086 ads_lookup_computer_attr_kvno(ADS_HANDLE *ah) 2087 { 2088 char *val = NULL; 2089 int kvno = 1; 2090 2091 if (ads_lookup_computer_n_attr(ah, "msDS-KeyVersionNumber", 2092 &val) == 1) { 2093 if (val) { 2094 kvno = atoi(val); 2095 free(val); 2096 } 2097 } 2098 2099 return (kvno); 2100 } 2101 2102 /* 2103 * ads_gen_machine_passwd 2104 * 2105 * Returned a null-terminated machine password generated randomly 2106 * from [0-9a-zA-Z] character set. In order to pass the password 2107 * quality check (three character classes), an uppercase letter is 2108 * used as the first character of the machine password. 2109 */ 2110 static int 2111 ads_gen_machine_passwd(char *machine_passwd, int bufsz) 2112 { 2113 char *data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK" 2114 "LMNOPQRSTUVWXYZ"; 2115 int datalen = strlen(data); 2116 int i, data_idx; 2117 2118 if (!machine_passwd || bufsz == 0) 2119 return (-1); 2120 2121 /* 2122 * The decimal value of upper case 'A' is 65. Randomly pick 2123 * an upper-case letter from the ascii table. 2124 */ 2125 machine_passwd[0] = (random() % 26) + 65; 2126 for (i = 1; i < bufsz - 1; i++) { 2127 data_idx = random() % datalen; 2128 machine_passwd[i] = data[data_idx]; 2129 } 2130 2131 machine_passwd[bufsz - 1] = 0; 2132 return (0); 2133 } 2134 2135 /* 2136 * ads_domain_change_cleanup 2137 * 2138 * If we're attempting to join the system to a new domain, the keys for 2139 * the host principal regarding the old domain should be removed from 2140 * Kerberos keytab. Also, the ads_host_info cache should be cleared. 2141 * 2142 * newdom is fully-qualified domain name. It can be set to empty string 2143 * if user attempts to switch to workgroup mode. 2144 */ 2145 int 2146 ads_domain_change_cleanup(char *newdom) 2147 { 2148 char origdom[MAXHOSTNAMELEN]; 2149 char *princ_r; 2150 krb5_context ctx = NULL; 2151 krb5_principal krb5princ; 2152 int rc; 2153 2154 if (smb_getfqdomainname(origdom, MAXHOSTNAMELEN)) 2155 return (0); 2156 2157 if (strcasecmp(origdom, newdom) == 0) 2158 return (0); 2159 2160 ads_free_host_info(); 2161 if (ads_get_host_principals(NULL, origdom, NULL, &princ_r) == -1) 2162 return (-1); 2163 2164 if (smb_krb5_ctx_init(&ctx) != 0) { 2165 free(princ_r); 2166 return (-1); 2167 } 2168 2169 if (smb_krb5_get_principal(ctx, princ_r, &krb5princ) != 0) { 2170 free(princ_r); 2171 smb_krb5_ctx_fini(ctx); 2172 return (-1); 2173 2174 } 2175 2176 rc = smb_krb5_remove_keytab_entries(ctx, krb5princ, SMBNS_KRB5_KEYTAB); 2177 free(princ_r); 2178 smb_krb5_ctx_fini(ctx); 2179 2180 return (rc); 2181 } 2182 2183 /* 2184 * ads_join 2185 * 2186 * Besides the NT-4 style domain join (using MS-RPC), CIFS server also 2187 * provides the domain join using Kerberos Authentication, Keberos 2188 * Change & Set password, and LDAP protocols. Basically, AD join 2189 * operation would require the following tickets to be acquired for the 2190 * the user account that is provided for the domain join. 2191 * 2192 * 1) a Keberos TGT ticket, 2193 * 2) a ldap service ticket, and 2194 * 3) kadmin/changpw service ticket 2195 * 2196 * The ADS client first sends a ldap search request to find out whether 2197 * or not the workstation trust account already exists in the Active Directory. 2198 * The existing computer object for this workstation will be removed and 2199 * a new one will be added. The machine account password is randomly 2200 * generated and set for the newly created computer object using KPASSD 2201 * protocol (See RFC 3244). Once the password is set, our ADS client 2202 * finalizes the machine account by modifying the user acount control 2203 * attribute of the computer object. Kerberos keys derived from the machine 2204 * account password will be stored locally in /etc/krb5/krb5.keytab file. 2205 * That would be needed while acquiring Kerberos TGT ticket for the host 2206 * principal after the domain join operation. 2207 */ 2208 adjoin_status_t 2209 ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd, 2210 int len) 2211 { 2212 ADS_HANDLE *ah = NULL; 2213 krb5_context ctx = NULL; 2214 krb5_principal krb5princ; 2215 krb5_kvno kvno; 2216 char *princ_r; 2217 boolean_t des_only, delete = B_TRUE, fini_krbctx = B_TRUE; 2218 adjoin_status_t rc = ADJOIN_SUCCESS; 2219 boolean_t new_acct; 2220 /* 2221 * Call library functions that can be used to get 2222 * the list of encryption algorithms available on the system. 2223 * (similar to what 'encrypt -l' CLI does). For now, 2224 * unless someone has modified the configuration of the 2225 * cryptographic framework (very unlikely), the following is the 2226 * list of algorithms available on any system running Nevada 2227 * by default. 2228 */ 2229 krb5_enctype enctypes[] = {ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 2230 ENCTYPE_ARCFOUR_HMAC, ENCTYPE_AES128_CTS_HMAC_SHA1_96}; 2231 2232 if ((ah = ads_open_main(domain, user, usr_passwd)) == NULL) { 2233 (void) smb_config_refresh(); 2234 return (ADJOIN_ERR_GET_HANDLE); 2235 } 2236 2237 if (ads_gen_machine_passwd(machine_passwd, len) != 0) { 2238 ads_close(ah); 2239 (void) smb_config_refresh(); 2240 return (ADJOIN_ERR_GEN_PASSWD); 2241 } 2242 2243 if (ads_find_computer(ah)) { 2244 new_acct = B_FALSE; 2245 if (ads_modify_computer(ah) != 0) { 2246 ads_close(ah); 2247 (void) smb_config_refresh(); 2248 return (ADJOIN_ERR_MOD_TRUST_ACCT); 2249 } 2250 } else { 2251 new_acct = B_TRUE; 2252 if (ads_add_computer(ah) != 0) { 2253 ads_close(ah); 2254 (void) smb_config_refresh(); 2255 return (ADJOIN_ERR_ADD_TRUST_ACCT); 2256 } 2257 } 2258 2259 des_only = B_FALSE; 2260 2261 /* 2262 * If we are talking to a Longhorn server, we need to set up 2263 * the msDS-SupportedEncryptionTypes attribute of the computer 2264 * object accordingly 2265 * 2266 * The code to modify the msDS-SupportedEncryptionTypes can be 2267 * added once we figure out why the Longhorn server rejects the 2268 * SmbSessionSetup request sent by SMB redirector. 2269 */ 2270 2271 if (ads_get_host_principals(NULL, ah->domain, NULL, &princ_r) == -1) { 2272 if (new_acct) 2273 ads_del_computer(ah); 2274 ads_close(ah); 2275 (void) smb_config_refresh(); 2276 return (ADJOIN_ERR_GET_HOST_PRINC); 2277 } 2278 2279 if (smb_krb5_ctx_init(&ctx) != 0) { 2280 fini_krbctx = B_FALSE; 2281 rc = ADJOIN_ERR_INIT_KRB_CTX; 2282 goto adjoin_cleanup; 2283 } 2284 2285 if (smb_krb5_get_principal(ctx, princ_r, &krb5princ) != 0) { 2286 rc = ADJOIN_ERR_GET_KRB_PRINC; 2287 goto adjoin_cleanup; 2288 } 2289 2290 if (smb_krb5_setpwd(ctx, krb5princ, machine_passwd) != 0) { 2291 rc = ADJOIN_ERR_KSETPWD; 2292 goto adjoin_cleanup; 2293 } 2294 2295 kvno = ads_lookup_computer_attr_kvno(ah); 2296 2297 if (ads_update_computer_cntrl_attr(ah, des_only) != 0) { 2298 rc = ADJOIN_ERR_UPDATE_CNTRL_ATTR; 2299 goto adjoin_cleanup; 2300 } 2301 2302 if (smb_krb5_update_keytab_entries(ctx, krb5princ, SMBNS_KRB5_KEYTAB, 2303 kvno, machine_passwd, enctypes, 2304 (sizeof (enctypes) / sizeof (krb5_enctype))) != 0) { 2305 rc = ADJOIN_ERR_WRITE_KEYTAB; 2306 goto adjoin_cleanup; 2307 } 2308 2309 /* Set IDMAP config */ 2310 if (smb_config_set_idmap_domain(ah->domain) != 0) { 2311 rc = ADJOIN_ERR_IDMAP_SET_DOMAIN; 2312 goto adjoin_cleanup; 2313 } 2314 2315 /* Refresh IDMAP service */ 2316 if (smb_config_refresh_idmap() != 0) { 2317 rc = ADJOIN_ERR_IDMAP_REFRESH; 2318 goto adjoin_cleanup; 2319 } 2320 2321 delete = B_FALSE; 2322 adjoin_cleanup: 2323 if (new_acct && delete) 2324 ads_del_computer(ah); 2325 2326 if (fini_krbctx) 2327 smb_krb5_ctx_fini(ctx); 2328 2329 ads_close(ah); 2330 free(princ_r); 2331 2332 /* 2333 * Don't mask other failure. Only reports SMF refresh 2334 * failure if no other domain join failure. 2335 */ 2336 if ((smb_config_refresh() != 0) && (rc == ADJOIN_SUCCESS)) 2337 rc = ADJOIN_ERR_SMB_REFRESH; 2338 2339 return (rc); 2340 } 2341 2342 /* 2343 * adjoin_report_err 2344 * 2345 * Display error message for the specific adjoin error code. 2346 */ 2347 char * 2348 adjoin_report_err(adjoin_status_t status) 2349 { 2350 if (status < 0 || status >= ADJOIN_NUM_STATUS) 2351 return ("ADJOIN: unknown status"); 2352 2353 return (adjoin_errmsg[status]); 2354 } 2355