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