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