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