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