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