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