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