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