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 ah->domain_dn = smb_ads_convert_domain(domain); 1168 if (ah->domain_dn == NULL) { 1169 smb_ads_close(ah); 1170 free(ads_host); 1171 return (NULL); 1172 } 1173 1174 ah->hostname = strdup(ads_host->name); 1175 if (ah->hostname == NULL) { 1176 smb_ads_close(ah); 1177 free(ads_host); 1178 return (NULL); 1179 } 1180 (void) mutex_lock(&smb_ads_cfg.c_mtx); 1181 if (*smb_ads_cfg.c_site != '\0') { 1182 if ((ah->site = strdup(smb_ads_cfg.c_site)) == NULL) { 1183 smb_ads_close(ah); 1184 (void) mutex_unlock(&smb_ads_cfg.c_mtx); 1185 free(ads_host); 1186 return (NULL); 1187 } 1188 } else { 1189 ah->site = NULL; 1190 } 1191 (void) mutex_unlock(&smb_ads_cfg.c_mtx); 1192 1193 rc = ldap_sasl_interactive_bind_s(ah->ld, "", "GSSAPI", NULL, NULL, 1194 LDAP_SASL_INTERACTIVE, &smb_ads_saslcallback, NULL); 1195 if (rc != LDAP_SUCCESS) { 1196 syslog(LOG_ERR, "ldal_sasl_interactive_bind_s failed (%s)", 1197 ldap_err2string(rc)); 1198 smb_ads_close(ah); 1199 free(ads_host); 1200 return (NULL); 1201 } 1202 1203 free(ads_host); 1204 return (ah); 1205 } 1206 1207 /* 1208 * smb_ads_close 1209 * Close connection to ADS server and free memory allocated for ADS handle. 1210 * LDAP unbind is called here. 1211 * Parameters: 1212 * ah: handle to ADS server 1213 * Returns: 1214 * void 1215 */ 1216 void 1217 smb_ads_close(smb_ads_handle_t *ah) 1218 { 1219 if (ah == NULL) 1220 return; 1221 /* close and free connection resources */ 1222 if (ah->ld) 1223 (void) ldap_unbind(ah->ld); 1224 1225 free(ah->domain); 1226 free(ah->domain_dn); 1227 free(ah->hostname); 1228 free(ah->site); 1229 free(ah); 1230 } 1231 1232 /* 1233 * smb_ads_alloc_attr 1234 * 1235 * Since the attrs is a null-terminated array, all elements 1236 * in the array (except the last one) will point to allocated 1237 * memory. 1238 */ 1239 static int 1240 smb_ads_alloc_attr(LDAPMod *attrs[], int num) 1241 { 1242 int i; 1243 1244 bzero(attrs, num * sizeof (LDAPMod *)); 1245 for (i = 0; i < (num - 1); i++) { 1246 attrs[i] = (LDAPMod *)malloc(sizeof (LDAPMod)); 1247 if (attrs[i] == NULL) { 1248 smb_ads_free_attr(attrs); 1249 return (-1); 1250 } 1251 } 1252 1253 return (0); 1254 } 1255 1256 /* 1257 * smb_ads_free_attr 1258 * Free memory allocated when publishing a share. 1259 * Parameters: 1260 * attrs: an array of LDAPMod pointers 1261 * Returns: 1262 * None 1263 */ 1264 static void 1265 smb_ads_free_attr(LDAPMod *attrs[]) 1266 { 1267 int i; 1268 for (i = 0; attrs[i]; i++) { 1269 free(attrs[i]); 1270 } 1271 } 1272 1273 /* 1274 * smb_ads_get_spnset 1275 * 1276 * Derives the core set of SPNs based on the FQHN. 1277 * The spn_set is a null-terminated array of char pointers. 1278 * 1279 * Returns 0 upon success. Otherwise, returns -1. 1280 */ 1281 static int 1282 smb_ads_get_spnset(char *fqhost, char **spn_set) 1283 { 1284 int i; 1285 1286 bzero(spn_set, (SMBKRB5_SPN_IDX_MAX + 1) * sizeof (char *)); 1287 for (i = 0; i < SMBKRB5_SPN_IDX_MAX; i++) { 1288 if ((spn_set[i] = smb_krb5_get_spn(i, fqhost)) == NULL) { 1289 smb_ads_free_spnset(spn_set); 1290 return (-1); 1291 } 1292 } 1293 1294 return (0); 1295 } 1296 1297 /* 1298 * smb_ads_free_spnset 1299 * 1300 * Free the memory allocated for the set of SPNs. 1301 */ 1302 static void 1303 smb_ads_free_spnset(char **spn_set) 1304 { 1305 int i; 1306 for (i = 0; spn_set[i]; i++) 1307 free(spn_set[i]); 1308 } 1309 1310 /* 1311 * Returns share DN in an allocated buffer. The format of the DN is 1312 * cn=<sharename>,<container RDNs>,<domain DN> 1313 * 1314 * If the domain DN is not included in the container parameter, 1315 * then it will be appended to create the share DN. 1316 * 1317 * The caller must free the allocated buffer. 1318 */ 1319 static char * 1320 smb_ads_get_sharedn(const char *sharename, const char *container, 1321 const char *domain_dn) 1322 { 1323 char *share_dn; 1324 int rc, offset, container_len, domain_len; 1325 boolean_t append_domain = B_TRUE; 1326 1327 container_len = strlen(container); 1328 domain_len = strlen(domain_dn); 1329 1330 if (container_len >= domain_len) { 1331 1332 /* offset to last domain_len characters */ 1333 offset = container_len - domain_len; 1334 1335 if (smb_strcasecmp(container + offset, 1336 domain_dn, domain_len) == 0) 1337 append_domain = B_FALSE; 1338 } 1339 1340 if (append_domain) 1341 rc = asprintf(&share_dn, "cn=%s,%s,%s", sharename, 1342 container, domain_dn); 1343 else 1344 rc = asprintf(&share_dn, "cn=%s,%s", sharename, 1345 container); 1346 1347 return ((rc == -1) ? NULL : share_dn); 1348 } 1349 1350 /* 1351 * smb_ads_add_share 1352 * Call by smb_ads_publish_share to create share object in ADS. 1353 * This routine specifies the attributes of an ADS LDAP share object. The first 1354 * attribute and values define the type of ADS object, the share object. The 1355 * second attribute and value define the UNC of the share data for the share 1356 * object. The LDAP synchronous add command is used to add the object into ADS. 1357 * The container location to add the object needs to specified. 1358 * Parameters: 1359 * ah : handle to ADS server 1360 * adsShareName: name of share object to be created in ADS 1361 * shareUNC : share name on NetForce 1362 * adsContainer: location in ADS to create share object 1363 * 1364 * Returns: 1365 * -1 : error 1366 * 0 : success 1367 */ 1368 int 1369 smb_ads_add_share(smb_ads_handle_t *ah, const char *adsShareName, 1370 const char *unc_name, const char *adsContainer) 1371 { 1372 LDAPMod *attrs[SMB_ADS_SHARE_NUM_ATTR]; 1373 int j = 0; 1374 char *share_dn; 1375 int ret; 1376 char *unc_names[] = {(char *)unc_name, NULL}; 1377 1378 if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer, 1379 ah->domain_dn)) == NULL) 1380 return (-1); 1381 1382 if (smb_ads_alloc_attr(attrs, SMB_ADS_SHARE_NUM_ATTR) != 0) { 1383 free(share_dn); 1384 return (-1); 1385 } 1386 1387 attrs[j]->mod_op = LDAP_MOD_ADD; 1388 attrs[j]->mod_type = "objectClass"; 1389 attrs[j]->mod_values = smb_ads_share_objcls; 1390 1391 attrs[++j]->mod_op = LDAP_MOD_ADD; 1392 attrs[j]->mod_type = "uNCName"; 1393 attrs[j]->mod_values = unc_names; 1394 1395 if ((ret = ldap_add_s(ah->ld, share_dn, attrs)) != LDAP_SUCCESS) { 1396 smb_tracef("%s: ldap_add: %s", share_dn, ldap_err2string(ret)); 1397 smb_ads_free_attr(attrs); 1398 free(share_dn); 1399 return (ret); 1400 } 1401 free(share_dn); 1402 smb_ads_free_attr(attrs); 1403 1404 return (0); 1405 } 1406 1407 /* 1408 * smb_ads_del_share 1409 * Call by smb_ads_remove_share to remove share object from ADS. The container 1410 * location to remove the object needs to specified. The LDAP synchronous 1411 * delete command is used. 1412 * Parameters: 1413 * ah : handle to ADS server 1414 * adsShareName: name of share object in ADS to be removed 1415 * adsContainer: location of share object in ADS 1416 * Returns: 1417 * -1 : error 1418 * 0 : success 1419 */ 1420 static int 1421 smb_ads_del_share(smb_ads_handle_t *ah, const char *adsShareName, 1422 const char *adsContainer) 1423 { 1424 char *share_dn; 1425 int ret; 1426 1427 if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer, 1428 ah->domain_dn)) == NULL) 1429 return (-1); 1430 1431 if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) { 1432 smb_tracef("ldap_delete: %s", ldap_err2string(ret)); 1433 free(share_dn); 1434 return (-1); 1435 } 1436 free(share_dn); 1437 1438 return (0); 1439 } 1440 1441 1442 /* 1443 * smb_ads_escape_search_filter_chars 1444 * 1445 * This routine will escape the special characters found in a string 1446 * that will later be passed to the ldap search filter. 1447 * 1448 * RFC 1960 - A String Representation of LDAP Search Filters 1449 * 3. String Search Filter Definition 1450 * If a value must contain one of the characters '*' OR '(' OR ')', 1451 * these characters 1452 * should be escaped by preceding them with the backslash '\' character. 1453 * 1454 * RFC 2252 - LDAP Attribute Syntax Definitions 1455 * a backslash quoting mechanism is used to escape 1456 * the following separator symbol character (such as "'", "$" or "#") if 1457 * it should occur in that string. 1458 */ 1459 static int 1460 smb_ads_escape_search_filter_chars(const char *src, char *dst) 1461 { 1462 int avail = SMB_ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */ 1463 1464 if (src == NULL || dst == NULL) 1465 return (-1); 1466 1467 while (*src) { 1468 if (!avail) { 1469 *dst = 0; 1470 return (-1); 1471 } 1472 1473 switch (*src) { 1474 case '\\': 1475 case '\'': 1476 case '$': 1477 case '#': 1478 case '*': 1479 case '(': 1480 case ')': 1481 *dst++ = '\\'; 1482 avail--; 1483 /* fall through */ 1484 1485 default: 1486 *dst++ = *src++; 1487 avail--; 1488 } 1489 } 1490 1491 *dst = 0; 1492 1493 return (0); 1494 } 1495 1496 /* 1497 * smb_ads_lookup_share 1498 * The search filter is set to search for a specific share name in the 1499 * specified ADS container. The LDSAP synchronous search command is used. 1500 * Parameters: 1501 * ah : handle to ADS server 1502 * adsShareName: name of share object in ADS to be searched 1503 * adsContainer: location of share object in ADS 1504 * Returns: 1505 * -1 : error 1506 * 0 : not found 1507 * 1 : found 1508 */ 1509 int 1510 smb_ads_lookup_share(smb_ads_handle_t *ah, const char *adsShareName, 1511 const char *adsContainer, char *unc_name) 1512 { 1513 char *attrs[4], filter[SMB_ADS_MAXBUFLEN]; 1514 char *share_dn; 1515 int ret; 1516 LDAPMessage *res; 1517 char tmpbuf[SMB_ADS_MAXBUFLEN]; 1518 1519 if (adsShareName == NULL || adsContainer == NULL) 1520 return (-1); 1521 1522 if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer, 1523 ah->domain_dn)) == NULL) 1524 return (-1); 1525 1526 res = NULL; 1527 attrs[0] = "cn"; 1528 attrs[1] = "objectClass"; 1529 attrs[2] = "uNCName"; 1530 attrs[3] = NULL; 1531 1532 if (smb_ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) { 1533 free(share_dn); 1534 return (-1); 1535 } 1536 1537 (void) snprintf(filter, sizeof (filter), 1538 "(&(objectClass=volume)(uNCName=%s))", tmpbuf); 1539 1540 if ((ret = ldap_search_s(ah->ld, share_dn, 1541 LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) { 1542 if (ret != LDAP_NO_SUCH_OBJECT) 1543 smb_tracef("%s: ldap_search: %s", share_dn, 1544 ldap_err2string(ret)); 1545 1546 (void) ldap_msgfree(res); 1547 free(share_dn); 1548 return (0); 1549 } 1550 1551 (void) free(share_dn); 1552 1553 /* no match is found */ 1554 if (ldap_count_entries(ah->ld, res) == 0) { 1555 (void) ldap_msgfree(res); 1556 return (0); 1557 } 1558 1559 /* free the search results */ 1560 (void) ldap_msgfree(res); 1561 1562 return (1); 1563 } 1564 1565 /* 1566 * smb_ads_publish_share 1567 * Publish share into ADS. If a share name already exist in ADS in the same 1568 * container then the existing share object is removed before adding the new 1569 * share object. 1570 * Parameters: 1571 * ah : handle return from smb_ads_open 1572 * adsShareName: name of share to be added to ADS directory 1573 * shareUNC : name of share on client, can be NULL to use the same name 1574 * as adsShareName 1575 * adsContainer: location for share to be added in ADS directory, ie 1576 * ou=share_folder 1577 * uncType : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR 1578 * to use host ip addr for UNC. 1579 * Returns: 1580 * -1 : error 1581 * 0 : success 1582 */ 1583 int 1584 smb_ads_publish_share(smb_ads_handle_t *ah, const char *adsShareName, 1585 const char *shareUNC, const char *adsContainer, const char *hostname) 1586 { 1587 int ret; 1588 char unc_name[SMB_ADS_MAXBUFLEN]; 1589 1590 if (adsShareName == NULL || adsContainer == NULL) 1591 return (-1); 1592 1593 if (shareUNC == 0 || *shareUNC == 0) 1594 shareUNC = adsShareName; 1595 1596 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1597 hostname, shareUNC) < 0) 1598 return (-1); 1599 1600 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1601 1602 switch (ret) { 1603 case 1: 1604 (void) smb_ads_del_share(ah, adsShareName, adsContainer); 1605 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1606 adsContainer); 1607 break; 1608 1609 case 0: 1610 ret = smb_ads_add_share(ah, adsShareName, unc_name, 1611 adsContainer); 1612 if (ret == LDAP_ALREADY_EXISTS) 1613 ret = -1; 1614 1615 break; 1616 1617 case -1: 1618 default: 1619 /* return with error code */ 1620 ret = -1; 1621 } 1622 1623 return (ret); 1624 } 1625 1626 /* 1627 * smb_ads_remove_share 1628 * Remove share from ADS. A search is done first before explicitly removing 1629 * the share. 1630 * Parameters: 1631 * ah : handle return from smb_ads_open 1632 * adsShareName: name of share to be removed from ADS directory 1633 * adsContainer: location for share to be removed from ADS directory, ie 1634 * ou=share_folder 1635 * Returns: 1636 * -1 : error 1637 * 0 : success 1638 */ 1639 int 1640 smb_ads_remove_share(smb_ads_handle_t *ah, const char *adsShareName, 1641 const char *shareUNC, const char *adsContainer, const char *hostname) 1642 { 1643 int ret; 1644 char unc_name[SMB_ADS_MAXBUFLEN]; 1645 1646 if (adsShareName == NULL || adsContainer == NULL) 1647 return (-1); 1648 if (shareUNC == 0 || *shareUNC == 0) 1649 shareUNC = adsShareName; 1650 1651 if (smb_ads_build_unc_name(unc_name, sizeof (unc_name), 1652 hostname, shareUNC) < 0) 1653 return (-1); 1654 1655 ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name); 1656 if (ret == 0) 1657 return (0); 1658 if (ret == -1) 1659 return (-1); 1660 1661 return (smb_ads_del_share(ah, adsShareName, adsContainer)); 1662 } 1663 1664 /* 1665 * smb_ads_get_default_comp_container_dn 1666 * 1667 * Build the distinguished name for the default computer conatiner (i.e. the 1668 * pre-defined Computers container). 1669 */ 1670 static void 1671 smb_ads_get_default_comp_container_dn(smb_ads_handle_t *ah, char *buf, 1672 size_t buflen) 1673 { 1674 (void) snprintf(buf, buflen, "cn=%s,%s", SMB_ADS_COMPUTERS_CN, 1675 ah->domain_dn); 1676 } 1677 1678 /* 1679 * smb_ads_get_default_comp_dn 1680 * 1681 * Build the distinguished name for this system. 1682 */ 1683 static void 1684 smb_ads_get_default_comp_dn(smb_ads_handle_t *ah, char *buf, size_t buflen) 1685 { 1686 char nbname[NETBIOS_NAME_SZ]; 1687 char container_dn[SMB_ADS_DN_MAX]; 1688 1689 (void) smb_getnetbiosname(nbname, sizeof (nbname)); 1690 smb_ads_get_default_comp_container_dn(ah, container_dn, SMB_ADS_DN_MAX); 1691 (void) snprintf(buf, buflen, "cn=%s,%s", nbname, container_dn); 1692 } 1693 1694 /* 1695 * smb_ads_add_computer 1696 * 1697 * Returns 0 upon success. Otherwise, returns -1. 1698 */ 1699 static int 1700 smb_ads_add_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1701 { 1702 return (smb_ads_computer_op(ah, LDAP_MOD_ADD, dclevel, dn)); 1703 } 1704 1705 /* 1706 * smb_ads_modify_computer 1707 * 1708 * Returns 0 upon success. Otherwise, returns -1. 1709 */ 1710 static int 1711 smb_ads_modify_computer(smb_ads_handle_t *ah, int dclevel, char *dn) 1712 { 1713 return (smb_ads_computer_op(ah, LDAP_MOD_REPLACE, dclevel, dn)); 1714 } 1715 1716 /* 1717 * smb_ads_get_dc_level 1718 * 1719 * Returns the functional level of the DC upon success. 1720 * Otherwise, -1 is returned. 1721 */ 1722 static int 1723 smb_ads_get_dc_level(smb_ads_handle_t *ah) 1724 { 1725 LDAPMessage *res, *entry; 1726 char *attr[2]; 1727 char **vals; 1728 int rc = -1; 1729 1730 res = NULL; 1731 attr[0] = SMB_ADS_ATTR_DCLEVEL; 1732 attr[1] = NULL; 1733 if (ldap_search_s(ah->ld, "", LDAP_SCOPE_BASE, NULL, attr, 1734 0, &res) != LDAP_SUCCESS) { 1735 (void) ldap_msgfree(res); 1736 return (-1); 1737 } 1738 1739 /* no match for the specified attribute is found */ 1740 if (ldap_count_entries(ah->ld, res) == 0) { 1741 (void) ldap_msgfree(res); 1742 return (-1); 1743 } 1744 1745 entry = ldap_first_entry(ah->ld, res); 1746 if (entry) { 1747 if ((vals = ldap_get_values(ah->ld, entry, 1748 SMB_ADS_ATTR_DCLEVEL)) == NULL) { 1749 /* 1750 * Observed the values aren't populated 1751 * by the Windows 2000 server. 1752 */ 1753 (void) ldap_msgfree(res); 1754 return (SMB_ADS_DCLEVEL_W2K); 1755 } 1756 1757 if (vals[0] != NULL) 1758 rc = atoi(vals[0]); 1759 1760 ldap_value_free(vals); 1761 } 1762 1763 (void) ldap_msgfree(res); 1764 return (rc); 1765 } 1766 1767 static int 1768 smb_ads_getfqhostname(smb_ads_handle_t *ah, char *fqhost, int len) 1769 { 1770 if (smb_gethostname(fqhost, len, 0) != 0) 1771 return (-1); 1772 1773 (void) snprintf(fqhost, len, "%s.%s", fqhost, 1774 ah->domain); 1775 1776 return (0); 1777 } 1778 1779 static int 1780 smb_ads_computer_op(smb_ads_handle_t *ah, int op, int dclevel, char *dn) 1781 { 1782 LDAPMod *attrs[SMB_ADS_COMPUTER_NUM_ATTR]; 1783 char *sam_val[2], *usr_val[2]; 1784 char *spn_set[SMBKRB5_SPN_IDX_MAX + 1], *ctl_val[2], *fqh_val[2]; 1785 char *encrypt_val[2]; 1786 int j = -1; 1787 int ret, usrctl_flags = 0; 1788 char sam_acct[SMB_SAMACCT_MAXLEN]; 1789 char fqhost[MAXHOSTNAMELEN]; 1790 char *user_principal; 1791 char usrctl_buf[16]; 1792 char encrypt_buf[16]; 1793 int max; 1794 1795 if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) 1796 return (-1); 1797 1798 if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN)) 1799 return (-1); 1800 1801 if (smb_ads_get_spnset(fqhost, spn_set) != 0) 1802 return (-1); 1803 1804 user_principal = smb_krb5_get_upn(spn_set[SMBKRB5_SPN_IDX_HOST], 1805 ah->domain); 1806 1807 if (user_principal == NULL) { 1808 smb_ads_free_spnset(spn_set); 1809 return (-1); 1810 } 1811 1812 max = (SMB_ADS_COMPUTER_NUM_ATTR - ((op != LDAP_MOD_ADD) ? 1 : 0)) 1813 - (dclevel >= SMB_ADS_DCLEVEL_W2K8 ? 0 : 1); 1814 1815 if (smb_ads_alloc_attr(attrs, max) != 0) { 1816 free(user_principal); 1817 smb_ads_free_spnset(spn_set); 1818 return (-1); 1819 } 1820 1821 /* objectClass attribute is not modifiable. */ 1822 if (op == LDAP_MOD_ADD) { 1823 attrs[++j]->mod_op = op; 1824 attrs[j]->mod_type = "objectClass"; 1825 attrs[j]->mod_values = smb_ads_computer_objcls; 1826 } 1827 1828 attrs[++j]->mod_op = op; 1829 attrs[j]->mod_type = SMB_ADS_ATTR_SAMACCT; 1830 sam_val[0] = sam_acct; 1831 sam_val[1] = 0; 1832 attrs[j]->mod_values = sam_val; 1833 1834 attrs[++j]->mod_op = op; 1835 attrs[j]->mod_type = SMB_ADS_ATTR_UPN; 1836 usr_val[0] = user_principal; 1837 usr_val[1] = 0; 1838 attrs[j]->mod_values = usr_val; 1839 1840 attrs[++j]->mod_op = op; 1841 attrs[j]->mod_type = SMB_ADS_ATTR_SPN; 1842 attrs[j]->mod_values = spn_set; 1843 1844 attrs[++j]->mod_op = op; 1845 attrs[j]->mod_type = SMB_ADS_ATTR_CTL; 1846 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 1847 SMB_ADS_USER_ACCT_CTL_PASSWD_NOTREQD | 1848 SMB_ADS_USER_ACCT_CTL_ACCOUNTDISABLE); 1849 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags); 1850 ctl_val[0] = usrctl_buf; 1851 ctl_val[1] = 0; 1852 attrs[j]->mod_values = ctl_val; 1853 1854 attrs[++j]->mod_op = op; 1855 attrs[j]->mod_type = SMB_ADS_ATTR_DNSHOST; 1856 fqh_val[0] = fqhost; 1857 fqh_val[1] = 0; 1858 attrs[j]->mod_values = fqh_val; 1859 1860 /* enctypes support starting in Windows Server 2008 */ 1861 if (dclevel > SMB_ADS_DCLEVEL_W2K3) { 1862 attrs[++j]->mod_op = op; 1863 attrs[j]->mod_type = SMB_ADS_ATTR_ENCTYPES; 1864 (void) snprintf(encrypt_buf, sizeof (encrypt_buf), "%d", 1865 SMB_ADS_ENC_AES256 + SMB_ADS_ENC_AES128 + SMB_ADS_ENC_RC4 + 1866 SMB_ADS_ENC_DES_MD5 + SMB_ADS_ENC_DES_CRC); 1867 encrypt_val[0] = encrypt_buf; 1868 encrypt_val[1] = 0; 1869 attrs[j]->mod_values = encrypt_val; 1870 } 1871 1872 switch (op) { 1873 case LDAP_MOD_ADD: 1874 if ((ret = ldap_add_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1875 syslog(LOG_NOTICE, "ldap_add: %s", 1876 ldap_err2string(ret)); 1877 ret = -1; 1878 } 1879 break; 1880 1881 case LDAP_MOD_REPLACE: 1882 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 1883 syslog(LOG_NOTICE, "ldap_modify: %s", 1884 ldap_err2string(ret)); 1885 ret = -1; 1886 } 1887 break; 1888 1889 default: 1890 ret = -1; 1891 1892 } 1893 1894 smb_ads_free_attr(attrs); 1895 free(user_principal); 1896 smb_ads_free_spnset(spn_set); 1897 1898 return (ret); 1899 } 1900 1901 /* 1902 * Delete an ADS computer account. 1903 */ 1904 static void 1905 smb_ads_del_computer(smb_ads_handle_t *ah, char *dn) 1906 { 1907 int rc; 1908 1909 if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS) 1910 smb_tracef("ldap_delete: %s", ldap_err2string(rc)); 1911 } 1912 1913 /* 1914 * Gets the value of the given attribute. 1915 */ 1916 static smb_ads_qstat_t 1917 smb_ads_getattr(LDAP *ld, LDAPMessage *entry, smb_ads_avpair_t *avpair) 1918 { 1919 char **vals; 1920 smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND; 1921 1922 assert(avpair); 1923 avpair->avp_val = NULL; 1924 vals = ldap_get_values(ld, entry, avpair->avp_attr); 1925 if (!vals) 1926 return (SMB_ADS_STAT_NOT_FOUND); 1927 1928 if (!vals[0]) { 1929 ldap_value_free(vals); 1930 return (SMB_ADS_STAT_NOT_FOUND); 1931 } 1932 1933 avpair->avp_val = strdup(vals[0]); 1934 if (!avpair->avp_val) 1935 rc = SMB_ADS_STAT_ERR; 1936 1937 ldap_value_free(vals); 1938 return (rc); 1939 } 1940 1941 /* 1942 * Process query's result. 1943 */ 1944 static smb_ads_qstat_t 1945 smb_ads_get_qstat(smb_ads_handle_t *ah, LDAPMessage *res, 1946 smb_ads_avpair_t *avpair) 1947 { 1948 char fqhost[MAXHOSTNAMELEN]; 1949 smb_ads_avpair_t dnshost_avp; 1950 smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND; 1951 LDAPMessage *entry; 1952 1953 if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN)) 1954 return (SMB_ADS_STAT_ERR); 1955 1956 if (ldap_count_entries(ah->ld, res) == 0) 1957 return (SMB_ADS_STAT_NOT_FOUND); 1958 1959 if ((entry = ldap_first_entry(ah->ld, res)) == NULL) 1960 return (SMB_ADS_STAT_ERR); 1961 1962 dnshost_avp.avp_attr = SMB_ADS_ATTR_DNSHOST; 1963 rc = smb_ads_getattr(ah->ld, entry, &dnshost_avp); 1964 1965 switch (rc) { 1966 case SMB_ADS_STAT_FOUND: 1967 /* 1968 * Returns SMB_ADS_STAT_DUP to avoid overwriting 1969 * the computer account of another system whose 1970 * NetBIOS name collides with that of the current 1971 * system. 1972 */ 1973 if (strcasecmp(dnshost_avp.avp_val, fqhost)) 1974 rc = SMB_ADS_STAT_DUP; 1975 1976 free(dnshost_avp.avp_val); 1977 break; 1978 1979 case SMB_ADS_STAT_NOT_FOUND: 1980 /* 1981 * Pre-created computer account doesn't have 1982 * the dNSHostname attribute. It's been observed 1983 * that the dNSHostname attribute is only set after 1984 * a successful domain join. 1985 * Returns SMB_ADS_STAT_FOUND as the account is 1986 * pre-created for the current system. 1987 */ 1988 rc = SMB_ADS_STAT_FOUND; 1989 break; 1990 1991 default: 1992 break; 1993 } 1994 1995 if (rc != SMB_ADS_STAT_FOUND) 1996 return (rc); 1997 1998 if (avpair) 1999 rc = smb_ads_getattr(ah->ld, entry, avpair); 2000 2001 return (rc); 2002 2003 } 2004 2005 /* 2006 * smb_ads_lookup_computer_n_attr 2007 * 2008 * If avpair is NULL, checks the status of the specified computer account. 2009 * Otherwise, looks up the value of the specified computer account's attribute. 2010 * If found, the value field of the avpair will be allocated and set. The 2011 * caller should free the allocated buffer. 2012 * 2013 * Return: 2014 * SMB_ADS_STAT_FOUND - if both the computer and the specified attribute is 2015 * found. 2016 * SMB_ADS_STAT_NOT_FOUND - if either the computer or the specified attribute 2017 * is not found. 2018 * SMB_ADS_STAT_DUP - if the computer account is already used by other systems 2019 * in the AD. This could happen if the hostname of multiple 2020 * systems resolved to the same NetBIOS name. 2021 * SMB_ADS_STAT_ERR - any failure. 2022 */ 2023 static smb_ads_qstat_t 2024 smb_ads_lookup_computer_n_attr(smb_ads_handle_t *ah, smb_ads_avpair_t *avpair, 2025 int scope, char *dn) 2026 { 2027 char *attrs[3], filter[SMB_ADS_MAXBUFLEN]; 2028 LDAPMessage *res; 2029 char sam_acct[SMB_SAMACCT_MAXLEN], sam_acct2[SMB_SAMACCT_MAXLEN]; 2030 smb_ads_qstat_t rc; 2031 2032 if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0) 2033 return (SMB_ADS_STAT_ERR); 2034 2035 res = NULL; 2036 attrs[0] = SMB_ADS_ATTR_DNSHOST; 2037 attrs[1] = NULL; 2038 attrs[2] = NULL; 2039 2040 if (avpair) { 2041 if (!avpair->avp_attr) 2042 return (SMB_ADS_STAT_ERR); 2043 2044 attrs[1] = avpair->avp_attr; 2045 } 2046 2047 if (smb_ads_escape_search_filter_chars(sam_acct, sam_acct2) != 0) 2048 return (SMB_ADS_STAT_ERR); 2049 2050 (void) snprintf(filter, sizeof (filter), 2051 "(&(objectClass=computer)(%s=%s))", SMB_ADS_ATTR_SAMACCT, 2052 sam_acct2); 2053 2054 if (ldap_search_s(ah->ld, dn, scope, filter, attrs, 0, 2055 &res) != LDAP_SUCCESS) { 2056 (void) ldap_msgfree(res); 2057 return (SMB_ADS_STAT_NOT_FOUND); 2058 } 2059 2060 rc = smb_ads_get_qstat(ah, res, avpair); 2061 /* free the search results */ 2062 (void) ldap_msgfree(res); 2063 return (rc); 2064 } 2065 2066 /* 2067 * smb_ads_find_computer 2068 * 2069 * Starts by searching for the system's AD computer object in the default 2070 * container (i.e. cn=Computers). If not found, searches the entire directory. 2071 * If found, 'dn' will be set to the distinguished name of the system's AD 2072 * computer object. 2073 */ 2074 static smb_ads_qstat_t 2075 smb_ads_find_computer(smb_ads_handle_t *ah, char *dn) 2076 { 2077 smb_ads_qstat_t stat; 2078 smb_ads_avpair_t avpair; 2079 2080 avpair.avp_attr = SMB_ADS_ATTR_DN; 2081 smb_ads_get_default_comp_container_dn(ah, dn, SMB_ADS_DN_MAX); 2082 stat = smb_ads_lookup_computer_n_attr(ah, &avpair, LDAP_SCOPE_ONELEVEL, 2083 dn); 2084 2085 if (stat == SMB_ADS_STAT_NOT_FOUND) { 2086 (void) strlcpy(dn, ah->domain_dn, SMB_ADS_DN_MAX); 2087 stat = smb_ads_lookup_computer_n_attr(ah, &avpair, 2088 LDAP_SCOPE_SUBTREE, dn); 2089 } 2090 2091 if (stat == SMB_ADS_STAT_FOUND) { 2092 (void) strlcpy(dn, avpair.avp_val, SMB_ADS_DN_MAX); 2093 free(avpair.avp_val); 2094 } 2095 2096 return (stat); 2097 } 2098 2099 /* 2100 * smb_ads_update_computer_cntrl_attr 2101 * 2102 * Modify the user account control attribute of an existing computer 2103 * object on AD. 2104 * 2105 * Returns LDAP error code. 2106 */ 2107 static int 2108 smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *ah, int flags, char *dn) 2109 { 2110 LDAPMod *attrs[2]; 2111 char *ctl_val[2]; 2112 int ret = 0; 2113 char usrctl_buf[16]; 2114 2115 if (smb_ads_alloc_attr(attrs, sizeof (attrs) / sizeof (LDAPMod *)) != 0) 2116 return (LDAP_NO_MEMORY); 2117 2118 attrs[0]->mod_op = LDAP_MOD_REPLACE; 2119 attrs[0]->mod_type = SMB_ADS_ATTR_CTL; 2120 2121 (void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", flags); 2122 ctl_val[0] = usrctl_buf; 2123 ctl_val[1] = 0; 2124 attrs[0]->mod_values = ctl_val; 2125 if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) { 2126 syslog(LOG_NOTICE, "ldap_modify: %s", ldap_err2string(ret)); 2127 } 2128 2129 smb_ads_free_attr(attrs); 2130 return (ret); 2131 } 2132 2133 /* 2134 * smb_ads_lookup_computer_attr_kvno 2135 * 2136 * Lookup the value of the Kerberos version number attribute of the computer 2137 * account. 2138 */ 2139 static krb5_kvno 2140 smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *ah, char *dn) 2141 { 2142 smb_ads_avpair_t avpair; 2143 int kvno = 1; 2144 2145 avpair.avp_attr = SMB_ADS_ATTR_KVNO; 2146 if (smb_ads_lookup_computer_n_attr(ah, &avpair, 2147 LDAP_SCOPE_BASE, dn) == SMB_ADS_STAT_FOUND) { 2148 kvno = atoi(avpair.avp_val); 2149 free(avpair.avp_val); 2150 } 2151 2152 return (kvno); 2153 } 2154 2155 static int 2156 smb_ads_gen_machine_passwd(char *machine_passwd, size_t bufsz) 2157 { 2158 int i; 2159 size_t pwdlen; 2160 uint8_t *random_bytes; 2161 2162 errno = 0; 2163 if (machine_passwd == NULL || bufsz == 0) { 2164 errno = EINVAL; 2165 return (-1); 2166 } 2167 2168 pwdlen = bufsz - 1; 2169 random_bytes = calloc(1, pwdlen); 2170 if (random_bytes == NULL) 2171 return (-1); 2172 2173 if (pkcs11_get_random(random_bytes, pwdlen) != 0) { 2174 free(random_bytes); 2175 return (-1); 2176 } 2177 2178 for (i = 0; i < pwdlen; i++) 2179 machine_passwd[i] = (random_bytes[i] % SMB_ADS_PWD_CHAR_NUM) + 2180 SMB_ADS_PWD_CHAR_START; 2181 2182 machine_passwd[pwdlen] = 0; 2183 bzero(random_bytes, pwdlen); 2184 free(random_bytes); 2185 return (0); 2186 } 2187 2188 /* 2189 * smb_ads_join 2190 * 2191 * Besides the NT-4 style domain join (using MS-RPC), CIFS server also 2192 * provides the domain join using Kerberos Authentication, Keberos 2193 * Change & Set password, and LDAP protocols. Basically, AD join 2194 * operation would require the following tickets to be acquired for the 2195 * the user account that is provided for the domain join. 2196 * 2197 * 1) a Keberos TGT ticket, 2198 * 2) a ldap service ticket, and 2199 * 3) kadmin/changpw service ticket 2200 * 2201 * The ADS client first sends a ldap search request to find out whether 2202 * or not the workstation trust account already exists in the Active Directory. 2203 * The existing computer object for this workstation will be removed and 2204 * a new one will be added. The machine account password is randomly 2205 * generated and set for the newly created computer object using KPASSWD 2206 * protocol (See RFC 3244). Once the password is set, our ADS client 2207 * finalizes the machine account by modifying the user acount control 2208 * attribute of the computer object. Kerberos keys derived from the machine 2209 * account password will be stored locally in /etc/krb5/krb5.keytab file. 2210 * That would be needed while acquiring Kerberos TGT ticket for the host 2211 * principal after the domain join operation. 2212 */ 2213 smb_adjoin_status_t 2214 smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd, 2215 size_t len) 2216 { 2217 smb_ads_handle_t *ah = NULL; 2218 krb5_context ctx = NULL; 2219 krb5_principal krb5princs[SMBKRB5_SPN_IDX_MAX]; 2220 krb5_kvno kvno; 2221 boolean_t des_only, delete = B_TRUE; 2222 smb_adjoin_status_t rc = SMB_ADJOIN_SUCCESS; 2223 boolean_t new_acct; 2224 int dclevel, num, usrctl_flags = 0; 2225 smb_ads_qstat_t qstat; 2226 char dn[SMB_ADS_DN_MAX]; 2227 char *tmpfile; 2228 2229 /* 2230 * Call library functions that can be used to get 2231 * the list of encryption algorithms available on the system. 2232 * (similar to what 'encrypt -l' CLI does). For now, 2233 * unless someone has modified the configuration of the 2234 * cryptographic framework (very unlikely), the following is the 2235 * list of algorithms available on any system running Nevada 2236 * by default. 2237 */ 2238 krb5_enctype w2k8enctypes[] = {ENCTYPE_AES256_CTS_HMAC_SHA1_96, 2239 ENCTYPE_AES128_CTS_HMAC_SHA1_96, ENCTYPE_ARCFOUR_HMAC, 2240 ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 2241 }; 2242 2243 krb5_enctype pre_w2k8enctypes[] = {ENCTYPE_ARCFOUR_HMAC, 2244 ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 2245 }; 2246 2247 krb5_enctype *encptr; 2248 2249 if ((ah = smb_ads_open_main(domain, user, usr_passwd)) == NULL) { 2250 smb_ccache_remove(SMB_CCACHE_PATH); 2251 return (SMB_ADJOIN_ERR_GET_HANDLE); 2252 } 2253 2254 if (smb_ads_gen_machine_passwd(machine_passwd, len) != 0) { 2255 syslog(LOG_NOTICE, "machine password generation: %m"); 2256 smb_ads_close(ah); 2257 smb_ccache_remove(SMB_CCACHE_PATH); 2258 return (SMB_ADJOIN_ERR_GEN_PWD); 2259 } 2260 2261 if ((dclevel = smb_ads_get_dc_level(ah)) == -1) { 2262 smb_ads_close(ah); 2263 smb_ccache_remove(SMB_CCACHE_PATH); 2264 return (SMB_ADJOIN_ERR_GET_DCLEVEL); 2265 } 2266 2267 qstat = smb_ads_find_computer(ah, dn); 2268 switch (qstat) { 2269 case SMB_ADS_STAT_FOUND: 2270 new_acct = B_FALSE; 2271 if (smb_ads_modify_computer(ah, dclevel, dn) != 0) { 2272 smb_ads_close(ah); 2273 smb_ccache_remove(SMB_CCACHE_PATH); 2274 return (SMB_ADJOIN_ERR_MOD_TRUST_ACCT); 2275 } 2276 break; 2277 2278 case SMB_ADS_STAT_NOT_FOUND: 2279 new_acct = B_TRUE; 2280 smb_ads_get_default_comp_dn(ah, dn, SMB_ADS_DN_MAX); 2281 if (smb_ads_add_computer(ah, dclevel, dn) != 0) { 2282 smb_ads_close(ah); 2283 smb_ccache_remove(SMB_CCACHE_PATH); 2284 return (SMB_ADJOIN_ERR_ADD_TRUST_ACCT); 2285 } 2286 break; 2287 2288 default: 2289 if (qstat == SMB_ADS_STAT_DUP) 2290 rc = SMB_ADJOIN_ERR_DUP_TRUST_ACCT; 2291 else 2292 rc = SMB_ADJOIN_ERR_TRUST_ACCT; 2293 smb_ads_close(ah); 2294 smb_ccache_remove(SMB_CCACHE_PATH); 2295 return (rc); 2296 } 2297 2298 des_only = B_FALSE; 2299 2300 if (smb_krb5_ctx_init(&ctx) != 0) { 2301 rc = SMB_ADJOIN_ERR_INIT_KRB_CTX; 2302 goto adjoin_cleanup; 2303 } 2304 2305 if (smb_krb5_get_principals(ah->domain, ctx, krb5princs) != 0) { 2306 rc = SMB_ADJOIN_ERR_GET_SPNS; 2307 goto adjoin_cleanup; 2308 } 2309 2310 if (smb_krb5_setpwd(ctx, krb5princs[SMBKRB5_SPN_IDX_HOST], 2311 machine_passwd) != 0) { 2312 rc = SMB_ADJOIN_ERR_KSETPWD; 2313 goto adjoin_cleanup; 2314 } 2315 2316 kvno = smb_ads_lookup_computer_attr_kvno(ah, dn); 2317 2318 /* 2319 * Only members of Domain Admins and Enterprise Admins can set 2320 * the TRUSTED_FOR_DELEGATION userAccountControl flag. 2321 */ 2322 if (smb_ads_update_computer_cntrl_attr(ah, 2323 SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 2324 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION, dn) 2325 == LDAP_INSUFFICIENT_ACCESS) { 2326 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 2327 SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); 2328 2329 syslog(LOG_NOTICE, "Unable to set the " 2330 "TRUSTED_FOR_DELEGATION userAccountControl flag on " 2331 "the machine account in Active Directory. Please refer " 2332 "to the Troubleshooting guide for more information."); 2333 2334 } else { 2335 usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT | 2336 SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION | 2337 SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD); 2338 } 2339 2340 if (des_only) 2341 usrctl_flags |= SMB_ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY; 2342 2343 if (smb_ads_update_computer_cntrl_attr(ah, usrctl_flags, dn) 2344 != 0) { 2345 rc = SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR; 2346 goto adjoin_cleanup; 2347 } 2348 2349 if (dclevel >= SMB_ADS_DCLEVEL_W2K8) { 2350 num = sizeof (w2k8enctypes) / sizeof (krb5_enctype); 2351 encptr = w2k8enctypes; 2352 } else { 2353 num = sizeof (pre_w2k8enctypes) / sizeof (krb5_enctype); 2354 encptr = pre_w2k8enctypes; 2355 } 2356 2357 tmpfile = mktemp(SMBNS_KRB5_KEYTAB_TMP); 2358 if (tmpfile == NULL) 2359 tmpfile = SMBNS_KRB5_KEYTAB_TMP; 2360 2361 if (smb_krb5_add_keytab_entries(ctx, krb5princs, tmpfile, 2362 kvno, machine_passwd, encptr, num) != 0) { 2363 rc = SMB_ADJOIN_ERR_WRITE_KEYTAB; 2364 goto adjoin_cleanup; 2365 } 2366 2367 delete = B_FALSE; 2368 adjoin_cleanup: 2369 if (new_acct && delete) 2370 smb_ads_del_computer(ah, dn); 2371 2372 if (rc != SMB_ADJOIN_ERR_INIT_KRB_CTX) { 2373 if (rc != SMB_ADJOIN_ERR_GET_SPNS) 2374 smb_krb5_free_principals(ctx, krb5princs, 2375 SMBKRB5_SPN_IDX_MAX); 2376 smb_krb5_ctx_fini(ctx); 2377 } 2378 2379 /* commit keytab file */ 2380 if (rc == SMB_ADJOIN_SUCCESS) { 2381 if (rename(tmpfile, SMBNS_KRB5_KEYTAB) != 0) { 2382 (void) unlink(tmpfile); 2383 rc = SMB_ADJOIN_ERR_COMMIT_KEYTAB; 2384 } else { 2385 /* Set IDMAP config */ 2386 if (smb_config_set_idmap_domain(ah->domain) != 0) { 2387 rc = SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN; 2388 } else { 2389 2390 /* Refresh IDMAP service */ 2391 if (smb_config_refresh_idmap() != 0) 2392 rc = SMB_ADJOIN_ERR_IDMAP_REFRESH; 2393 } 2394 } 2395 } else { 2396 (void) unlink(tmpfile); 2397 } 2398 2399 smb_ads_close(ah); 2400 smb_ccache_remove(SMB_CCACHE_PATH); 2401 return (rc); 2402 } 2403 2404 /* 2405 * smb_ads_join_errmsg 2406 * 2407 * Display error message for the specific adjoin error code. 2408 */ 2409 void 2410 smb_ads_join_errmsg(smb_adjoin_status_t status) 2411 { 2412 int i; 2413 struct xlate_table { 2414 smb_adjoin_status_t status; 2415 char *msg; 2416 } adjoin_table[] = { 2417 { SMB_ADJOIN_ERR_GET_HANDLE, "Failed to connect to an " 2418 "Active Directory server." }, 2419 { SMB_ADJOIN_ERR_GEN_PWD, "Failed to generate machine " 2420 "password." }, 2421 { SMB_ADJOIN_ERR_GET_DCLEVEL, "Unknown functional level of " 2422 "the domain controller. The rootDSE attribute named " 2423 "\"domainControllerFunctionality\" is missing from the " 2424 "Active Directory." }, 2425 { SMB_ADJOIN_ERR_ADD_TRUST_ACCT, "Failed to create the " 2426 "workstation trust account." }, 2427 { SMB_ADJOIN_ERR_MOD_TRUST_ACCT, "Failed to modify the " 2428 "workstation trust account." }, 2429 { SMB_ADJOIN_ERR_DUP_TRUST_ACCT, "Failed to create the " 2430 "workstation trust account because its name is already " 2431 "in use." }, 2432 { SMB_ADJOIN_ERR_TRUST_ACCT, "Error in querying the " 2433 "workstation trust account" }, 2434 { SMB_ADJOIN_ERR_INIT_KRB_CTX, "Failed to initialize Kerberos " 2435 "context." }, 2436 { SMB_ADJOIN_ERR_GET_SPNS, "Failed to get Kerberos " 2437 "principals." }, 2438 { SMB_ADJOIN_ERR_KSETPWD, "Failed to set machine password." }, 2439 { SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR, "Failed to modify " 2440 "userAccountControl attribute of the workstation trust " 2441 "account." }, 2442 { SMB_ADJOIN_ERR_WRITE_KEYTAB, "Error in writing to local " 2443 "keytab file (i.e /etc/krb5/krb5.keytab)." }, 2444 { SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN, "Failed to update idmap " 2445 "configuration." }, 2446 { SMB_ADJOIN_ERR_IDMAP_REFRESH, "Failed to refresh idmap " 2447 "service." }, 2448 { SMB_ADJOIN_ERR_COMMIT_KEYTAB, "Failed to commit changes to " 2449 "local keytab file (i.e. /etc/krb5/krb5.keytab)." } 2450 }; 2451 2452 for (i = 0; i < sizeof (adjoin_table) / sizeof (adjoin_table[0]); i++) { 2453 if (adjoin_table[i].status == status) 2454 syslog(LOG_NOTICE, "%s", adjoin_table[i].msg); 2455 } 2456 } 2457 2458 /* 2459 * smb_ads_match_pdc 2460 * 2461 * Returns B_TRUE if the given host's IP address matches the preferred DC's 2462 * IP address. Otherwise, returns B_FALSE. 2463 */ 2464 static boolean_t 2465 smb_ads_match_pdc(smb_ads_host_info_t *host) 2466 { 2467 boolean_t match = B_FALSE; 2468 2469 if (!host) 2470 return (match); 2471 2472 (void) mutex_lock(&smb_ads_cfg.c_mtx); 2473 if (smb_inet_equal(&host->ipaddr, &smb_ads_cfg.c_pdc)) 2474 match = B_TRUE; 2475 (void) mutex_unlock(&smb_ads_cfg.c_mtx); 2476 2477 return (match); 2478 } 2479 2480 /* 2481 * smb_ads_select_dcfromsubnet 2482 * 2483 * This method walks the list of DCs and returns the first DC record that 2484 * responds to ldap ping and is in the same subnet as the host. 2485 * 2486 * Returns a pointer to the found DC record. 2487 * Returns NULL, on error or if no DC record is found. 2488 */ 2489 static smb_ads_host_info_t * 2490 smb_ads_select_dcfromsubnet(smb_ads_host_list_t *hlist) 2491 { 2492 smb_ads_host_info_t *hentry; 2493 smb_nic_t *lnic; 2494 smb_niciter_t ni; 2495 size_t cnt; 2496 int i; 2497 2498 if (smb_nic_getfirst(&ni) != 0) 2499 return (NULL); 2500 do { 2501 lnic = &ni.ni_nic; 2502 cnt = hlist->ah_cnt; 2503 2504 for (i = 0; i < cnt; i++) { 2505 hentry = &hlist->ah_list[i]; 2506 if ((hentry->ipaddr.a_family == AF_INET) && 2507 (lnic->nic_ip.a_family == AF_INET)) { 2508 if ((hentry->ipaddr.a_ipv4 & 2509 lnic->nic_mask) == 2510 (lnic->nic_ip.a_ipv4 & 2511 lnic->nic_mask)) 2512 if (smb_ads_ldap_ping(hentry) == 0) 2513 return (hentry); 2514 } 2515 } 2516 } while (smb_nic_getnext(&ni) == 0); 2517 2518 return (NULL); 2519 } 2520 2521 /* 2522 * smb_ads_select_dcfromlist 2523 * 2524 * This method walks the list of DCs and returns the first DC that 2525 * responds to ldap ping. 2526 * 2527 * Returns a pointer to the found DC record. 2528 * Returns NULL if no DC record is found. 2529 */ 2530 static smb_ads_host_info_t * 2531 smb_ads_select_dcfromlist(smb_ads_host_list_t *hlist) 2532 { 2533 smb_ads_host_info_t *hentry; 2534 size_t cnt; 2535 int i; 2536 2537 cnt = hlist->ah_cnt; 2538 for (i = 0; i < cnt; i++) { 2539 hentry = &hlist->ah_list[i]; 2540 if (smb_ads_ldap_ping(hentry) == 0) 2541 return (hentry); 2542 } 2543 2544 return (NULL); 2545 } 2546 2547 /* 2548 * smb_ads_dc_compare 2549 * 2550 * Comparision function for sorting host entries (SRV records of DC) via qsort. 2551 * RFC 2052/2782 are taken as reference, while implementing this algorithm. 2552 * 2553 * Domain Controllers(DCs) with lowest priority in their SRV DNS records 2554 * are selected first. If they have equal priorities, then DC with highest 2555 * weight in its SRV DNS record is selected. If the priority and weight are 2556 * both equal, then the DC at the top of the list is selected. 2557 */ 2558 static int 2559 smb_ads_dc_compare(const void *p, const void *q) 2560 { 2561 smb_ads_host_info_t *h1 = (smb_ads_host_info_t *)p; 2562 smb_ads_host_info_t *h2 = (smb_ads_host_info_t *)q; 2563 2564 if (h1->priority < h2->priority) 2565 return (-1); 2566 if (h1->priority > h2->priority) 2567 return (1); 2568 2569 /* Priorities are equal */ 2570 if (h1->weight < h2->weight) 2571 return (1); 2572 if (h1->weight > h2->weight) 2573 return (-1); 2574 2575 return (0); 2576 } 2577 2578 /* 2579 * smb_ads_select_dc 2580 * 2581 * The list of ADS hosts returned by ADS lookup, is sorted by lowest priority 2582 * and highest weight. On this sorted list, following additional rules are 2583 * applied, to select a DC. 2584 * 2585 * - If there is a DC in the same subnet, then return the DC, 2586 * if it responds to ldap ping. 2587 * - Else, return first DC that responds to ldap ping. 2588 * 2589 * A reference to the host entry from input host list is returned. 2590 * 2591 * Returns NULL on error. 2592 */ 2593 static smb_ads_host_info_t * 2594 smb_ads_select_dc(smb_ads_host_list_t *hlist) 2595 { 2596 smb_ads_host_info_t *hentry = NULL; 2597 2598 if (hlist->ah_cnt == 0) 2599 return (NULL); 2600 2601 if (hlist->ah_cnt == 1) { 2602 hentry = hlist->ah_list; 2603 if (smb_ads_ldap_ping(hentry) == 0) 2604 return (hentry); 2605 } 2606 2607 /* Sort the list by priority and weight */ 2608 qsort(hlist->ah_list, hlist->ah_cnt, 2609 sizeof (smb_ads_host_info_t), smb_ads_dc_compare); 2610 2611 if ((hentry = smb_ads_select_dcfromsubnet(hlist)) != NULL) 2612 return (hentry); 2613 2614 if ((hentry = smb_ads_select_dcfromlist(hlist)) != NULL) 2615 return (hentry); 2616 2617 return (NULL); 2618 } 2619 2620 /* 2621 * smb_ads_lookup_msdcs 2622 * 2623 * If server argument is set, try to locate the specified DC. 2624 * If it is set to empty string, locate any DCs in the specified domain. 2625 * Returns the discovered DC via buf. 2626 * 2627 * fqdn - fully-qualified domain name 2628 * server - fully-qualifed hostname of a DC 2629 * buf - the hostname of the discovered DC 2630 */ 2631 boolean_t 2632 smb_ads_lookup_msdcs(char *fqdn, char *server, char *buf, uint32_t buflen) 2633 { 2634 smb_ads_host_info_t *hinfo = NULL; 2635 char *p; 2636 char *sought_host; 2637 char ipstr[INET6_ADDRSTRLEN]; 2638 2639 if (!fqdn || !buf) 2640 return (B_FALSE); 2641 2642 ipstr[0] = '\0'; 2643 *buf = '\0'; 2644 sought_host = (*server == 0 ? NULL : server); 2645 if ((hinfo = smb_ads_find_host(fqdn, sought_host)) == NULL) 2646 return (B_FALSE); 2647 2648 (void) smb_inet_ntop(&hinfo->ipaddr, ipstr, 2649 SMB_IPSTRLEN(hinfo->ipaddr.a_family)); 2650 smb_tracef("msdcsLookupADS: %s [%s]", hinfo->name, ipstr); 2651 2652 (void) strlcpy(buf, hinfo->name, buflen); 2653 /* 2654 * Remove the domain extension 2655 */ 2656 if ((p = strchr(buf, '.')) != 0) 2657 *p = '\0'; 2658 2659 free(hinfo); 2660 return (B_TRUE); 2661 } 2662