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