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