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