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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/param.h> 27 #include <ldap.h> 28 #include <stdlib.h> 29 #include <gssapi/gssapi.h> 30 #include <sys/types.h> 31 #include <sys/socket.h> 32 #include <netinet/in.h> 33 #include <arpa/inet.h> 34 #include <sys/time.h> 35 #include <netdb.h> 36 #include <pthread.h> 37 #include <unistd.h> 38 #include <arpa/nameser.h> 39 #include <resolv.h> 40 #include <sys/synch.h> 41 #include <string.h> 42 #include <strings.h> 43 #include <fcntl.h> 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 #include <assert.h> 47 48 #include <smbsrv/libsmbns.h> 49 #include <smbns_ads.h> 50 #include <smbns_dyndns.h> 51 #include <smbns_krb.h> 52 53 #define SMB_ADS_DN_MAX 300 54 #define SMB_ADS_MAXMSGLEN 512 55 #define SMB_ADS_COMPUTERS_CN "Computers" 56 #define SMB_ADS_COMPUTER_NUM_ATTR 8 57 #define SMB_ADS_SHARE_NUM_ATTR 3 58 #define SMB_ADS_SITE_MAX MAXHOSTNAMELEN 59 60 #define SMB_ADS_MSDCS_SRV_DC_RR "_ldap._tcp.dc._msdcs" 61 #define SMB_ADS_MSDCS_SRV_SITE_RR "_ldap._tcp.%s._sites.dc._msdcs" 62 63 /* 64 * domainControllerFunctionality 65 * 66 * This rootDSE attribute indicates the functional level of the DC. 67 */ 68 #define SMB_ADS_ATTR_DCLEVEL "domainControllerFunctionality" 69 #define SMB_ADS_DCLEVEL_W2K 0 70 #define SMB_ADS_DCLEVEL_W2K3 2 71 #define SMB_ADS_DCLEVEL_W2K8 3 72 73 /* 74 * msDs-supportedEncryptionTypes (Windows Server 2008 only) 75 * 76 * This attribute defines the encryption types supported by the system. 77 * Encryption Types: 78 * - DES cbc mode with CRC-32 79 * - DES cbc mode with RSA-MD5 80 * - ArcFour with HMAC/md5 81 * - AES-128 82 * - AES-256 83 */ 84 #define SMB_ADS_ATTR_ENCTYPES "msDs-supportedEncryptionTypes" 85 #define SMB_ADS_ENC_DES_CRC 1 86 #define SMB_ADS_ENC_DES_MD5 2 87 #define SMB_ADS_ENC_RC4 4 88 #define SMB_ADS_ENC_AES128 8 89 #define SMB_ADS_ENC_AES256 16 90 91 #define SMB_ADS_ATTR_SAMACCT "sAMAccountName" 92 #define SMB_ADS_ATTR_UPN "userPrincipalName" 93 #define SMB_ADS_ATTR_SPN "servicePrincipalName" 94 #define SMB_ADS_ATTR_CTL "userAccountControl" 95 #define SMB_ADS_ATTR_DNSHOST "dNSHostName" 96 #define SMB_ADS_ATTR_KVNO "msDS-KeyVersionNumber" 97 #define SMB_ADS_ATTR_DN "distinguishedName" 98 99 /* 100 * Length of "dc=" prefix. 101 */ 102 #define SMB_ADS_DN_PREFIX_LEN 3 103 #define SMB_ADS_MSDCS_SVC_CNT 2 104 105 static char *smb_ads_computer_objcls[] = { 106 "top", "person", "organizationalPerson", 107 "user", "computer", NULL 108 }; 109 110 static char *smb_ads_share_objcls[] = { 111 "top", "leaf", "connectionPoint", "volume", NULL 112 }; 113 114 /* Cached ADS server to communicate with */ 115 static smb_ads_host_info_t *smb_ads_cached_host_info = NULL; 116 static mutex_t smb_ads_cached_host_mtx; 117 118 static char smb_ads_site[SMB_ADS_SITE_MAX]; 119 static mutex_t smb_ads_site_mtx; 120 121 /* 122 * smb_ads_adjoin_errmsg 123 * 124 * Use the adjoin return status defined in adjoin_status_t as the index 125 * to this table. 126 */ 127 static char *smb_ads_adjoin_errmsg[] = { 128 "ADJOIN succeeded.", 129 "ADJOIN failed to get handle.", 130 "ADJOIN failed to generate machine password.", 131 "ADJOIN failed to add workstation trust account.", 132 "ADJOIN failed to modify workstation trust account.", 133 "ADJOIN failed to get list of encryption types.", 134 "ADJOIN failed to initialize kerberos context.", 135 "ADJOIN failed to get Kerberos principal.", 136 "ADJOIN failed to set machine account password on AD.", 137 "ADJOIN failed to modify CONTROL attribute of the account.", 138 "ADJOIN failed to write Kerberos keytab file.", 139 "ADJOIN failed to configure domain_name property for idmapd.", 140 "ADJOIN failed to refresh idmap service." 141 }; 142 143 /* attribute/value pair */ 144 typedef struct smb_ads_avpair { 145 char *avp_attr; 146 char *avp_val; 147 } smb_ads_avpair_t; 148 149 /* query status */ 150 typedef enum smb_ads_qstat { 151 SMB_ADS_STAT_ERR = -2, 152 SMB_ADS_STAT_DUP, 153 SMB_ADS_STAT_NOT_FOUND, 154 SMB_ADS_STAT_FOUND 155 } smb_ads_qstat_t; 156 157 static smb_ads_handle_t *smb_ads_open_main(char *, char *, char *); 158 static int smb_ads_bind(smb_ads_handle_t *); 159 static int smb_ads_add_computer(smb_ads_handle_t *, int, char *); 160 static int smb_ads_modify_computer(smb_ads_handle_t *, int, char *); 161 static int smb_ads_computer_op(smb_ads_handle_t *, int, int, char *); 162 static smb_ads_qstat_t smb_ads_lookup_computer_n_attr(smb_ads_handle_t *, 163 smb_ads_avpair_t *, int, char *); 164 static int smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *, int, char *); 165 static krb5_kvno smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *, char *); 166 static int smb_ads_gen_machine_passwd(char *, int); 167 static smb_ads_host_info_t *smb_ads_get_cached_host(void); 168 static void smb_ads_set_cached_host(smb_ads_host_info_t *); 169 static void smb_ads_free_cached_host(void); 170 static int smb_ads_get_spnset(char *, char **); 171 static void smb_ads_free_spnset(char **); 172 static int smb_ads_alloc_attr(LDAPMod **, int); 173 static void smb_ads_free_attr(LDAPMod **); 174 static int smb_ads_get_dc_level(smb_ads_handle_t *); 175 static smb_ads_host_info_t *smb_ads_select_dc(smb_ads_host_list_t *); 176 static smb_ads_qstat_t smb_ads_find_computer(smb_ads_handle_t *, char *); 177 static smb_ads_qstat_t smb_ads_getattr(LDAP *, LDAPMessage *, 178 smb_ads_avpair_t *); 179 static smb_ads_qstat_t smb_ads_get_qstat(smb_ads_handle_t *, LDAPMessage *, 180 smb_ads_avpair_t *); 181 182 /* 183 * smb_ads_init 184 * 185 * Initializes the smb_ads_site global variable. 186 */ 187 void 188 smb_ads_init(void) 189 { 190 (void) mutex_lock(&smb_ads_site_mtx); 191 (void) smb_config_getstr(SMB_CI_ADS_SITE, 192 smb_ads_site, sizeof (smb_ads_site)); 193 (void) mutex_unlock(&smb_ads_site_mtx); 194 } 195 196 /* 197 * smb_ads_refresh 198 * 199 * If the smb_ads_site has changed, clear the smb_ads_cached_host_info cache. 200 */ 201 void 202 smb_ads_refresh(void) 203 { 204 char new_site[SMB_ADS_SITE_MAX]; 205 206 (void) smb_config_getstr(SMB_CI_ADS_SITE, new_site, sizeof (new_site)); 207 (void) mutex_lock(&smb_ads_site_mtx); 208 if (utf8_strcasecmp(smb_ads_site, new_site)) { 209 (void) strlcpy(smb_ads_site, new_site, sizeof (smb_ads_site)); 210 smb_ads_free_cached_host(); 211 } 212 (void) mutex_unlock(&smb_ads_site_mtx); 213 } 214 215 /* 216 * smb_ads_build_unc_name 217 * 218 * Construct the UNC name of the share object in the format of 219 * \\hostname.domain\shareUNC 220 * 221 * Returns 0 on success, -1 on error. 222 */ 223 int 224 smb_ads_build_unc_name(char *unc_name, int maxlen, 225 const char *hostname, const char *shareUNC) 226 { 227 char my_domain[MAXHOSTNAMELEN]; 228 229 if (smb_getfqdomainname(my_domain, sizeof (my_domain)) != 0) 230 return (-1); 231 232 (void) snprintf(unc_name, maxlen, "\\\\%s.%s\\%s", 233 hostname, my_domain, shareUNC); 234 return (0); 235 } 236 237 /* 238 * smb_ads_ldap_ping 239 * 240 * This is used to bind to an ADS server to see 241 * if it is still alive. 242 * 243 * Returns: 244 * -1: error 245 * 0: successful 246 */ 247 /*ARGSUSED*/ 248 static int 249 smb_ads_ldap_ping(smb_ads_host_info_t *ads_host) 250 { 251 int ldversion = LDAP_VERSION3, status, timeoutms = 5 * 1000; 252 LDAP *ld = NULL; 253 254 ld = ldap_init(ads_host->name, ads_host->port); 255 if (ld == NULL) 256 return (-1); 257 258 ldversion = LDAP_VERSION3; 259 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion); 260 /* setup TCP/IP connect timeout */ 261 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms); 262 263 status = ldap_bind_s(ld, "", NULL, LDAP_AUTH_SIMPLE); 264 265 if (status != LDAP_SUCCESS) { 266 (void) ldap_unbind(ld); 267 return (-1); 268 } 269 270 (void) ldap_unbind(ld); 271 272 return (0); 273 } 274 275 /* 276 * smb_ads_set_cached_host 277 * 278 * Cache the result of the ADS discovery if the cache is empty. 279 */ 280 static void 281 smb_ads_set_cached_host(smb_ads_host_info_t *host) 282 { 283 (void) mutex_lock(&smb_ads_cached_host_mtx); 284 if (!smb_ads_cached_host_info) 285 smb_ads_cached_host_info = host; 286 (void) mutex_unlock(&smb_ads_cached_host_mtx); 287 } 288 289 /* 290 * smb_ads_get_cached_host 291 * 292 * Get the cached ADS host info. 293 */ 294 static smb_ads_host_info_t * 295 smb_ads_get_cached_host(void) 296 { 297 smb_ads_host_info_t *host; 298 299 (void) mutex_lock(&smb_ads_cached_host_mtx); 300 host = smb_ads_cached_host_info; 301 (void) mutex_unlock(&smb_ads_cached_host_mtx); 302 return (host); 303 } 304 305 /* 306 * smb_ads_is_sought_host 307 * 308 * Returns true, if the sought host name matches the input host (host) name. 309 * The sought host is expected to be in Fully Qualified Domain Name (FQDN) 310 * format. 311 */ 312 static boolean_t 313 smb_ads_is_sought_host(smb_ads_host_info_t *host, char *sought_host_name) 314 { 315 if ((host == NULL) || (sought_host_name == NULL)) 316 return (B_FALSE); 317 318 if (utf8_strcasecmp(host->name, sought_host_name)) 319 return (B_FALSE); 320 321 return (B_TRUE); 322 } 323 324 /* 325 * smb_ads_match_hosts_same_domain 326 * 327 * Returns true, if the cached ADS host is in the same domain as the 328 * current (given) domain. 329 */ 330 static boolean_t 331 smb_ads_is_same_domain(char *cached_host_name, char *current_domain) 332 { 333 char *cached_host_domain; 334 335 if ((cached_host_name == NULL) || (current_domain == NULL)) 336 return (B_FALSE); 337 338 cached_host_domain = strchr(cached_host_name, '.'); 339 if (cached_host_domain == NULL) 340 return (B_FALSE); 341 342 ++cached_host_domain; 343 if (utf8_strcasecmp(cached_host_domain, current_domain)) 344 return (B_FALSE); 345 346 return (B_TRUE); 347 } 348 349 /* 350 * smb_ads_skip_ques_sec 351 * Skips the question section. 352 */ 353 static int 354 smb_ads_skip_ques_sec(int qcnt, uchar_t **ptr, uchar_t *eom) 355 { 356 int i, len; 357 358 for (i = 0; i < qcnt; i++) { 359 if ((len = dn_skipname(*ptr, eom)) < 0) 360 return (-1); 361 362 *ptr += len + QFIXEDSZ; 363 } 364 365 return (0); 366 } 367 368 /* 369 * smb_ads_decode_host_ans_sec 370 * Decodes ADS hosts, priority, weight and port number from the answer 371 * section based on the current buffer pointer. 372 */ 373 static int 374 smb_ads_decode_host_ans_sec(int ans_cnt, uchar_t **ptr, uchar_t *eom, 375 uchar_t *buf, smb_ads_host_info_t *ads_host_list) 376 { 377 int i, len; 378 smb_ads_host_info_t *ads_host; 379 380 for (i = 0; i < ans_cnt; i++) { 381 ads_host = &ads_host_list[i]; 382 383 if ((len = dn_skipname(*ptr, eom)) < 0) 384 return (-1); 385 386 387 *ptr += len; 388 389 /* skip type, class, ttl */ 390 *ptr += 8; 391 /* data size */ 392 *ptr += 2; 393 394 /* Get priority, weight */ 395 /* LINTED: E_CONSTANT_CONDITION */ 396 NS_GET16(ads_host->priority, *ptr); 397 /* LINTED: E_CONSTANT_CONDITION */ 398 NS_GET16(ads_host->weight, *ptr); 399 400 /* port */ 401 /* LINTED: E_CONSTANT_CONDITION */ 402 NS_GET16(ads_host->port, *ptr); 403 /* domain name */ 404 len = dn_expand(buf, eom, *ptr, ads_host->name, MAXHOSTNAMELEN); 405 if (len < 0) 406 return (-1); 407 408 *ptr += len; 409 } 410 411 return (0); 412 } 413 414 /* 415 * smb_ads_skip_auth_sec 416 * Skips the authority section. 417 */ 418 static int 419 smb_ads_skip_auth_sec(int ns_cnt, uchar_t **ptr, uchar_t *eom) 420 { 421 int i, len; 422 uint16_t size; 423 424 for (i = 0; i < ns_cnt; i++) { 425 if ((len = dn_skipname(*ptr, eom)) < 0) 426 return (-1); 427 428 *ptr += len; 429 /* skip type, class, ttl */ 430 *ptr += 8; 431 /* get len of data */ 432 /* LINTED: E_CONSTANT_CONDITION */ 433 NS_GET16(size, *ptr); 434 if ((*ptr + size) > eom) 435 return (-1); 436 437 *ptr += size; 438 } 439 440 return (0); 441 } 442 443 /* 444 * smb_ads_decode_host_ip 445 * 446 * Decodes ADS hosts and IP Addresses from the additional section based 447 * on the current buffer pointer. 448 */ 449 static int 450 smb_ads_decode_host_ip(int addit_cnt, int ans_cnt, uchar_t **ptr, 451 uchar_t *eom, uchar_t *buf, smb_ads_host_info_t *ads_host_list) 452 { 453 int i, j, len; 454 smb_inaddr_t ipaddr; 455 char hostname[MAXHOSTNAMELEN]; 456 char *name; 457 uint16_t size = 0; 458 459 for (i = 0; i < addit_cnt; i++) { 460 461 /* domain name */ 462 len = dn_expand(buf, eom, *ptr, hostname, MAXHOSTNAMELEN); 463 if (len < 0) 464 return (-1); 465 466 *ptr += len; 467 468 /* skip type, class, TTL, data len */ 469 *ptr += 8; 470 /* LINTED: E_CONSTANT_CONDITION */ 471 NS_GET16(size, *ptr); 472 473 if (size == INADDRSZ) { 474 /* LINTED: E_CONSTANT_CONDITION */ 475 NS_GET32(ipaddr.a_ipv4, *ptr); 476 ipaddr.a_ipv4 = htonl(ipaddr.a_ipv4); 477 ipaddr.a_family = AF_INET; 478 } else if (size == IN6ADDRSZ) { 479 #ifdef BIG_ENDIAN 480 bcopy(*ptr, &ipaddr.a_ipv6, IN6ADDRSZ); 481 #else 482 for (i = 0; i < IN6ADDRSZ; i++) 483 (uint8_t *)(ipaddr.a_ipv6) 484 [IN6ADDRSZ-1-i] = *(*ptr+i); 485 #endif 486 ipaddr.a_family = AF_INET6; 487 } 488 489 /* 490 * find the host in the list of DC records from 491 * the answer section, that matches the host in the 492 * additional section, and set its IP address. 493 */ 494 for (j = 0; j < ans_cnt; j++) { 495 if ((name = ads_host_list[j].name) == NULL) 496 continue; 497 if (utf8_strcasecmp(name, hostname) == 0) { 498 ads_host_list[j].ipaddr = ipaddr; 499 } 500 } 501 } 502 return (0); 503 } 504 505 /* 506 * smb_ads_dup_host_info 507 * 508 * Duplicates the passed smb_ads_host_info_t structure. 509 * Caller must free memory allocated by this method. 510 * 511 * Returns a reference to the duplicated smb_ads_host_info_t structure. 512 * Returns NULL on error. 513 */ 514 static smb_ads_host_info_t * 515 smb_ads_dup_host_info(smb_ads_host_info_t *ads_host) 516 { 517 smb_ads_host_info_t *dup_host; 518 519 if (ads_host == NULL) 520 return (NULL); 521 522 dup_host = malloc(sizeof (smb_ads_host_info_t)); 523 524 if (dup_host != NULL) 525 bcopy(ads_host, dup_host, sizeof (smb_ads_host_info_t)); 526 527 return (dup_host); 528 } 529 530 /* 531 * smb_ads_hlist_alloc 532 */ 533 smb_ads_host_list_t * 534 smb_ads_hlist_alloc(int count) 535 { 536 int size; 537 smb_ads_host_list_t *hlist; 538 539 if (count == 0) 540 return (NULL); 541 542 size = sizeof (smb_ads_host_info_t) * count; 543 hlist = (smb_ads_host_list_t *)malloc(sizeof (smb_ads_host_list_t)); 544 if (hlist == NULL) 545 return (NULL); 546 547 hlist->ah_cnt = count; 548 hlist->ah_list = (smb_ads_host_info_t *)malloc(size); 549 if (hlist->ah_list == NULL) { 550 free(hlist); 551 return (NULL); 552 } 553 554 bzero(hlist->ah_list, size); 555 return (hlist); 556 } 557 558 /* 559 * smb_ads_hlist_free 560 */ 561 static void 562 smb_ads_hlist_free(smb_ads_host_list_t *host_list) 563 { 564 if (host_list == NULL) 565 return; 566 567 free(host_list->ah_list); 568 free(host_list); 569 } 570 571 /* 572 * smb_ads_query_dns_server 573 * 574 * This routine sends a DNS service location (SRV) query message to the 575 * DNS server via TCP to query it for a list of ADS server(s). Once a reply 576 * is received, the reply message is parsed to get the hostname. If there are IP 577 * addresses populated in the additional section then the additional section 578 * is parsed to obtain the IP addresses. 579 * 580 * The service location of _ldap._tcp.dc.msdcs.<ADS domain> is used to 581 * guarantee that Microsoft domain controllers are returned. Microsoft domain 582 * controllers are also ADS servers. 583 * 584 * The ADS hostnames are stored in the answer section of the DNS reply message. 585 * The IP addresses are stored in the additional section. 586 * 587 * The DNS reply message may be in compress formed. The compression is done 588 * on repeating domain name label in the message. i.e hostname. 589 * 590 * Upon successful completion, host list of ADS server(s) is returned. 591 */ 592 static smb_ads_host_list_t * 593 smb_ads_query_dns_server(char *domain, char *msdcs_svc_name) 594 { 595 smb_ads_host_list_t *hlist = NULL; 596 int len, qcnt, ans_cnt, ns_cnt, addit_cnt; 597 uchar_t *ptr, *eom; 598 struct __res_state res_state; 599 union { 600 HEADER hdr; 601 uchar_t buf[NS_MAXMSG]; 602 } msg; 603 604 bzero(&res_state, sizeof (struct __res_state)); 605 if (res_ninit(&res_state) < 0) 606 return (NULL); 607 608 /* use TCP */ 609 res_state.options |= RES_USEVC; 610 611 len = res_nquerydomain(&res_state, msdcs_svc_name, domain, 612 C_IN, T_SRV, msg.buf, sizeof (msg.buf)); 613 614 if (len < 0) { 615 smb_tracef("smbns_ads: DNS query for '%s' failed (%s)", 616 msdcs_svc_name, hstrerror(res_state.res_h_errno)); 617 res_ndestroy(&res_state); 618 return (NULL); 619 } 620 621 if (len > sizeof (msg.buf)) { 622 smb_tracef("smbns_ads: DNS query %s: message too big (%d)", 623 msdcs_svc_name, len); 624 res_ndestroy(&res_state); 625 return (NULL); 626 } 627 628 /* parse the reply, skip header and question sections */ 629 ptr = msg.buf + sizeof (msg.hdr); 630 eom = msg.buf + len; 631 632 /* check truncated message bit */ 633 if (msg.hdr.tc) 634 smb_tracef("smbns_ads: DNS query for '%s' detected " 635 "truncated TCP reply message", msdcs_svc_name); 636 637 qcnt = ntohs(msg.hdr.qdcount); 638 ans_cnt = ntohs(msg.hdr.ancount); 639 ns_cnt = ntohs(msg.hdr.nscount); 640 addit_cnt = ntohs(msg.hdr.arcount); 641 642 if (smb_ads_skip_ques_sec(qcnt, &ptr, eom) != 0) { 643 res_ndestroy(&res_state); 644 return (NULL); 645 } 646 647 hlist = smb_ads_hlist_alloc(ans_cnt); 648 if (hlist == NULL) { 649 res_ndestroy(&res_state); 650 return (NULL); 651 } 652 653 /* walk through the answer section */ 654 if (smb_ads_decode_host_ans_sec(ans_cnt, &ptr, eom, msg.buf, 655 hlist->ah_list) != 0) { 656 smb_ads_hlist_free(hlist); 657 res_ndestroy(&res_state); 658 return (NULL); 659 } 660 661 /* check authority section */ 662 if (ns_cnt > 0) { 663 if (smb_ads_skip_auth_sec(ns_cnt, &ptr, eom) != 0) { 664 smb_ads_hlist_free(hlist); 665 res_ndestroy(&res_state); 666 return (NULL); 667 } 668 } 669 670 /* 671 * Check additional section to get IP address of ADS host. 672 */ 673 if (addit_cnt > 0) { 674 if (smb_ads_decode_host_ip(addit_cnt, ans_cnt, 675 &ptr, eom, msg.buf, hlist->ah_list) != 0) { 676 smb_ads_hlist_free(hlist); 677 res_ndestroy(&res_state); 678 return (NULL); 679 } 680 } 681 682 res_ndestroy(&res_state); 683 return (hlist); 684 } 685 686 /* 687 * smb_ads_set_site_service 688 * 689 * This method sets the name of the site, to look for the ADS domain. 690 */ 691 static void 692 smb_ads_set_site_service(char *site_service) 693 { 694 (void) mutex_lock(&smb_ads_site_mtx); 695 if (*smb_ads_site == '\0') 696 *site_service = '\0'; 697 else 698 (void) snprintf(site_service, sizeof (site_service), 699 SMB_ADS_MSDCS_SRV_SITE_RR, smb_ads_site); 700 (void) mutex_unlock(&smb_ads_site_mtx); 701 } 702 703 /* 704 * smb_ads_getipnodebyname 705 * 706 * This method gets the IP address by doing a host name lookup. 707 */ 708 static int 709 smb_ads_getipnodebyname(smb_ads_host_info_t *hentry) 710 { 711 struct hostent *h; 712 int error; 713 714 switch (hentry->ipaddr.a_family) { 715 case AF_INET6: 716 h = getipnodebyname(hentry->name, hentry->ipaddr.a_family, 717 AI_DEFAULT, &error); 718 if (h == NULL || h->h_length != IPV6_ADDR_LEN) 719 return (-1); 720 break; 721 722 case AF_INET: 723 h = getipnodebyname(hentry->name, hentry->ipaddr.a_family, 724 0, &error); 725 if (h == NULL || h->h_length != INADDRSZ) 726 return (-1); 727 break; 728 729 default: 730 return (-1); 731 } 732 bcopy(*(h->h_addr_list), &hentry->ipaddr.a_ip, h->h_length); 733 freehostent(h); 734 return (0); 735 } 736 737 /* 738 * smb_ads_find_host 739 * 740 * Finds a ADS host in a given domain. 741 * 742 * Parameters: 743 * domain: domain of ADS host. 744 * sought: the ADS host to be sought. 745 * 746 * If the ADS host is cached and it responds to ldap ping, 747 * - Cached ADS host is returned, if sought host is not specified. 748 * OR 749 * - Cached ADS host is returned, if the sought host matches the 750 * cached ADS host AND the cached ADS host is in the same domain 751 * as the given domain. 752 * 753 * If the ADS host is not cached in the given domain, the ADS host 754 * is returned if it matches the sought host. 755 * 756 * Returns: 757 * ADS host: fully qualified hostname, ip address, ldap port. 758 */ 759 /*ARGSUSED*/ 760 smb_ads_host_info_t * 761 smb_ads_find_host(char *domain, char *sought) 762 { 763 int i; 764 smb_ads_host_list_t *hlist; 765 smb_ads_host_info_t *hlistp = NULL, *hentry = NULL, *host = NULL; 766 char site_service[MAXHOSTNAMELEN]; 767 char *msdcs_svc_name[SMB_ADS_MSDCS_SVC_CNT] = 768 {site_service, SMB_ADS_MSDCS_SRV_DC_RR}; 769 770 if ((sought) && (*sought == '\0')) 771 sought = NULL; 772 773 /* 774 * If the cached host responds to ldap ping, 775 * - return cached ADS host, if sought host is not specified OR 776 * - return cached ADS host, if the sought host matches the cached 777 * ADS host AND the cached ADS host is in the same domain as the 778 * given domain. 779 */ 780 host = smb_ads_get_cached_host(); 781 if (host) { 782 if (smb_ads_ldap_ping(host) == 0) { 783 if (!sought) 784 return (host); 785 786 if (smb_ads_is_same_domain(host->name, domain) && 787 smb_ads_is_sought_host(host, sought)) 788 return (host); 789 } 790 791 smb_ads_free_cached_host(); 792 } 793 794 smb_ads_set_site_service(site_service); 795 796 /* 797 * First look for ADS hosts in ADS site if configured. Then try 798 * without ADS site info. 799 */ 800 for (i = 0; i < SMB_ADS_MSDCS_SVC_CNT; i++) { 801 if (*msdcs_svc_name[i] == '\0') 802 continue; 803 804 hlist = smb_ads_query_dns_server(domain, msdcs_svc_name[i]); 805 if (hlist != NULL) 806 break; 807 } 808 809 if ((hlist == NULL) || (hlist->ah_list == NULL) || (hlist->ah_cnt == 0)) 810 return (NULL); 811 hlistp = hlist->ah_list; 812 813 for (i = 0; i < hlist->ah_cnt; i++) { 814 /* Do a host lookup by hostname to get the IP address */ 815 if (smb_inet_iszero(&hlistp[i].ipaddr)) { 816 if (smb_ads_getipnodebyname(&hlistp[i]) < 0) 817 continue; 818 } 819 820 /* If a dc is sought, return it here */ 821 if (smb_ads_is_sought_host(&hlistp[i], sought) && 822 (smb_ads_ldap_ping(&hlistp[i]) == 0)) { 823 host = smb_ads_dup_host_info(&hlistp[i]); 824 smb_ads_set_cached_host(host); 825 smb_ads_hlist_free(hlist); 826 return (host); 827 } 828 } 829 830 /* Select DC from DC list */ 831 hentry = smb_ads_select_dc(hlist); 832 if (hentry != NULL) { 833 host = smb_ads_dup_host_info(hentry); 834 smb_ads_set_cached_host(host); 835 smb_ads_hlist_free(hlist); 836 return (host); 837 } 838 839 smb_ads_hlist_free(hlist); 840 841 return (NULL); 842 } 843 844 /* 845 * Return the number of dots in a string. 846 */ 847 static int 848 smb_ads_count_dots(const char *s) 849 { 850 int ndots = 0; 851 852 while (*s) { 853 if (*s++ == '.') 854 ndots++; 855 } 856 857 return (ndots); 858 } 859 860 /* 861 * Convert a domain name in dot notation to distinguished name format, 862 * for example: sun.com -> dc=sun,dc=com. 863 * 864 * Returns a pointer to an allocated buffer containing the distinguished 865 * name. 866 */ 867 static char * 868 smb_ads_convert_domain(const char *domain_name) 869 { 870 const char *s; 871 char *dn_name; 872 char buf[2]; 873 int ndots; 874 int len; 875 876 if (domain_name == NULL || *domain_name == 0) 877 return (NULL); 878 879 ndots = smb_ads_count_dots(domain_name); 880 ++ndots; 881 len = strlen(domain_name) + (ndots * SMB_ADS_DN_PREFIX_LEN) + 1; 882 883 if ((dn_name = malloc(len)) == NULL) 884 return (NULL); 885 886 bzero(dn_name, len); 887 (void) strlcpy(dn_name, "dc=", len); 888 889 buf[1] = '\0'; 890 s = domain_name; 891 892 while (*s) { 893 if (*s == '.') { 894 (void) strlcat(dn_name, ",dc=", len); 895 } else { 896 buf[0] = *s; 897 (void) strlcat(dn_name, buf, len); 898 } 899 ++s; 900 } 901 902 return (dn_name); 903 } 904 905 /* 906 * smb_ads_free_cached_host 907 * 908 * Free the memory use by the global smb_ads_cached_host_info & set it to NULL. 909 */ 910 static void 911 smb_ads_free_cached_host(void) 912 { 913 (void) mutex_lock(&smb_ads_cached_host_mtx); 914 if (smb_ads_cached_host_info) { 915 free(smb_ads_cached_host_info); 916 smb_ads_cached_host_info = NULL; 917 } 918 (void) mutex_unlock(&smb_ads_cached_host_mtx); 919 } 920 921 /* 922 * smb_ads_open 923 * Open a LDAP connection to an ADS server if the system is in domain mode. 924 * Acquire both Kerberos TGT and LDAP service tickets for the host principal. 925 * 926 * This function should only be called after the system is successfully joined 927 * to a domain. 928 */ 929 smb_ads_handle_t * 930 smb_ads_open(void) 931 { 932 char domain[MAXHOSTNAMELEN]; 933 934 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) 935 return (NULL); 936 937 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) 938 return (NULL); 939 940 return (smb_ads_open_main(domain, NULL, NULL)); 941 } 942 943 /* 944 * smb_ads_open_main 945 * Open a LDAP connection to an ADS server. 946 * If ADS is enabled and the administrative username, password, and 947 * ADS domain are defined then query DNS to find an ADS server if this is the 948 * very first call to this routine. After an ADS server is found then this 949 * server will be used everytime this routine is called until the system is 950 * rebooted or the ADS server becomes unavailable then an ADS server will 951 * be queried again. After the connection is made then an ADS handle 952 * is created to be returned. 953 * 954 * After the LDAP connection, the LDAP version will be set to 3 using 955 * ldap_set_option(). 956 * 957 * The smb_ads_bind() routine is also called before the ADS handle is returned. 958 * Parameters: 959 * domain - fully-qualified domain name 960 * user - the user account for whom the Kerberos TGT ticket and ADS 961 * service tickets are acquired. 962 * password - password of the specified user 963 * 964 * Returns: 965 * NULL : can't connect to ADS server or other errors 966 * smb_ads_handle_t* : handle to ADS server 967 */ 968 static smb_ads_handle_t * 969 smb_ads_open_main(char *domain, char *user, char *password) 970 { 971 smb_ads_handle_t *ah; 972 LDAP *ld; 973 int version = 3; 974 smb_ads_host_info_t *ads_host = NULL; 975 976 ads_host = smb_ads_find_host(domain, NULL); 977 if (ads_host == NULL) 978 return (NULL); 979 980 ah = (smb_ads_handle_t *)malloc(sizeof (smb_ads_handle_t)); 981 if (ah == NULL) 982 return (NULL); 983 (void) memset(ah, 0, sizeof (smb_ads_handle_t)); 984 985 if ((ld = ldap_init(ads_host->name, ads_host->port)) == NULL) { 986 smb_ads_free_cached_host(); 987 free(ah); 988 return (NULL); 989 } 990 991 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version) 992 != LDAP_SUCCESS) { 993 smb_ads_free_cached_host(); 994 free(ah); 995 (void) ldap_unbind(ld); 996 return (NULL); 997 } 998 999 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 1000 ah->ld = ld; 1001 ah->user = (user) ? strdup(user) : NULL; 1002 ah->pwd = (password) ? strdup(password) : NULL; 1003 ah->domain = strdup(domain); 1004 1005 if (ah->domain == NULL) { 1006 smb_ads_close(ah); 1007 return (NULL); 1008 } 1009 1010 ah->domain_dn = smb_ads_convert_domain(domain); 1011 if (ah->domain_dn == NULL) { 1012 smb_ads_close(ah); 1013 return (NULL); 1014 } 1015 1016 ah->hostname = strdup(ads_host->name); 1017 if (ah->hostname == NULL) { 1018 smb_ads_close(ah); 1019 return (NULL); 1020 } 1021 (void) mutex_lock(&smb_ads_site_mtx); 1022 if (*smb_ads_site != '\0') { 1023 if ((ah->site = strdup(smb_ads_site)) == NULL) { 1024 smb_ads_close(ah); 1025 (void) mutex_unlock(&smb_ads_site_mtx); 1026 return (NULL); 1027 } 1028 } else { 1029 ah->site = NULL; 1030 } 1031 (void) mutex_unlock(&smb_ads_site_mtx); 1032 1033 if (smb_ads_bind(ah) == -1) { 1034 smb_ads_close(ah); 1035 return (NULL); 1036 } 1037 1038 return (ah); 1039 } 1040 1041 /* 1042 * smb_ads_close 1043 * Close connection to ADS server and free memory allocated for ADS handle. 1044 * LDAP unbind is called here. 1045 * Parameters: 1046 * ah: handle to ADS server 1047 * Returns: 1048 * void 1049 */ 1050 void 1051 smb_ads_close(smb_ads_handle_t *ah) 1052 { 1053 int len; 1054 1055 if (ah == NULL) 1056 return; 1057 /* close and free connection resources */ 1058 if (ah->ld) 1059 (void) ldap_unbind(ah->ld); 1060 1061 free(ah->user); 1062 if (ah->pwd) { 1063 len = strlen(ah->pwd); 1064 /* zero out the memory that contains user's password */ 1065 if (len > 0) 1066 bzero(ah->pwd, len); 1067 free(ah->pwd); 1068 } 1069 free(ah->domain); 1070 free(ah->domain_dn); 1071 free(ah->hostname); 1072 free(ah->site); 1073 free(ah); 1074 } 1075 1076 /* 1077 * smb_ads_display_stat 1078 * Display error message for GSS-API routines. 1079 * Parameters: 1080 * maj: GSS major status 1081 * min: GSS minor status 1082 * Returns: 1083 * None 1084 */ 1085 static void 1086 smb_ads_display_stat(OM_uint32 maj, OM_uint32 min) 1087 { 1088 gss_buffer_desc msg; 1089 OM_uint32 msg_ctx = 0; 1090 OM_uint32 min2; 1091 (void) gss_display_status(&min2, maj, GSS_C_GSS_CODE, GSS_C_NULL_OID, 1092 &msg_ctx, &msg); 1093 smb_tracef("smbns_ads: major status error: %s", (char *)msg.value); 1094 (void) gss_release_buffer(&min2, &msg); 1095 (void) gss_display_status(&min2, min, GSS_C_MECH_CODE, GSS_C_NULL_OID, 1096 &msg_ctx, &msg); 1097 smb_tracef("smbns_ads: minor status error: %s", (char *)msg.value); 1098 (void) gss_release_buffer(&min2, &msg); 1099 } 1100 1101 /* 1102 * smb_ads_alloc_attr 1103 * 1104 * Since the attrs is a null-terminated array, all elements 1105 * in the array (except the last one) will point to allocated 1106 * memory. 1107 */ 1108 static int 1109 smb_ads_alloc_attr(LDAPMod *attrs[], int num) 1110 { 1111 int i; 1112 1113 bzero(attrs, num * sizeof (LDAPMod *)); 1114 for (i = 0; i < (num - 1); i++) { 1115 attrs[i] = (LDAPMod *)malloc(sizeof (LDAPMod)); 1116 if (attrs[i] == NULL) { 1117 smb_ads_free_attr(attrs); 1118 return (-1); 1119 } 1120 } 1121 1122 return (0); 1123 } 1124 1125 /* 1126 * smb_ads_free_attr 1127 * Free memory allocated when publishing a share. 1128 * Parameters: 1129 * attrs: an array of LDAPMod pointers 1130 * Returns: 1131 * None 1132 */ 1133 static void 1134 smb_ads_free_attr(LDAPMod *attrs[]) 1135 { 1136 int i; 1137 for (i = 0; attrs[i]; i++) { 1138 free(attrs[i]); 1139 } 1140 } 1141 1142 /* 1143 * smb_ads_get_spnset 1144 * 1145 * Derives the core set of SPNs based on the FQHN. 1146 * The spn_set is a null-terminated array of char pointers. 1147 * 1148 * Returns 0 upon success. Otherwise, returns -1. 1149 */ 1150 static int 1151 smb_ads_get_spnset(char *fqhost, char **spn_set) 1152 { 1153 int i; 1154 1155 bzero(spn_set, (SMBKRB5_SPN_IDX_MAX + 1) * sizeof (char *)); 1156 for (i = 0; i < SMBKRB5_SPN_IDX_MAX; i++) { 1157 if ((spn_set[i] = smb_krb5_get_spn(i, fqhost)) == NULL) { 1158 smb_ads_free_spnset(spn_set); 1159 return (-1); 1160 } 1161 } 1162 1163 return (0); 1164 } 1165 1166 /* 1167 * smb_ads_free_spnset 1168 * 1169 * Free the memory allocated for the set of SPNs. 1170 */ 1171 static void 1172 smb_ads_free_spnset(char **spn_set) 1173 { 1174 int i; 1175 for (i = 0; spn_set[i]; i++) 1176 free(spn_set[i]); 1177 } 1178 1179 /* 1180 * smb_ads_acquire_cred 1181 * Called by smb_ads_bind() to get a handle to administrative user's credential 1182 * stored locally on the system. The credential is the TGT. If the attempt at 1183 * getting handle fails then a second attempt will be made after getting a 1184 * new TGT. 1185 * Please look at smb_ads_bind() for more information. 1186 * 1187 * Paramters: 1188 * ah : handle to ADS server 1189 * kinit_retry: if 0 then a second attempt will be made to get handle to the 1190 * credential if the first attempt fails 1191 * Returns: 1192 * cred_handle: handle to the administrative user's credential (TGT) 1193 * oid : contains Kerberos 5 object identifier 1194 * kinit_retry: A 1 indicates that a second attempt has been made to get 1195 * handle to the credential and no further attempts can be made 1196 * -1 : error 1197 * 0 : success 1198 */ 1199 static int 1200 smb_ads_acquire_cred(smb_ads_handle_t *ah, gss_cred_id_t *cred_handle, 1201 gss_OID *oid, int *kinit_retry) 1202 { 1203 return (krb5_acquire_cred_kinit(ah->user, ah->pwd, cred_handle, oid, 1204 kinit_retry, "ads")); 1205 } 1206 1207 /* 1208 * smb_ads_establish_sec_context 1209 * Called by smb_ads_bind() to establish a security context to an LDAP service 1210 * on an ADS server. If the attempt at establishing the security context fails 1211 * then a second attempt will be made by smb_ads_bind() if a new TGT has not 1212 * been already obtained in ads_acquire_cred. The second attempt, if allowed, 1213 * will obtained a new TGT here and a new handle to the credential will also be 1214 * obtained in ads_acquire_cred. LDAP SASL bind is used to send and receive 1215 * the GSS tokens to and from the ADS server. 1216 * Please look at ads_bind for more information. 1217 * Paramters: 1218 * ah : handle to ADS server 1219 * cred_handle : handle to administrative user's credential (TGT) 1220 * oid : Kerberos 5 object identifier 1221 * kinit_retry : if 0 then a second attempt can be made to establish a 1222 * security context with ADS server if first attempt fails 1223 * Returns: 1224 * gss_context : security context to ADS server 1225 * sercred : encrypted ADS server's supported security layers 1226 * do_acquire_cred: if 1 then a second attempt will be made to establish a 1227 * security context with ADS server after getting a new 1228 * handle to the user's credential 1229 * kinit_retry : if 1 then a second attempt will be made to establish a 1230 * a security context and no further attempts can be made 1231 * -1 : error 1232 * 0 : success 1233 */ 1234 static int 1235 smb_ads_establish_sec_context(smb_ads_handle_t *ah, gss_ctx_id_t *gss_context, 1236 gss_cred_id_t cred_handle, gss_OID oid, struct berval **sercred, 1237 int *kinit_retry, int *do_acquire_cred) 1238 { 1239 OM_uint32 maj, min, time_rec; 1240 char service_name[SMB_ADS_MAXBUFLEN]; 1241 gss_buffer_desc send_tok, service_buf; 1242 gss_name_t target_name; 1243 gss_buffer_desc input; 1244 gss_buffer_desc *inputptr; 1245 struct berval cred; 1246 OM_uint32 ret_flags; 1247 int stat; 1248 int gss_flags; 1249 1250 (void) snprintf(service_name, SMB_ADS_MAXBUFLEN, "ldap@%s", 1251 ah->hostname); 1252 service_buf.value = service_name; 1253 service_buf.length = strlen(service_name)+1; 1254 if ((maj = gss_import_name(&min, &service_buf, 1255 (gss_OID) gss_nt_service_name, 1256 &target_name)) != GSS_S_COMPLETE) { 1257 smb_ads_display_stat(maj, min); 1258 if (oid != GSS_C_NO_OID) 1259 (void) gss_release_oid(&min, &oid); 1260 return (-1); 1261 } 1262 1263 *gss_context = GSS_C_NO_CONTEXT; 1264 *sercred = NULL; 1265 inputptr = GSS_C_NO_BUFFER; 1266 gss_flags = GSS_C_MUTUAL_FLAG; 1267 do { 1268 if (krb5_establish_sec_ctx_kinit(ah->user, ah->pwd, 1269 cred_handle, gss_context, target_name, oid, 1270 gss_flags, inputptr, &send_tok, 1271 &ret_flags, &time_rec, kinit_retry, 1272 do_acquire_cred, &maj, "ads") == -1) { 1273 if (oid != GSS_C_NO_OID) 1274 (void) gss_release_oid(&min, &oid); 1275 (void) gss_release_name(&min, &target_name); 1276 return (-1); 1277 } 1278 1279 cred.bv_val = send_tok.value; 1280 cred.bv_len = send_tok.length; 1281 if (*sercred) { 1282 ber_bvfree(*sercred); 1283 *sercred = NULL; 1284 } 1285 stat = ldap_sasl_bind_s(ah->ld, NULL, "GSSAPI", 1286 &cred, NULL, NULL, sercred); 1287 if (stat != LDAP_SUCCESS && 1288 stat != LDAP_SASL_BIND_IN_PROGRESS) { 1289 smb_tracef("smbns_ads: ldap_sasl_bind error: %s", 1290 ldap_err2string(stat)); 1291 if (oid != GSS_C_NO_OID) 1292 (void) gss_release_oid(&min, &oid); 1293 (void) gss_release_name(&min, &target_name); 1294 (void) gss_release_buffer(&min, &send_tok); 1295 return (-1); 1296 } 1297 input.value = (*sercred)->bv_val; 1298 input.length = (*sercred)->bv_len; 1299 inputptr = &input; 1300 if (send_tok.length > 0) 1301 (void) gss_release_buffer(&min, &send_tok); 1302 } while (maj != GSS_S_COMPLETE); 1303 1304 if (oid != GSS_C_NO_OID) 1305 (void) gss_release_oid(&min, &oid); 1306 (void) gss_release_name(&min, &target_name); 1307 1308 return (0); 1309 } 1310 1311 /* 1312 * smb_ads_negotiate_sec_layer 1313 * Call by smb_ads_bind() to negotiate additional security layer for further 1314 * communication after security context establishment. No additional security 1315 * is needed so a "no security layer" is negotiated. The security layer is 1316 * described in the SASL RFC 2478 and this step is needed for secure LDAP 1317 * binding. LDAP SASL bind is used to send and receive the GSS tokens to and 1318 * from the ADS server. 1319 * Please look at smb_ads_bind for more information. 1320 * 1321 * Paramters: 1322 * ah : handle to ADS server 1323 * gss_context: security context to ADS server 1324 * sercred : encrypted ADS server's supported security layers 1325 * Returns: 1326 * -1 : error 1327 * 0 : success 1328 */ 1329 static int 1330 smb_ads_negotiate_sec_layer(smb_ads_handle_t *ah, gss_ctx_id_t gss_context, 1331 struct berval *sercred) 1332 { 1333 OM_uint32 maj, min; 1334 gss_buffer_desc unwrap_inbuf, unwrap_outbuf; 1335 gss_buffer_desc wrap_inbuf, wrap_outbuf; 1336 int conf_state, sec_layer; 1337 char auth_id[5]; 1338 struct berval cred; 1339 int stat; 1340 gss_qop_t qt; 1341 1342 /* check for server supported security layer */ 1343 unwrap_inbuf.value = sercred->bv_val; 1344 unwrap_inbuf.length = sercred->bv_len; 1345 if ((maj = gss_unwrap(&min, gss_context, 1346 &unwrap_inbuf, &unwrap_outbuf, 1347 &conf_state, &qt)) != GSS_S_COMPLETE) { 1348 smb_ads_display_stat(maj, min); 1349 if (sercred) 1350 ber_bvfree(sercred); 1351 return (-1); 1352 } 1353 sec_layer = *((char *)unwrap_outbuf.value); 1354 (void) gss_release_buffer(&min, &unwrap_outbuf); 1355 if (!(sec_layer & 1)) { 1356 if (sercred) 1357 ber_bvfree(sercred); 1358 return (-1); 1359 } 1360 if (sercred) ber_bvfree(sercred); 1361 1362 /* no security layer needed after successful binding */ 1363 auth_id[0] = 0x01; 1364 1365 /* byte 2-4: max client recv size in network byte order */ 1366 auth_id[1] = 0x00; 1367 auth_id[2] = 0x40; 1368 auth_id[3] = 0x00; 1369 wrap_inbuf.value = auth_id; 1370 wrap_inbuf.length = 4; 1371 conf_state = 0; 1372 if ((maj = gss_wrap(&min, gss_context, conf_state, 0, &wrap_inbuf, 1373 &conf_state, &wrap_outbuf)) != GSS_S_COMPLETE) { 1374 smb_ads_display_stat(maj, min); 1375 return (-1); 1376 } 1377 1378 cred.bv_val = wrap_outbuf.value; 1379 cred.bv_len = wrap_outbuf.length; 1380 sercred = NULL; 1381 stat = ldap_sasl_bind_s(ah->ld, NULL, "GSSAPI", &cred, NULL, NULL, 1382 &sercred); 1383 if (stat != LDAP_SUCCESS && stat != LDAP_SASL_BIND_IN_PROGRESS) { 1384 smb_tracef("smbns_ads: ldap_sasl_bind error: %s:", 1385 ldap_err2string(stat)); 1386 (void) gss_release_buffer(&min, &wrap_outbuf); 1387 return (-1); 1388 } 1389 1390 (void) gss_release_buffer(&min, &wrap_outbuf); 1391 if (sercred) 1392 ber_bvfree(sercred); 1393 1394 return (0); 1395 } 1396 1397 /* 1398 * smb_ads_bind 1399 * Use secure binding to bind to ADS server. 1400 * Use GSS-API with Kerberos 5 as the security mechanism and LDAP SASL with 1401 * Kerberos 5 as the security mechanisn to authenticate, obtain a security 1402 * context, and securely bind an administrative user so that other LDAP 1403 * commands can be used, i.e. add and delete. 1404 * 1405 * To obtain the security context, a Kerberos ticket-granting ticket (TGT) 1406 * for the user is needed to obtain a ticket for the LDAP service. To get 1407 * a TGT for the user, the username and password is needed. Once a TGT is 1408 * obtained then it will be stored locally and used until it is expired. 1409 * This routine will automatically obtained a TGT for the first time or when 1410 * it expired. LDAP SASL bind is then finally used to send GSS tokens to 1411 * obtain a security context for the LDAP service on the ADS server. If 1412 * there is any problem getting the security context then a new TGT will be 1413 * obtain to try getting the security context once more. 1414 * 1415 * After the security context is obtain and established, the LDAP SASL bind 1416 * is used to negotiate an additional security layer. No further security is 1417 * needed so a "no security layer" is negotiated. After this the security 1418 * context can be deleted and further LDAP commands can be sent to the ADS 1419 * server until a LDAP unbind command is issued to the ADS server. 1420 * Paramaters: 1421 * ah: handle to ADS server 1422 * Returns: 1423 * -1: error 1424 * 0: success 1425 */ 1426 static int 1427 smb_ads_bind(smb_ads_handle_t *ah) 1428 { 1429 OM_uint32 min; 1430 gss_cred_id_t cred_handle; 1431 gss_ctx_id_t gss_context; 1432 gss_OID oid; 1433 struct berval *sercred; 1434 int kinit_retry, do_acquire_cred; 1435 int rc = 0; 1436 1437 kinit_retry = 0; 1438 do_acquire_cred = 0; 1439 1440 acquire_cred: 1441 1442 if (smb_ads_acquire_cred(ah, &cred_handle, &oid, &kinit_retry)) 1443 return (-1); 1444 1445 if (smb_ads_establish_sec_context(ah, &gss_context, cred_handle, 1446 oid, &sercred, &kinit_retry, &do_acquire_cred)) { 1447 (void) gss_release_cred(&min, &cred_handle); 1448 if (do_acquire_cred) { 1449 do_acquire_cred = 0; 1450 goto acquire_cred; 1451 } 1452 return (-1); 1453 } 1454 rc = smb_ads_negotiate_sec_layer(ah, gss_context, sercred); 1455 1456 if (cred_handle != GSS_C_NO_CREDENTIAL) 1457 (void) gss_release_cred(&min, &cred_handle); 1458 (void) gss_delete_sec_context(&min, &gss_context, NULL); 1459 1460 return ((rc) ? -1 : 0); 1461 } 1462 1463 /* 1464 * smb_ads_add_share 1465 * Call by smb_ads_publish_share to create share object in ADS. 1466 * This routine specifies the attributes of an ADS LDAP share object. The first 1467 * attribute and values define the type of ADS object, the share object. The 1468 * second attribute and value define the UNC of the share data for the share 1469 * object. The LDAP synchronous add command is used to add the object into ADS. 1470 * The container location to add the object needs to specified. 1471 * Parameters: 1472 * ah : handle to ADS server 1473 * adsShareName: name of share object to be created in ADS 1474 * shareUNC : share name on NetForce 1475 * adsContainer: location in ADS to create share object 1476 * 1477 * Returns: 1478 * -1 : error 1479 * 0 : success 1480 */ 1481 int 1482 smb_ads_add_share(smb_ads_handle_t *ah, const char *adsShareName, 1483 const char *unc_name, const char *adsContainer) 1484 { 1485 LDAPMod *attrs[SMB_ADS_SHARE_NUM_ATTR]; 1486 int j = 0; 1487 char *share_dn; 1488 int len, ret; 1489 char *unc_names[] = {(char *)unc_name, NULL}; 1490 1491 len = 5 + strlen(adsShareName) + strlen(adsContainer) + 1492 strlen(ah->domain_dn) + 1; 1493 1494 share_dn = (char *)malloc(len); 1495 if (share_dn == NULL) 1496 return (-1); 1497 1498 (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, 1499 adsContainer, ah->domain_dn); 1500 1501 if (smb_ads_alloc_attr(attrs, SMB_ADS_SHARE_NUM_ATTR) != 0) { 1502 free(share_dn); 1503 return (-1); 1504 } 1505 1506 attrs[j]->mod_op = LDAP_MOD_ADD; 1507 attrs[j]->mod_type = "objectClass"; 1508 attrs[j]->mod_values = smb_ads_share_objcls; 1509 1510 attrs[++j]->mod_op = LDAP_MOD_ADD; 1511 attrs[j]->mod_type = "uNCName"; 1512 attrs[j]->mod_values = unc_names; 1513 1514 if ((ret = ldap_add_s(ah->ld, share_dn, attrs)) != LDAP_SUCCESS) { 1515 smb_tracef("smbns_ads: %s: ldap_add error: %s", 1516 share_dn, ldap_err2string(ret)); 1517 smb_ads_free_attr(attrs); 1518 free(share_dn); 1519 return (ret); 1520 } 1521 free(share_dn); 1522 smb_ads_free_attr(attrs); 1523 1524 return (0); 1525 } 1526 1527 /* 1528 * smb_ads_del_share 1529 * Call by smb_ads_remove_share to remove share object from ADS. The container 1530 * location to remove the object needs to specified. The LDAP synchronous 1531 * delete command is used. 1532 * Parameters: 1533 * ah : handle to ADS server 1534 * adsShareName: name of share object in ADS to be removed 1535 * adsContainer: location of share object in ADS 1536 * Returns: 1537 * -1 : error 1538 * 0 : success 1539 */ 1540 static int 1541 smb_ads_del_share(smb_ads_handle_t *ah, const char *adsShareName, 1542 const char *adsContainer) 1543 { 1544 char *share_dn; 1545 int len, ret; 1546 1547 len = 5 + strlen(adsShareName) + strlen(adsContainer) + 1548 strlen(ah->domain_dn) + 1; 1549 1550 share_dn = (char *)malloc(len); 1551 if (share_dn == NULL) 1552 return (-1); 1553 1554 (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, 1555 adsContainer, ah->domain_dn); 1556 if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) { 1557 smb_tracef("smbns_ads: ldap_delete error: %s", 1558 ldap_err2string(ret)); 1559 free(share_dn); 1560 return (-1); 1561 } 1562 free(share_dn); 1563 1564 return (0); 1565 } 1566 1567 1568 /* 1569 * smb_ads_escape_search_filter_chars 1570 * 1571 * This routine will escape the special characters found in a string 1572 * that will later be passed to the ldap search filter. 1573 * 1574 * RFC 1960 - A String Representation of LDAP Search Filters 1575 * 3. String Search Filter Definition 1576 * If a value must contain one of the characters '*' OR '(' OR ')', 1577 * these characters 1578 * should be escaped by preceding them with the backslash '\' character. 1579 * 1580 * RFC 2252 - LDAP Attribute Syntax Definitions 1581 * a backslash quoting mechanism is used to escape 1582 * the following separator symbol character (such as "'", "$" or "#") if 1583 * it should occur in that string. 1584 */ 1585 static int 1586 smb_ads_escape_search_filter_chars(const char *src, char *dst) 1587 { 1588 int avail = SMB_ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */ 1589 1590 if (src == NULL || dst == NULL) 1591 return (-1); 1592 1593 while (*src) { 1594 if (!avail) { 1595 *dst = 0; 1596 return (-1); 1597 } 1598 1599 switch (*src) { 1600 case '\\': 1601 case '\'': 1602 case '$': 1603 case '#': 1604 case '*': 1605 case '(': 1606 case ')': 1607 *dst++ = '\\'; 1608 avail--; 1609 /* fall through */ 1610 1611 default: 1612 *dst++ = *src++; 1613 avail--; 1614 } 1615 } 1616 1617 *dst = 0; 1618 1619 return (0); 1620 } 1621 1622 /* 1623 * smb_ads_lookup_share 1624 * The search filter is set to search for a specific share name in the 1625 * specified ADS container. The LDSAP synchronous search command is used. 1626 * Parameters: 1627 * ah : handle to ADS server 1628 * adsShareName: name of share object in ADS to be searched 1629 * adsContainer: location of share object in ADS 1630 * Returns: 1631 * -1 : error 1632 * 0 : not found 1633 * 1 : found 1634 */ 1635 int 1636 smb_ads_lookup_share(smb_ads_handle_t *ah, const char *adsShareName, 1637 const char *adsContainer, char *unc_name) 1638 { 1639 char *attrs[4], filter[SMB_ADS_MAXBUFLEN]; 1640 char *share_dn; 1641 int len, ret; 1642 LDAPMessage *res; 1643 char tmpbuf[SMB_ADS_MAXBUFLEN]; 1644 1645 if (adsShareName == NULL || adsContainer == NULL) 1646 return (-1); 1647 1648 len = 5 + strlen(adsShareName) + strlen(adsContainer) + 1649 strlen(ah->domain_dn) + 1; 1650 1651 share_dn = (char *)malloc(len); 1652 if (share_dn == NULL) 1653 return (-1); 1654 1655 (void) snprintf(share_dn, len, "cn=%s,%s,%s", adsShareName, 1656 adsContainer, ah->domain_dn); 1657 1658 res = NULL; 1659 attrs[0] = "cn"; 1660 attrs[1] = "objectClass"; 1661 attrs[2] = "uNCName"; 1662 attrs[3] = NULL; 1663 1664 if (smb_ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) { 1665 free(share_dn); 1666 return (-1); 1667 } 1668 1669 (void) snprintf(filter, sizeof (filter), 1670 "(&(objectClass=volume)(uNCName=%s))", tmpbuf); 1671 1672 if ((ret = ldap_search_s(ah->ld, share_dn, 1673 LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) { 1674 if (ret != LDAP_NO_SUCH_OBJECT) 1675 smb_tracef("smbns_ads: %s: ldap_search error: %s", 1676 share_dn, ldap_err2string(ret)); 1677 1678 (void) ldap_msgfree(res); 1679 free(share_dn); 1680 return (0); 1681 } 1682 1683 (void) free(share_dn); 1684 1685 /* no match is found */ 1686 if (ldap_count_entries(ah->ld, res) == 0) { 1687 (void) ldap_msgfree(res); 1688 return (0); 1689 } 1690 1691 /* free the search results */ 1692 (void) ldap_msgfree(res); 1693 1694 return (1); 1695 } 1696 1697 /* 1698 * smb_ads_publish_share 1699 * Publish share into ADS. If a share name already exist in ADS in the same 1700 * container then the existing share object is removed before adding the new 1701 * share object. 1702 * Parameters: 1703 * ah : handle return from smb_ads_open 1704 * adsShareName: name of share to be added to ADS directory 1705 * shareUNC : name of share on client, can be NULL to use the same name 1706 * as adsShareName 1707 * adsContainer: location for share to be added in ADS directory, ie 1708 * ou=share_folder 1709 * uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR 1710 * to use host ip addr for UNC. 1711 * Returns: 1712 * -1 : error 1713 * 0 : success 1714 */ 1715 int 1716 smb_ads_publish_share(smb_ads_handle_t *ah, const char *adsShareName, 1717 const char *shareUNC, const char *adsContainer, const char *hostname) 1718 { 1719 int ret; 1720 char unc_name[SMB_ADS_MAXBUFLEN]; 1721 1722 if (adsShareName == NULL || adsContainer == NULL) 1723 return (-1); 1724 1725 if (shareUNC == 0 || *shareUNC == 0) 1726 shareUNC = adsShareName; 1727 1728 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1729 hostname, shareUNC) < 0) 1730 return (-1); 1731 1732 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1733 1734 switch (ret) { 1735 case 1: 1736 (void) smb_ads_del_share(ah, adsShareName, adsContainer); 1737 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1738 adsContainer); 1739 break; 1740 1741 case 0: 1742 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1743 adsContainer); 1744 if (ret == LDAP_ALREADY_EXISTS) 1745 ret = -1; 1746 1747 break; 1748 1749 case -1: 1750 default: 1751 /* return with error code */ 1752 ret = -1; 1753 } 1754 1755 return (ret); 1756 } 1757 1758 /* 1759 * smb_ads_remove_share 1760 * Remove share from ADS. A search is done first before explicitly removing 1761 * the share. 1762 * Parameters: 1763 * ah : handle return from smb_ads_open 1764 * adsShareName: name of share to be removed from ADS directory 1765 * adsContainer: location for share to be removed from ADS directory, ie 1766 * ou=share_folder 1767 * Returns: 1768 * -1 : error 1769 * 0 : success 1770 */ 1771 int 1772 smb_ads_remove_share(smb_ads_handle_t *ah, const char *adsShareName, 1773 const char *shareUNC, const char *adsContainer, const char *hostname) 1774 { 1775 int ret; 1776 char unc_name[SMB_ADS_MAXBUFLEN]; 1777 1778 if (adsShareName == NULL || adsContainer == NULL) 1779 return (-1); 1780 if (shareUNC == 0 || *shareUNC == 0) 1781 shareUNC = adsShareName; 1782 1783 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1784 hostname, shareUNC) < 0) 1785 return (-1); 1786 1787 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1788 if (ret == 0) 1789 return (0); 1790 if (ret == -1) 1791 return (-1); 1792 1793 return (smb_ads_del_share(ah, adsShareName, adsContainer)); 1794 } 1795 1796 /* 1797 * smb_ads_get_default_comp_container_dn 1798 * 1799 * Build the distinguished name for the default computer conatiner (i.e. the 1800 * pre-defined Computers container). 1801 */ 1802 static void 1803 smb_ads_get_default_comp_container_dn(smb_ads_handle_t *ah, char *buf, 1804 size_t buflen) 1805 { 1806 (void) snprintf(buf, buflen, "cn=%s,%s", SMB_ADS_COMPUTERS_CN, 1807 ah->domain_dn); 1808 } 1809 1810 /* 1811 * smb_ads_get_default_comp_dn 1812 * 1813 * Build the distinguished name for this system. 1814 */ 1815 static void 1816 smb_ads_get_default_comp_dn(smb_ads_handle_t *ah, char *buf, size_t buflen) 1817 { 1818 char nbname[NETBIOS_NAME_SZ]; 1819 char container_dn[SMB_ADS_DN_MAX]; 1820 1821 (void) smb_getnetbiosname(nbname, sizeof (nbname)); 1822 smb_ads_get_default_comp_container_dn(ah, container_dn, SMB_ADS_DN_MAX); 1823 (void) snprintf(buf, buflen, "cn=%s,%s", nbname, container_dn); 1824 } 1825 1826 /* 1827 * smb_ads_add_computer 1828 * 1829 * Returns 0 upon success. Otherwise, returns -1. 1830 */ 1831 static int 1832 smb_ads_add_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1833 { 1834 return (smb_ads_computer_op(ah, LDAP_MOD_ADD, dclevel, dn)); 1835 } 1836 1837 /* 1838 * smb_ads_modify_computer 1839 * 1840 * Returns 0 upon success. Otherwise, returns -1. 1841 */ 1842 static int 1843 smb_ads_modify_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1844 { 1845 return (smb_ads_computer_op(ah, LDAP_MOD_REPLACE, dclevel, dn)); 1846 } 1847 1848 /* 1849 * smb_ads_get_dc_level 1850 * 1851 * Returns the functional level of the DC upon success. 1852 * Otherwise, -1 is returned. 1853 */ 1854 static int 1855 smb_ads_get_dc_level(smb_ads_handle_t *ah) 1856 { 1857 LDAPMessage *res, *entry; 1858 char *attr[2]; 1859 char **vals; 1860 int rc = -1; 1861 1862 res = NULL; 1863 attr[0] = SMB_ADS_ATTR_DCLEVEL; 1864 attr[1] = NULL; 1865 if (ldap_search_s(ah->ld, "", LDAP_SCOPE_BASE, NULL, attr, 1866 0, &res) != LDAP_SUCCESS) { 1867 (void) ldap_msgfree(res); 1868 return (-1); 1869 } 1870 1871 /* no match for the specified attribute is found */ 1872 if (ldap_count_entries(ah->ld, res) == 0) { 1873 (void) ldap_msgfree(res); 1874 return (-1); 1875 } 1876 1877 entry = ldap_first_entry(ah->ld, res); 1878 if (entry) { 1879 if ((vals = ldap_get_values(ah->ld, entry, 1880 SMB_ADS_ATTR_DCLEVEL)) == NULL) { 1881 /* 1882 * Observed the values aren't populated 1883 * by the Windows 2000 server. 1884 */ 1885 (void) ldap_msgfree(res); 1886 return (SMB_ADS_DCLEVEL_W2K); 1887 } 1888 1889 if (vals[0] != NULL) 1890 rc = atoi(vals[0]); 1891 1892 ldap_value_free(vals); 1893 } 1894 1895 (void) ldap_msgfree(res); 1896 return (rc); 1897 } 1898 1899 static int 1900 smb_ads_getfqhostname(smb_ads_handle_t *ah, char *fqhost, int len) 1901 { 1902 if (smb_gethostname(fqhost, len, 0) != 0) 1903 return (-1); 1904 1905 (void) snprintf(fqhost, len, "%s.%s", fqhost, 1906 ah->domain); 1907 1908 return (0); 1909 } 1910 1911 static int 1912 smb_ads_computer_op(smb_ads_handle_t *ah, int op, int dclevel, char *dn) 1913 { 1914 LDAPMod *attrs[SMB_ADS_COMPUTER_NUM_ATTR]; 1915 char *sam_val[2], *usr_val[2]; 1916 char *spn_set[SMBKRB5_SPN_IDX_MAX + 1], *ctl_val[2], *fqh_val[2]; 1917 char *encrypt_val[2]; 1918 int j = -1; 1919 int ret, usrctl_flags = 0; 1920 char sam_acct[SMB_SAMACCT_MAXLEN]; 1921 char fqhost[MAXHOSTNAMELEN]; 1922 char *user_principal; 1923 char usrctl_buf[16]; 1924 char encrypt_buf[16]; 1925 int max; 1926 1927 if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) 1928 return (-1); 1929 1930 if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN)) 1931 return (-1); 1932 1933 if (smb_ads_get_spnset(fqhost, spn_set) != 0) 1934 return (-1); 1935 1936 /* 1937 * Windows 2008 DC expects the UPN attribute to be host/fqhn while 1938 * both Windows 2000 & 2003 expect it to be host/fqhn@realm. 1939 */ 1940 if (dclevel == SMB_ADS_DCLEVEL_W2K8) 1941 user_principal = smb_krb5_get_spn(SMBKRB5_SPN_IDX_HOST, fqhost); 1942 else 1943 user_principal = smb_krb5_get_upn(spn_set[SMBKRB5_SPN_IDX_HOST], 1944 ah->domain); 1945 1946 if (user_principal == NULL) { 1947 smb_ads_free_spnset(spn_set); 1948 return (-1); 1949 } 1950 1951 max = (SMB_ADS_COMPUTER_NUM_ATTR - ((op != LDAP_MOD_ADD) ? 1 : 0)) 1952 - (dclevel == SMB_ADS_DCLEVEL_W2K8 ? 0 : 1); 1953 1954 if (smb_ads_alloc_attr(attrs, max) != 0) { 1955 free(user_principal); 1956 smb_ads_free_spnset(spn_set); 1957 return (-1); 1958 } 1959 1960 /* objectClass attribute is not modifiable. */ 1961 if (op == LDAP_MOD_ADD) { 1962 attrs[++j]->mod_op = op; 1963 attrs[j]->mod_type = "objectClass"; 1964 attrs[j]->mod_values = smb_ads_computer_objcls; 1965 } 1966 1967 attrs[++j]->mod_op = op; 1968 attrs[j]->mod_type = SMB_ADS_ATTR_SAMACCT; 1969 sam_val[0] = sam_acct; 1970 sam_val[1] = 0; 1971 attrs[j]->mod_values = sam_val; 1972 1973 attrs[++j]->mod_op = op; 1974 attrs[j]->mod_type = SMB_ADS_ATTR_UPN; 1975 usr_val[0] = user_principal; 1976 usr_val[1] = 0; 1977 attrs[j]->mod_values = usr_val; 1978 1979 attrs[++j]->mod_op = op; 1980 attrs[j]->mod_type = SMB_ADS_ATTR_SPN; 1981 attrs[j]->mod_values = spn_set; 1982 1983 attrs[++j]->mod_op = op; 1984 attrs[j]->mod_type = SMB_ADS_ATTR_CTL; 1985 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 1986 SMB_ADS_USER_ACCT_CTL_PASSWD_NOTREQD | 1987 SMB_ADS_USER_ACCT_CTL_ACCOUNTDISABLE); 1988 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 1989 ctl_val[0] = usrctl_buf; 1990 ctl_val[1] = 0; 1991 attrs[j]->mod_values = ctl_val; 1992 1993 attrs[++j]->mod_op = op; 1994 attrs[j]->mod_type = SMB_ADS_ATTR_DNSHOST; 1995 fqh_val[0] = fqhost; 1996 fqh_val[1] = 0; 1997 attrs[j]->mod_values = fqh_val; 1998 1999 /* enctypes support starting in Windows Server 2008 */ 2000 if (dclevel > SMB_ADS_DCLEVEL_W2K3) { 2001 attrs[++j]->mod_op = op; 2002 attrs[j]->mod_type = SMB_ADS_ATTR_ENCTYPES; 2003 (void) snprintf(encrypt_buf, sizeof (encrypt_buf), "%d", 2004 SMB_ADS_ENC_AES256 + SMB_ADS_ENC_AES128 + SMB_ADS_ENC_RC4 + 2005 SMB_ADS_ENC_DES_MD5 + SMB_ADS_ENC_DES_CRC); 2006 encrypt_val[0] = encrypt_buf; 2007 encrypt_val[1] = 0; 2008 attrs[j]->mod_values = encrypt_val; 2009 } 2010 2011 switch (op) { 2012 case LDAP_MOD_ADD: 2013 if ((ret = ldap_add_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2014 smb_tracef("smbns_ads: ldap_add error: %s", 2015 ldap_err2string(ret)); 2016 ret = -1; 2017 } 2018 break; 2019 2020 case LDAP_MOD_REPLACE: 2021 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2022 smb_tracef("smbns_ads: ldap_replace error: %s", 2023 ldap_err2string(ret)); 2024 ret = -1; 2025 } 2026 break; 2027 2028 default: 2029 ret = -1; 2030 2031 } 2032 2033 smb_ads_free_attr(attrs); 2034 free(user_principal); 2035 smb_ads_free_spnset(spn_set); 2036 2037 return (ret); 2038 } 2039 2040 /* 2041 * Delete an ADS computer account. 2042 */ 2043 static void 2044 smb_ads_del_computer(smb_ads_handle_t *ah, char *dn) 2045 { 2046 int rc; 2047 2048 if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) 2049 smb_tracef("smbns_ads: ldap_delete error: %s", 2050 ldap_err2string(rc)); 2051 } 2052 2053 /* 2054 * Gets the value of the given attribute. 2055 */ 2056 static smb_ads_qstat_t 2057 smb_ads_getattr(LDAP *ld, LDAPMessage *entry, smb_ads_avpair_t *avpair) 2058 { 2059 char **vals; 2060 smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND; 2061 2062 assert(avpair); 2063 avpair->avp_val = NULL; 2064 vals = ldap_get_values(ld, entry, avpair->avp_attr); 2065 if (!vals) 2066 return (SMB_ADS_STAT_NOT_FOUND); 2067 2068 if (!vals[0]) { 2069 ldap_value_free(vals); 2070 return (SMB_ADS_STAT_NOT_FOUND); 2071 } 2072 2073 avpair->avp_val = strdup(vals[0]); 2074 if (!avpair->avp_val) 2075 rc = SMB_ADS_STAT_ERR; 2076 2077 ldap_value_free(vals); 2078 return (rc); 2079 } 2080 2081 /* 2082 * Process query's result. 2083 */ 2084 static smb_ads_qstat_t 2085 smb_ads_get_qstat(smb_ads_handle_t *ah, LDAPMessage *res, 2086 smb_ads_avpair_t *avpair) 2087 { 2088 char fqhost[MAXHOSTNAMELEN]; 2089 smb_ads_avpair_t dnshost_avp; 2090 smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND; 2091 LDAPMessage *entry; 2092 2093 if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN)) 2094 return (SMB_ADS_STAT_ERR); 2095 2096 if (ldap_count_entries(ah->ld, res) == 0) 2097 return (SMB_ADS_STAT_NOT_FOUND); 2098 2099 if ((entry = ldap_first_entry(ah->ld, res)) == NULL) 2100 return (SMB_ADS_STAT_ERR); 2101 2102 dnshost_avp.avp_attr = SMB_ADS_ATTR_DNSHOST; 2103 rc = smb_ads_getattr(ah->ld, entry, &dnshost_avp); 2104 2105 switch (rc) { 2106 case SMB_ADS_STAT_FOUND: 2107 /* 2108 * Returns SMB_ADS_STAT_DUP to avoid overwriting 2109 * the computer account of another system whose 2110 * NetBIOS name collides with that of the current 2111 * system. 2112 */ 2113 if (strcasecmp(dnshost_avp.avp_val, fqhost)) 2114 rc = SMB_ADS_STAT_DUP; 2115 2116 free(dnshost_avp.avp_val); 2117 break; 2118 2119 case SMB_ADS_STAT_NOT_FOUND: 2120 /* 2121 * Pre-created computer account doesn't have 2122 * the dNSHostname attribute. It's been observed 2123 * that the dNSHostname attribute is only set after 2124 * a successful domain join. 2125 * Returns SMB_ADS_STAT_FOUND as the account is 2126 * pre-created for the current system. 2127 */ 2128 rc = SMB_ADS_STAT_FOUND; 2129 break; 2130 2131 default: 2132 break; 2133 } 2134 2135 if (rc != SMB_ADS_STAT_FOUND) 2136 return (rc); 2137 2138 if (avpair) 2139 rc = smb_ads_getattr(ah->ld, entry, avpair); 2140 2141 return (rc); 2142 2143 } 2144 2145 /* 2146 * smb_ads_lookup_computer_n_attr 2147 * 2148 * If avpair is NULL, checks the status of the specified computer account. 2149 * Otherwise, looks up the value of the specified computer account's attribute. 2150 * If found, the value field of the avpair will be allocated and set. The 2151 * caller should free the allocated buffer. 2152 * 2153 * Return: 2154 * SMB_ADS_STAT_FOUND - if both the computer and the specified attribute is 2155 * found. 2156 * SMB_ADS_STAT_NOT_FOUND - if either the computer or the specified attribute 2157 * is not found. 2158 * SMB_ADS_STAT_DUP - if the computer account is already used by other systems 2159 * in the AD. This could happen if the hostname of multiple 2160 * systems resolved to the same NetBIOS name. 2161 * SMB_ADS_STAT_ERR - any failure. 2162 */ 2163 static smb_ads_qstat_t 2164 smb_ads_lookup_computer_n_attr(smb_ads_handle_t *ah, smb_ads_avpair_t *avpair, 2165 int scope, char *dn) 2166 { 2167 char *attrs[3], filter[SMB_ADS_MAXBUFLEN]; 2168 LDAPMessage *res; 2169 char sam_acct[SMB_SAMACCT_MAXLEN], sam_acct2[SMB_SAMACCT_MAXLEN]; 2170 smb_ads_qstat_t rc; 2171 2172 if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) 2173 return (SMB_ADS_STAT_ERR); 2174 2175 res = NULL; 2176 attrs[0] = SMB_ADS_ATTR_DNSHOST; 2177 attrs[1] = NULL; 2178 attrs[2] = NULL; 2179 2180 if (avpair) { 2181 if (!avpair->avp_attr) 2182 return (SMB_ADS_STAT_ERR); 2183 2184 attrs[1] = avpair->avp_attr; 2185 } 2186 2187 if (smb_ads_escape_search_filter_chars(sam_acct, sam_acct2) != 0) 2188 return (SMB_ADS_STAT_ERR); 2189 2190 (void) snprintf(filter, sizeof (filter), 2191 "(&(objectClass=computer)(%s=%s))", SMB_ADS_ATTR_SAMACCT, 2192 sam_acct2); 2193 2194 if (ldap_search_s(ah->ld, dn, scope, filter, attrs, 0, 2195 &res) != LDAP_SUCCESS) { 2196 (void) ldap_msgfree(res); 2197 return (SMB_ADS_STAT_NOT_FOUND); 2198 } 2199 2200 rc = smb_ads_get_qstat(ah, res, avpair); 2201 /* free the search results */ 2202 (void) ldap_msgfree(res); 2203 return (rc); 2204 } 2205 2206 /* 2207 * smb_ads_find_computer 2208 * 2209 * Starts by searching for the system's AD computer object in the default 2210 * container (i.e. cn=Computers). If not found, searches the entire directory. 2211 * If found, 'dn' will be set to the distinguished name of the system's AD 2212 * computer object. 2213 */ 2214 static smb_ads_qstat_t 2215 smb_ads_find_computer(smb_ads_handle_t *ah, char *dn) 2216 { 2217 smb_ads_qstat_t stat; 2218 smb_ads_avpair_t avpair; 2219 2220 avpair.avp_attr = SMB_ADS_ATTR_DN; 2221 smb_ads_get_default_comp_container_dn(ah, dn, SMB_ADS_DN_MAX); 2222 stat = smb_ads_lookup_computer_n_attr(ah, &avpair, LDAP_SCOPE_ONELEVEL, 2223 dn); 2224 2225 if (stat == SMB_ADS_STAT_NOT_FOUND) { 2226 (void) strlcpy(dn, ah->domain_dn, SMB_ADS_DN_MAX); 2227 stat = smb_ads_lookup_computer_n_attr(ah, &avpair, 2228 LDAP_SCOPE_SUBTREE, dn); 2229 } 2230 2231 if (stat == SMB_ADS_STAT_FOUND) { 2232 (void) strlcpy(dn, avpair.avp_val, SMB_ADS_DN_MAX); 2233 free(avpair.avp_val); 2234 } 2235 2236 return (stat); 2237 } 2238 2239 /* 2240 * smb_ads_update_computer_cntrl_attr 2241 * 2242 * Modify the user account control attribute of an existing computer 2243 * object on AD. 2244 * 2245 * Returns LDAP error code. 2246 */ 2247 static int 2248 smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *ah, int flags, char *dn) 2249 { 2250 LDAPMod *attrs[2]; 2251 char *ctl_val[2]; 2252 int ret = 0; 2253 char usrctl_buf[16]; 2254 2255 if (smb_ads_alloc_attr(attrs, sizeof (attrs) / sizeof (LDAPMod *)) != 0) 2256 return (LDAP_NO_MEMORY); 2257 2258 attrs[0]->mod_op = LDAP_MOD_REPLACE; 2259 attrs[0]->mod_type = SMB_ADS_ATTR_CTL; 2260 2261 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", flags); 2262 ctl_val[0] = usrctl_buf; 2263 ctl_val[1] = 0; 2264 attrs[0]->mod_values = ctl_val; 2265 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2266 smb_tracef("smbns_ads: ldap_modify error: %s", 2267 ldap_err2string(ret)); 2268 } 2269 2270 smb_ads_free_attr(attrs); 2271 return (ret); 2272 } 2273 2274 /* 2275 * smb_ads_lookup_computer_attr_kvno 2276 * 2277 * Lookup the value of the Kerberos version number attribute of the computer 2278 * account. 2279 */ 2280 static krb5_kvno 2281 smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *ah, char *dn) 2282 { 2283 smb_ads_avpair_t avpair; 2284 int kvno = 1; 2285 2286 avpair.avp_attr = SMB_ADS_ATTR_KVNO; 2287 if (smb_ads_lookup_computer_n_attr(ah, &avpair, 2288 LDAP_SCOPE_BASE, dn) == SMB_ADS_STAT_FOUND) { 2289 kvno = atoi(avpair.avp_val); 2290 free(avpair.avp_val); 2291 } 2292 2293 return (kvno); 2294 } 2295 2296 /* 2297 * smb_ads_gen_machine_passwd 2298 * 2299 * Returned a null-terminated machine password generated randomly 2300 * from [0-9a-zA-Z] character set. In order to pass the password 2301 * quality check (three character classes), an uppercase letter is 2302 * used as the first character of the machine password. 2303 */ 2304 static int 2305 smb_ads_gen_machine_passwd(char *machine_passwd, int bufsz) 2306 { 2307 char *data = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK" 2308 "LMNOPQRSTUVWXYZ"; 2309 int datalen = strlen(data); 2310 int i, data_idx; 2311 2312 if (!machine_passwd || bufsz == 0) 2313 return (-1); 2314 2315 /* 2316 * The decimal value of upper case 'A' is 65. Randomly pick 2317 * an upper-case letter from the ascii table. 2318 */ 2319 machine_passwd[0] = (random() % 26) + 65; 2320 for (i = 1; i < bufsz - 1; i++) { 2321 data_idx = random() % datalen; 2322 machine_passwd[i] = data[data_idx]; 2323 } 2324 2325 machine_passwd[bufsz - 1] = 0; 2326 return (0); 2327 } 2328 2329 /* 2330 * smb_ads_join 2331 * 2332 * Besides the NT-4 style domain join (using MS-RPC), CIFS server also 2333 * provides the domain join using Kerberos Authentication, Keberos 2334 * Change & Set password, and LDAP protocols. Basically, AD join 2335 * operation would require the following tickets to be acquired for the 2336 * the user account that is provided for the domain join. 2337 * 2338 * 1) a Keberos TGT ticket, 2339 * 2) a ldap service ticket, and 2340 * 3) kadmin/changpw service ticket 2341 * 2342 * The ADS client first sends a ldap search request to find out whether 2343 * or not the workstation trust account already exists in the Active Directory. 2344 * The existing computer object for this workstation will be removed and 2345 * a new one will be added. The machine account password is randomly 2346 * generated and set for the newly created computer object using KPASSWD 2347 * protocol (See RFC 3244). Once the password is set, our ADS client 2348 * finalizes the machine account by modifying the user acount control 2349 * attribute of the computer object. Kerberos keys derived from the machine 2350 * account password will be stored locally in /etc/krb5/krb5.keytab file. 2351 * That would be needed while acquiring Kerberos TGT ticket for the host 2352 * principal after the domain join operation. 2353 */ 2354 smb_adjoin_status_t 2355 smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd, 2356 int len) 2357 { 2358 smb_ads_handle_t *ah = NULL; 2359 krb5_context ctx = NULL; 2360 krb5_principal krb5princs[SMBKRB5_SPN_IDX_MAX]; 2361 krb5_kvno kvno; 2362 boolean_t des_only, delete = B_TRUE; 2363 smb_adjoin_status_t rc = SMB_ADJOIN_SUCCESS; 2364 boolean_t new_acct; 2365 int dclevel, num, usrctl_flags = 0; 2366 smb_ads_qstat_t qstat; 2367 char dn[SMB_ADS_DN_MAX]; 2368 char *tmpfile; 2369 2370 /* 2371 * Call library functions that can be used to get 2372 * the list of encryption algorithms available on the system. 2373 * (similar to what 'encrypt -l' CLI does). For now, 2374 * unless someone has modified the configuration of the 2375 * cryptographic framework (very unlikely), the following is the 2376 * list of algorithms available on any system running Nevada 2377 * by default. 2378 */ 2379 krb5_enctype w2k8enctypes[] = {ENCTYPE_AES256_CTS_HMAC_SHA1_96, 2380 ENCTYPE_AES128_CTS_HMAC_SHA1_96, ENCTYPE_ARCFOUR_HMAC, 2381 ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 2382 }; 2383 2384 krb5_enctype pre_w2k8enctypes[] = {ENCTYPE_ARCFOUR_HMAC, 2385 ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 2386 }; 2387 2388 krb5_enctype *encptr; 2389 2390 if ((ah = smb_ads_open_main(domain, user, usr_passwd)) == NULL) { 2391 smb_ccache_remove(SMB_CCACHE_PATH); 2392 return (SMB_ADJOIN_ERR_GET_HANDLE); 2393 } 2394 2395 if (smb_ads_gen_machine_passwd(machine_passwd, len) != 0) { 2396 smb_ads_close(ah); 2397 smb_ccache_remove(SMB_CCACHE_PATH); 2398 return (SMB_ADJOIN_ERR_GEN_PASSWD); 2399 } 2400 2401 if ((dclevel = smb_ads_get_dc_level(ah)) == -1) { 2402 smb_ads_close(ah); 2403 smb_ccache_remove(SMB_CCACHE_PATH); 2404 return (SMB_ADJOIN_ERR_GET_DCLEVEL); 2405 } 2406 2407 qstat = smb_ads_find_computer(ah, dn); 2408 switch (qstat) { 2409 case SMB_ADS_STAT_FOUND: 2410 new_acct = B_FALSE; 2411 if (smb_ads_modify_computer(ah, dclevel, dn) != 0) { 2412 smb_ads_close(ah); 2413 smb_ccache_remove(SMB_CCACHE_PATH); 2414 return (SMB_ADJOIN_ERR_MOD_TRUST_ACCT); 2415 } 2416 break; 2417 2418 case SMB_ADS_STAT_NOT_FOUND: 2419 new_acct = B_TRUE; 2420 smb_ads_get_default_comp_dn(ah, dn, SMB_ADS_DN_MAX); 2421 if (smb_ads_add_computer(ah, dclevel, dn) != 0) { 2422 smb_ads_close(ah); 2423 smb_ccache_remove(SMB_CCACHE_PATH); 2424 return (SMB_ADJOIN_ERR_ADD_TRUST_ACCT); 2425 } 2426 break; 2427 2428 default: 2429 if (qstat == SMB_ADS_STAT_DUP) 2430 rc = SMB_ADJOIN_ERR_DUP_TRUST_ACCT; 2431 else 2432 rc = SMB_ADJOIN_ERR_TRUST_ACCT; 2433 smb_ads_close(ah); 2434 smb_ccache_remove(SMB_CCACHE_PATH); 2435 return (rc); 2436 } 2437 2438 des_only = B_FALSE; 2439 2440 if (smb_krb5_ctx_init(&ctx) != 0) { 2441 rc = SMB_ADJOIN_ERR_INIT_KRB_CTX; 2442 goto adjoin_cleanup; 2443 } 2444 2445 if (smb_krb5_get_principals(ah->domain, ctx, krb5princs) != 0) { 2446 rc = SMB_ADJOIN_ERR_GET_SPNS; 2447 goto adjoin_cleanup; 2448 } 2449 2450 if (smb_krb5_setpwd(ctx, krb5princs[SMBKRB5_SPN_IDX_HOST], 2451 machine_passwd) != 0) { 2452 rc = SMB_ADJOIN_ERR_KSETPWD; 2453 goto adjoin_cleanup; 2454 } 2455 2456 kvno = smb_ads_lookup_computer_attr_kvno(ah, dn); 2457 2458 /* 2459 * Only members of Domain Admins and Enterprise Admins can set 2460 * the TRUSTED_FOR_DELEGATION userAccountControl flag. 2461 */ 2462 if (smb_ads_update_computer_cntrl_attr(ah, 2463 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION, dn) 2464 == LDAP_INSUFFICIENT_ACCESS) { 2465 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 2466 SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); 2467 2468 syslog(LOG_NOTICE, "smbns_ads: Unable to set the " 2469 "TRUSTED_FOR_DELEGATION userAccountControl flag on " 2470 "the machine account in Active Directory. Please refer " 2471 "to the Troubleshooting guide for more information."); 2472 2473 } else { 2474 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 2475 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | 2476 SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); 2477 } 2478 2479 if (des_only) 2480 usrctl_flags |= SMB_ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY; 2481 2482 if (smb_ads_update_computer_cntrl_attr(ah, usrctl_flags, dn) 2483 != 0) { 2484 rc = SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR; 2485 goto adjoin_cleanup; 2486 } 2487 2488 if (dclevel == SMB_ADS_DCLEVEL_W2K8) { 2489 num = sizeof (w2k8enctypes) / sizeof (krb5_enctype); 2490 encptr = w2k8enctypes; 2491 } else { 2492 num = sizeof (pre_w2k8enctypes) / sizeof (krb5_enctype); 2493 encptr = pre_w2k8enctypes; 2494 } 2495 2496 tmpfile = mktemp(SMBNS_KRB5_KEYTAB_TMP); 2497 if (tmpfile == NULL) 2498 tmpfile = SMBNS_KRB5_KEYTAB_TMP; 2499 2500 if (smb_krb5_add_keytab_entries(ctx, krb5princs, tmpfile, 2501 kvno, machine_passwd, encptr, num) != 0) { 2502 rc = SMB_ADJOIN_ERR_WRITE_KEYTAB; 2503 goto adjoin_cleanup; 2504 } 2505 2506 delete = B_FALSE; 2507 adjoin_cleanup: 2508 if (new_acct && delete) 2509 smb_ads_del_computer(ah, dn); 2510 2511 if (rc != SMB_ADJOIN_ERR_INIT_KRB_CTX) { 2512 if (rc != SMB_ADJOIN_ERR_GET_SPNS) 2513 smb_krb5_free_principals(ctx, krb5princs, 2514 SMBKRB5_SPN_IDX_MAX); 2515 smb_krb5_ctx_fini(ctx); 2516 } 2517 2518 /* commit keytab file */ 2519 if (rc == SMB_ADJOIN_SUCCESS) { 2520 if (rename(tmpfile, SMBNS_KRB5_KEYTAB) != 0) { 2521 (void) unlink(tmpfile); 2522 rc = SMB_ADJOIN_ERR_COMMIT_KEYTAB; 2523 } else { 2524 /* Set IDMAP config */ 2525 if (smb_config_set_idmap_domain(ah->domain) != 0) { 2526 rc = SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN; 2527 } else { 2528 2529 /* Refresh IDMAP service */ 2530 if (smb_config_refresh_idmap() != 0) 2531 rc = SMB_ADJOIN_ERR_IDMAP_REFRESH; 2532 } 2533 } 2534 } else { 2535 (void) unlink(tmpfile); 2536 } 2537 2538 smb_ads_close(ah); 2539 smb_ccache_remove(SMB_CCACHE_PATH); 2540 return (rc); 2541 } 2542 2543 /* 2544 * smb_adjoin_report_err 2545 * 2546 * Display error message for the specific adjoin error code. 2547 */ 2548 char * 2549 smb_adjoin_report_err(smb_adjoin_status_t status) 2550 { 2551 if (status < 0 || status >= SMB_ADJOIN_NUM_STATUS) 2552 return ("ADJOIN: unknown status"); 2553 2554 return (smb_ads_adjoin_errmsg[status]); 2555 } 2556 2557 /* 2558 * smb_ads_select_pdc 2559 * 2560 * This method walks the list of DCs and returns the first DC record that 2561 * responds to ldap ping and whose IP address is same as the IP address set in 2562 * the Preferred Domain Controller (pdc) property. 2563 * 2564 * Returns a pointer to the found DC record. 2565 * Returns NULL, on error or if no DC record is found. 2566 */ 2567 static smb_ads_host_info_t * 2568 smb_ads_select_pdc(smb_ads_host_list_t *hlist) 2569 { 2570 smb_ads_host_info_t *hentry; 2571 smb_inaddr_t ipaddr; 2572 size_t cnt; 2573 int i; 2574 2575 if (smb_config_getip(SMB_CI_DOMAIN_SRV, &ipaddr) != SMBD_SMF_OK) 2576 return (NULL); 2577 2578 cnt = hlist->ah_cnt; 2579 for (i = 0; i < cnt; i++) { 2580 hentry = &hlist->ah_list[i]; 2581 if (smb_inet_equal(&hentry->ipaddr, &ipaddr, SMB_INET_NOMASK) && 2582 (smb_ads_ldap_ping(hentry) == 0)) 2583 return (hentry); 2584 } 2585 2586 return (NULL); 2587 } 2588 2589 /* 2590 * smb_ads_select_dcfromsubnet 2591 * 2592 * This method walks the list of DCs and returns the first DC record that 2593 * responds to ldap ping and is in the same subnet as the host. 2594 * 2595 * Returns a pointer to the found DC record. 2596 * Returns NULL, on error or if no DC record is found. 2597 */ 2598 static smb_ads_host_info_t * 2599 smb_ads_select_dcfromsubnet(smb_ads_host_list_t *hlist) 2600 { 2601 smb_ads_host_info_t *hentry; 2602 smb_nic_t *lnic; 2603 smb_niciter_t ni; 2604 size_t cnt; 2605 int i; 2606 2607 if (smb_nic_getfirst(&ni) != 0) 2608 return (NULL); 2609 do { 2610 lnic = &ni.ni_nic; 2611 cnt = hlist->ah_cnt; 2612 2613 for (i = 0; i < cnt; i++) { 2614 hentry = &hlist->ah_list[i]; 2615 if ((hentry->ipaddr.a_family == AF_INET) && 2616 (lnic->nic_ip.a_family == AF_INET)) { 2617 if ((hentry->ipaddr.a_ipv4 & 2618 lnic->nic_mask) == 2619 (lnic->nic_ip.a_ipv4 & 2620 lnic->nic_mask)) 2621 if (smb_ads_ldap_ping(hentry) == 0) 2622 return (hentry); 2623 } 2624 } 2625 } while (smb_nic_getnext(&ni) == 0); 2626 2627 return (NULL); 2628 } 2629 2630 /* 2631 * smb_ads_select_dcfromlist 2632 * 2633 * This method walks the list of DCs and returns the first DC that 2634 * responds to ldap ping. 2635 * 2636 * Returns a pointer to the found DC record. 2637 * Returns NULL if no DC record is found. 2638 */ 2639 static smb_ads_host_info_t * 2640 smb_ads_select_dcfromlist(smb_ads_host_list_t *hlist) 2641 { 2642 smb_ads_host_info_t *hentry; 2643 size_t cnt; 2644 int i; 2645 2646 cnt = hlist->ah_cnt; 2647 for (i = 0; i < cnt; i++) { 2648 hentry = &hlist->ah_list[i]; 2649 if (smb_ads_ldap_ping(hentry) == 0) 2650 return (hentry); 2651 } 2652 2653 return (NULL); 2654 } 2655 2656 /* 2657 * smb_ads_dc_compare 2658 * 2659 * Comparision function for sorting host entries (SRV records of DC) via qsort. 2660 * RFC 2052/2782 are taken as reference, while implementing this algorithm. 2661 * 2662 * Domain Controllers(DCs) with lowest priority in their SRV DNS records 2663 * are selected first. If they have equal priorities, then DC with highest 2664 * weight in its SRV DNS record is selected. If the priority and weight are 2665 * both equal, then the DC at the top of the list is selected. 2666 */ 2667 static int 2668 smb_ads_dc_compare(const void *p, const void *q) 2669 { 2670 smb_ads_host_info_t *h1 = (smb_ads_host_info_t *)p; 2671 smb_ads_host_info_t *h2 = (smb_ads_host_info_t *)q; 2672 2673 if (h1->priority < h2->priority) 2674 return (-1); 2675 if (h1->priority > h2->priority) 2676 return (1); 2677 2678 /* Priorities are equal */ 2679 if (h1->weight < h2->weight) 2680 return (1); 2681 if (h1->weight > h2->weight) 2682 return (-1); 2683 2684 return (0); 2685 } 2686 2687 /* 2688 * smb_ads_select_dc 2689 * 2690 * The list of ADS hosts returned by ADS lookup, is sorted by lowest priority 2691 * and highest weight. On this sorted list, following additional rules are 2692 * applied, to select a DC. 2693 * 2694 * - If there is a configured PDC and it's in the ADS list, 2695 * then return the DC, if it responds to ldap ping. 2696 * - If there is a DC in the same subnet, then return the DC, 2697 * if it responds to ldap ping. 2698 * - Else, return first DC that responds to ldap ping. 2699 * 2700 * A reference to the host entry from input host list is returned. 2701 * 2702 * Returns NULL on error. 2703 */ 2704 static smb_ads_host_info_t * 2705 smb_ads_select_dc(smb_ads_host_list_t *hlist) 2706 { 2707 smb_ads_host_info_t *hentry = NULL; 2708 2709 if (hlist->ah_cnt == 0) 2710 return (NULL); 2711 2712 if (hlist->ah_cnt == 1) { 2713 hentry = hlist->ah_list; 2714 if (smb_ads_ldap_ping(hentry) == 0) 2715 return (hentry); 2716 } 2717 2718 /* Sort the list by priority and weight */ 2719 qsort(hlist->ah_list, hlist->ah_cnt, 2720 sizeof (smb_ads_host_info_t), smb_ads_dc_compare); 2721 2722 if ((hentry = smb_ads_select_pdc(hlist)) != NULL) 2723 return (hentry); 2724 2725 if ((hentry = smb_ads_select_dcfromsubnet(hlist)) != NULL) 2726 return (hentry); 2727 2728 if ((hentry = smb_ads_select_dcfromlist(hlist)) != NULL) 2729 return (hentry); 2730 2731 return (NULL); 2732 } 2733 2734 /* 2735 * smb_ads_lookup_msdcs 2736 * 2737 * If server argument is set, try to locate the specified DC. 2738 * If it is set to empty string, locate any DCs in the specified domain. 2739 * Returns the discovered DC via buf. 2740 * 2741 * fqdn - fully-qualified domain name 2742 * server - fully-qualifed hostname of a DC 2743 * buf - the hostname of the discovered DC 2744 */ 2745 boolean_t 2746 smb_ads_lookup_msdcs(char *fqdn, char *server, char *buf, uint32_t buflen) 2747 { 2748 smb_ads_host_info_t *hinfo = NULL; 2749 char *p; 2750 char *sought_host; 2751 char ipstr[INET6_ADDRSTRLEN]; 2752 2753 if (!fqdn || !buf) 2754 return (B_FALSE); 2755 2756 *buf = '\0'; 2757 sought_host = (*server == 0 ? NULL : server); 2758 if ((hinfo = smb_ads_find_host(fqdn, sought_host)) == NULL) 2759 return (B_FALSE); 2760 2761 smb_tracef("msdcsLookupADS: %s [%s]", hinfo->name, 2762 smb_inet_ntop(&hinfo->ipaddr, ipstr, 2763 SMB_IPSTRLEN(hinfo->ipaddr.a_family))); 2764 2765 (void) strlcpy(buf, hinfo->name, buflen); 2766 /* 2767 * Remove the domain extension 2768 */ 2769 if ((p = strchr(buf, '.')) != 0) 2770 *p = '\0'; 2771 2772 return (B_TRUE); 2773 } 2774