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