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