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