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