xref: /onnv-gate/usr/src/lib/smbsrv/libsmbns/common/smbns_ads.c (revision 12508:edb7861a1533)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
2212065SKeyur.Desai@Sun.COM  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
235331Samw  */
245331Samw 
255331Samw #include <sys/param.h>
265331Samw #include <ldap.h>
275331Samw #include <stdlib.h>
285331Samw #include <sys/types.h>
295331Samw #include <sys/socket.h>
305331Samw #include <netinet/in.h>
315331Samw #include <arpa/inet.h>
325331Samw #include <sys/time.h>
335331Samw #include <netdb.h>
345331Samw #include <pthread.h>
355331Samw #include <unistd.h>
365331Samw #include <arpa/nameser.h>
375331Samw #include <resolv.h>
385331Samw #include <sys/synch.h>
395331Samw #include <string.h>
405331Samw #include <strings.h>
415331Samw #include <fcntl.h>
425331Samw #include <sys/types.h>
435331Samw #include <sys/stat.h>
447961SNatalie.Li@Sun.COM #include <assert.h>
4510717Samw@Sun.COM #include <sasl/sasl.h>
4610717Samw@Sun.COM #include <note.h>
4711571SShawn.Emery@Sun.COM #include <errno.h>
4811571SShawn.Emery@Sun.COM #include <cryptoutil.h>
495331Samw 
505331Samw #include <smbsrv/libsmbns.h>
515331Samw #include <smbns_dyndns.h>
525331Samw #include <smbns_krb.h>
535331Samw 
5411633SJoyce.McIntosh@Sun.COM #define	SMB_ADS_AF_UNKNOWN(x)	(((x)->ipaddr.a_family != AF_INET) && \
5511633SJoyce.McIntosh@Sun.COM 	((x)->ipaddr.a_family != AF_INET6))
5611633SJoyce.McIntosh@Sun.COM 
579832Samw@Sun.COM #define	SMB_ADS_MAXBUFLEN 100
587052Samw #define	SMB_ADS_DN_MAX	300
597052Samw #define	SMB_ADS_MAXMSGLEN 512
607052Samw #define	SMB_ADS_COMPUTERS_CN "Computers"
617052Samw #define	SMB_ADS_COMPUTER_NUM_ATTR 8
627052Samw #define	SMB_ADS_SHARE_NUM_ATTR 3
637052Samw #define	SMB_ADS_SITE_MAX MAXHOSTNAMELEN
647052Samw 
6511571SShawn.Emery@Sun.COM /*
6611571SShawn.Emery@Sun.COM  * [MS-DISO] A machine password is an ASCII string of randomly chosen
6711571SShawn.Emery@Sun.COM  * characters. Each character's ASCII code is between 32 and 122 inclusive.
6811571SShawn.Emery@Sun.COM  */
6911571SShawn.Emery@Sun.COM #define	SMB_ADS_PWD_CHAR_NUM	91
7011571SShawn.Emery@Sun.COM #define	SMB_ADS_PWD_CHAR_START	32
7111571SShawn.Emery@Sun.COM 
727052Samw #define	SMB_ADS_MSDCS_SRV_DC_RR		"_ldap._tcp.dc._msdcs"
737052Samw #define	SMB_ADS_MSDCS_SRV_SITE_RR	"_ldap._tcp.%s._sites.dc._msdcs"
747052Samw 
757052Samw /*
767052Samw  * domainControllerFunctionality
777052Samw  *
787052Samw  * This rootDSE attribute indicates the functional level of the DC.
797052Samw  */
807052Samw #define	SMB_ADS_ATTR_DCLEVEL	"domainControllerFunctionality"
817052Samw #define	SMB_ADS_DCLEVEL_W2K	0
827052Samw #define	SMB_ADS_DCLEVEL_W2K3	2
837052Samw #define	SMB_ADS_DCLEVEL_W2K8	3
849914Samw@Sun.COM #define	SMB_ADS_DCLEVEL_W2K8_R2 4
857052Samw 
867052Samw /*
877052Samw  * msDs-supportedEncryptionTypes (Windows Server 2008 only)
887052Samw  *
897052Samw  * This attribute defines the encryption types supported by the system.
907052Samw  * Encryption Types:
917052Samw  *  - DES cbc mode with CRC-32
927052Samw  *  - DES cbc mode with RSA-MD5
937052Samw  *  - ArcFour with HMAC/md5
947052Samw  *  - AES-128
957052Samw  *  - AES-256
967052Samw  */
977052Samw #define	SMB_ADS_ATTR_ENCTYPES	"msDs-supportedEncryptionTypes"
987052Samw #define	SMB_ADS_ENC_DES_CRC	1
997052Samw #define	SMB_ADS_ENC_DES_MD5	2
1007052Samw #define	SMB_ADS_ENC_RC4		4
1017052Samw #define	SMB_ADS_ENC_AES128	8
1027052Samw #define	SMB_ADS_ENC_AES256	16
1037052Samw 
104*12508Samw@Sun.COM static krb5_enctype w2k8enctypes[] = {
105*12508Samw@Sun.COM     ENCTYPE_AES256_CTS_HMAC_SHA1_96,
106*12508Samw@Sun.COM     ENCTYPE_AES128_CTS_HMAC_SHA1_96,
107*12508Samw@Sun.COM     ENCTYPE_ARCFOUR_HMAC,
108*12508Samw@Sun.COM     ENCTYPE_DES_CBC_CRC,
109*12508Samw@Sun.COM     ENCTYPE_DES_CBC_MD5,
110*12508Samw@Sun.COM };
111*12508Samw@Sun.COM 
112*12508Samw@Sun.COM static krb5_enctype pre_w2k8enctypes[] = {
113*12508Samw@Sun.COM     ENCTYPE_ARCFOUR_HMAC,
114*12508Samw@Sun.COM     ENCTYPE_DES_CBC_CRC,
115*12508Samw@Sun.COM     ENCTYPE_DES_CBC_MD5,
116*12508Samw@Sun.COM };
117*12508Samw@Sun.COM 
1187052Samw #define	SMB_ADS_ATTR_SAMACCT	"sAMAccountName"
1197052Samw #define	SMB_ADS_ATTR_UPN	"userPrincipalName"
1207052Samw #define	SMB_ADS_ATTR_SPN	"servicePrincipalName"
1217052Samw #define	SMB_ADS_ATTR_CTL	"userAccountControl"
1227052Samw #define	SMB_ADS_ATTR_DNSHOST	"dNSHostName"
1237052Samw #define	SMB_ADS_ATTR_KVNO	"msDS-KeyVersionNumber"
1247348SJose.Borrego@Sun.COM #define	SMB_ADS_ATTR_DN		"distinguishedName"
1255331Samw 
1267961SNatalie.Li@Sun.COM /*
1279832Samw@Sun.COM  * UserAccountControl flags: manipulate user account properties.
1289832Samw@Sun.COM  *
1299832Samw@Sun.COM  * The hexadecimal value of the following property flags are based on MSDN
1309832Samw@Sun.COM  * article # 305144.
1319832Samw@Sun.COM  */
1329832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_SCRIPT				0x00000001
1339832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_ACCOUNTDISABLE			0x00000002
1349832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_HOMEDIR_REQUIRED			0x00000008
1359832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_LOCKOUT				0x00000010
1369832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_PASSWD_NOTREQD			0x00000020
1379832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_PASSWD_CANT_CHANGE		0x00000040
1389832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_ENCRYPTED_TEXT_PWD_ALLOWED	0x00000080
1399832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_TMP_DUP_ACCT			0x00000100
1409832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_NORMAL_ACCT			0x00000200
1419832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_INTERDOMAIN_TRUST_ACCT		0x00000800
1429832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT		0x00001000
1439832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_SRV_TRUST_ACCT			0x00002000
1449832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD		0x00010000
1459832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_MNS_LOGON_ACCT			0x00020000
1469832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_SMARTCARD_REQUIRED		0x00040000
1479832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION		0x00080000
1489832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_NOT_DELEGATED			0x00100000
1499832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY			0x00200000
1509832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_DONT_REQ_PREAUTH			0x00400000
1519832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_PASSWD_EXPIRED			0x00800000
1529832Samw@Sun.COM #define	SMB_ADS_USER_ACCT_CTL_TRUSTED_TO_AUTH_FOR_DELEGATION	0x01000000
1539832Samw@Sun.COM 
1549832Samw@Sun.COM /*
1557961SNatalie.Li@Sun.COM  * Length of "dc=" prefix.
1567961SNatalie.Li@Sun.COM  */
1577961SNatalie.Li@Sun.COM #define	SMB_ADS_DN_PREFIX_LEN	3
1587961SNatalie.Li@Sun.COM 
1598670SJose.Borrego@Sun.COM static char *smb_ads_computer_objcls[] = {
1608670SJose.Borrego@Sun.COM 	"top", "person", "organizationalPerson",
1618670SJose.Borrego@Sun.COM 	"user", "computer", NULL
1628670SJose.Borrego@Sun.COM };
1638670SJose.Borrego@Sun.COM 
1648670SJose.Borrego@Sun.COM static char *smb_ads_share_objcls[] = {
1658670SJose.Borrego@Sun.COM 	"top", "leaf", "connectionPoint", "volume", NULL
1668670SJose.Borrego@Sun.COM };
1678670SJose.Borrego@Sun.COM 
1687961SNatalie.Li@Sun.COM /* Cached ADS server to communicate with */
1697961SNatalie.Li@Sun.COM static smb_ads_host_info_t *smb_ads_cached_host_info = NULL;
1707961SNatalie.Li@Sun.COM static mutex_t smb_ads_cached_host_mtx;
1717961SNatalie.Li@Sun.COM 
1729832Samw@Sun.COM /*
1739832Samw@Sun.COM  * SMB ADS config cache is maintained to facilitate the detection of
1749832Samw@Sun.COM  * changes in configuration that is relevant to AD selection.
1759832Samw@Sun.COM  */
1769832Samw@Sun.COM typedef struct smb_ads_config {
1779832Samw@Sun.COM 	char c_site[SMB_ADS_SITE_MAX];
1789832Samw@Sun.COM 	smb_inaddr_t c_pdc;
1799832Samw@Sun.COM 	mutex_t c_mtx;
1809832Samw@Sun.COM } smb_ads_config_t;
1819832Samw@Sun.COM 
1829832Samw@Sun.COM static smb_ads_config_t smb_ads_cfg;
1839832Samw@Sun.COM 
1845772Sas200622 
1857961SNatalie.Li@Sun.COM /* attribute/value pair */
1867961SNatalie.Li@Sun.COM typedef struct smb_ads_avpair {
1877961SNatalie.Li@Sun.COM 	char *avp_attr;
1887961SNatalie.Li@Sun.COM 	char *avp_val;
1897961SNatalie.Li@Sun.COM } smb_ads_avpair_t;
1907961SNatalie.Li@Sun.COM 
1917961SNatalie.Li@Sun.COM /* query status */
1927961SNatalie.Li@Sun.COM typedef enum smb_ads_qstat {
1937961SNatalie.Li@Sun.COM 	SMB_ADS_STAT_ERR = -2,
1947961SNatalie.Li@Sun.COM 	SMB_ADS_STAT_DUP,
1957961SNatalie.Li@Sun.COM 	SMB_ADS_STAT_NOT_FOUND,
1967961SNatalie.Li@Sun.COM 	SMB_ADS_STAT_FOUND
1977961SNatalie.Li@Sun.COM } smb_ads_qstat_t;
1987961SNatalie.Li@Sun.COM 
1999832Samw@Sun.COM typedef struct smb_ads_host_list {
2009832Samw@Sun.COM 	int ah_cnt;
2019832Samw@Sun.COM 	smb_ads_host_info_t *ah_list;
2029832Samw@Sun.COM } smb_ads_host_list_t;
2039832Samw@Sun.COM 
2047348SJose.Borrego@Sun.COM static smb_ads_handle_t *smb_ads_open_main(char *, char *, char *);
2057348SJose.Borrego@Sun.COM static int smb_ads_add_computer(smb_ads_handle_t *, int, char *);
2067348SJose.Borrego@Sun.COM static int smb_ads_modify_computer(smb_ads_handle_t *, int, char *);
2077348SJose.Borrego@Sun.COM static int smb_ads_computer_op(smb_ads_handle_t *, int, int, char *);
2087961SNatalie.Li@Sun.COM static smb_ads_qstat_t smb_ads_lookup_computer_n_attr(smb_ads_handle_t *,
2097961SNatalie.Li@Sun.COM     smb_ads_avpair_t *, int, char *);
2107348SJose.Borrego@Sun.COM static int smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *, int, char *);
2117348SJose.Borrego@Sun.COM static krb5_kvno smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *, char *);
21211571SShawn.Emery@Sun.COM static int smb_ads_gen_machine_passwd(char *, size_t);
2137961SNatalie.Li@Sun.COM static void smb_ads_free_cached_host(void);
2147348SJose.Borrego@Sun.COM static int smb_ads_alloc_attr(LDAPMod **, int);
2157348SJose.Borrego@Sun.COM static void smb_ads_free_attr(LDAPMod **);
2167348SJose.Borrego@Sun.COM static int smb_ads_get_dc_level(smb_ads_handle_t *);
2177348SJose.Borrego@Sun.COM static smb_ads_host_info_t *smb_ads_select_dc(smb_ads_host_list_t *);
2187961SNatalie.Li@Sun.COM static smb_ads_qstat_t smb_ads_find_computer(smb_ads_handle_t *, char *);
2197961SNatalie.Li@Sun.COM static smb_ads_qstat_t smb_ads_getattr(LDAP *, LDAPMessage *,
2207961SNatalie.Li@Sun.COM     smb_ads_avpair_t *);
2217961SNatalie.Li@Sun.COM static smb_ads_qstat_t smb_ads_get_qstat(smb_ads_handle_t *, LDAPMessage *,
2227961SNatalie.Li@Sun.COM     smb_ads_avpair_t *);
2239832Samw@Sun.COM static boolean_t smb_ads_match_pdc(smb_ads_host_info_t *);
2249832Samw@Sun.COM static boolean_t smb_ads_is_sought_host(smb_ads_host_info_t *, char *);
2259832Samw@Sun.COM static boolean_t smb_ads_is_same_domain(char *, char *);
2269832Samw@Sun.COM static boolean_t smb_ads_is_pdc_configured(void);
2279832Samw@Sun.COM static smb_ads_host_info_t *smb_ads_dup_host_info(smb_ads_host_info_t *);
22811337SWilliam.Krier@Sun.COM static char *smb_ads_get_sharedn(const char *, const char *, const char *);
229*12508Samw@Sun.COM static krb5_enctype *smb_ads_get_enctypes(int, int *);
2305772Sas200622 
2315772Sas200622 /*
2327052Samw  * smb_ads_init
2335772Sas200622  *
2349832Samw@Sun.COM  * Initializes the ADS config cache.
2355772Sas200622  */
2365772Sas200622 void
smb_ads_init(void)2377052Samw smb_ads_init(void)
2385772Sas200622 {
2399832Samw@Sun.COM 	(void) mutex_lock(&smb_ads_cfg.c_mtx);
2407961SNatalie.Li@Sun.COM 	(void) smb_config_getstr(SMB_CI_ADS_SITE,
2419832Samw@Sun.COM 	    smb_ads_cfg.c_site, SMB_ADS_SITE_MAX);
2429832Samw@Sun.COM 	(void) smb_config_getip(SMB_CI_DOMAIN_SRV, &smb_ads_cfg.c_pdc);
2439832Samw@Sun.COM 	(void) mutex_unlock(&smb_ads_cfg.c_mtx);
2449832Samw@Sun.COM }
2459832Samw@Sun.COM 
2469832Samw@Sun.COM void
smb_ads_fini(void)2479832Samw@Sun.COM smb_ads_fini(void)
2489832Samw@Sun.COM {
2499832Samw@Sun.COM 	smb_ads_free_cached_host();
2505772Sas200622 }
2515772Sas200622 
2525772Sas200622 /*
2537052Samw  * smb_ads_refresh
2545772Sas200622  *
2559832Samw@Sun.COM  * This function will be called when smb/server SMF service is refreshed.
2569832Samw@Sun.COM  * Clearing the smb_ads_cached_host_info would allow the next DC
2579832Samw@Sun.COM  * discovery process to pick up an AD based on the new AD configuration.
2585772Sas200622  */
2595772Sas200622 void
smb_ads_refresh(void)2607052Samw smb_ads_refresh(void)
2615772Sas200622 {
2627052Samw 	char new_site[SMB_ADS_SITE_MAX];
2639832Samw@Sun.COM 	smb_inaddr_t new_pdc;
2649832Samw@Sun.COM 	boolean_t purge = B_FALSE;
2659832Samw@Sun.COM 
2669832Samw@Sun.COM 	(void) smb_config_getstr(SMB_CI_ADS_SITE, new_site, SMB_ADS_SITE_MAX);
2679832Samw@Sun.COM 	(void) smb_config_getip(SMB_CI_DOMAIN_SRV, &new_pdc);
2689832Samw@Sun.COM 	(void) mutex_lock(&smb_ads_cfg.c_mtx);
26910966SJordan.Brown@Sun.COM 	if (smb_strcasecmp(smb_ads_cfg.c_site, new_site, 0)) {
2709832Samw@Sun.COM 		(void) strlcpy(smb_ads_cfg.c_site, new_site, SMB_ADS_SITE_MAX);
2719832Samw@Sun.COM 		purge = B_TRUE;
2729832Samw@Sun.COM 	}
2739832Samw@Sun.COM 
2749832Samw@Sun.COM 	smb_ads_cfg.c_pdc = new_pdc;
2759832Samw@Sun.COM 	(void) mutex_unlock(&smb_ads_cfg.c_mtx);
2769832Samw@Sun.COM 
2779832Samw@Sun.COM 	(void) mutex_lock(&smb_ads_cached_host_mtx);
2789832Samw@Sun.COM 	if (smb_ads_cached_host_info &&
2799832Samw@Sun.COM 	    smb_ads_is_pdc_configured() &&
2809832Samw@Sun.COM 	    !smb_ads_match_pdc(smb_ads_cached_host_info))
2819832Samw@Sun.COM 		purge = B_TRUE;
2829832Samw@Sun.COM 	(void) mutex_unlock(&smb_ads_cached_host_mtx);
2839832Samw@Sun.COM 
2849832Samw@Sun.COM 	if (purge)
2857961SNatalie.Li@Sun.COM 		smb_ads_free_cached_host();
2869832Samw@Sun.COM }
2879832Samw@Sun.COM 
2889832Samw@Sun.COM 
2899832Samw@Sun.COM 
2909832Samw@Sun.COM static boolean_t
smb_ads_is_pdc_configured(void)2919832Samw@Sun.COM smb_ads_is_pdc_configured(void)
2929832Samw@Sun.COM {
2939832Samw@Sun.COM 	boolean_t configured;
2949832Samw@Sun.COM 
2959832Samw@Sun.COM 	(void) mutex_lock(&smb_ads_cfg.c_mtx);
2969832Samw@Sun.COM 	configured = !smb_inet_iszero(&smb_ads_cfg.c_pdc);
2979832Samw@Sun.COM 	(void) mutex_unlock(&smb_ads_cfg.c_mtx);
2989832Samw@Sun.COM 
2999832Samw@Sun.COM 	return (configured);
3005772Sas200622 }
3015331Samw 
3025331Samw /*
3037052Samw  * smb_ads_build_unc_name
3045331Samw  *
3055331Samw  * Construct the UNC name of the share object in the format of
3065331Samw  * \\hostname.domain\shareUNC
3075331Samw  *
3085331Samw  * Returns 0 on success, -1 on error.
3095331Samw  */
3105331Samw int
smb_ads_build_unc_name(char * unc_name,int maxlen,const char * hostname,const char * shareUNC)3117052Samw smb_ads_build_unc_name(char *unc_name, int maxlen,
3125331Samw     const char *hostname, const char *shareUNC)
3135331Samw {
3145772Sas200622 	char my_domain[MAXHOSTNAMELEN];
3155331Samw 
3165772Sas200622 	if (smb_getfqdomainname(my_domain, sizeof (my_domain)) != 0)
3175331Samw 		return (-1);
3185331Samw 
3195331Samw 	(void) snprintf(unc_name, maxlen, "\\\\%s.%s\\%s",
3205331Samw 	    hostname, my_domain, shareUNC);
3215331Samw 	return (0);
3225331Samw }
3235331Samw 
3245331Samw /*
3257348SJose.Borrego@Sun.COM  * smb_ads_ldap_ping
3267052Samw  *
3277348SJose.Borrego@Sun.COM  * This is used to bind to an ADS server to see
3287348SJose.Borrego@Sun.COM  * if it is still alive.
3297348SJose.Borrego@Sun.COM  *
3305331Samw  * Returns:
3315331Samw  *   -1: error
3325331Samw  *    0: successful
3335331Samw  */
3345331Samw /*ARGSUSED*/
3355331Samw static int
smb_ads_ldap_ping(smb_ads_host_info_t * ads_host)3367348SJose.Borrego@Sun.COM smb_ads_ldap_ping(smb_ads_host_info_t *ads_host)
3375331Samw {
3387348SJose.Borrego@Sun.COM 	int ldversion = LDAP_VERSION3, status, timeoutms = 5 * 1000;
3397348SJose.Borrego@Sun.COM 	LDAP *ld = NULL;
3407348SJose.Borrego@Sun.COM 
3418670SJose.Borrego@Sun.COM 	ld = ldap_init(ads_host->name, ads_host->port);
3427348SJose.Borrego@Sun.COM 	if (ld == NULL)
3437348SJose.Borrego@Sun.COM 		return (-1);
3447348SJose.Borrego@Sun.COM 
3457348SJose.Borrego@Sun.COM 	ldversion = LDAP_VERSION3;
3467348SJose.Borrego@Sun.COM 	(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldversion);
3477348SJose.Borrego@Sun.COM 	/* setup TCP/IP connect timeout */
3487348SJose.Borrego@Sun.COM 	(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeoutms);
3497348SJose.Borrego@Sun.COM 
3507348SJose.Borrego@Sun.COM 	status = ldap_bind_s(ld, "", NULL, LDAP_AUTH_SIMPLE);
3517348SJose.Borrego@Sun.COM 
3527348SJose.Borrego@Sun.COM 	if (status != LDAP_SUCCESS) {
3537348SJose.Borrego@Sun.COM 		(void) ldap_unbind(ld);
3547348SJose.Borrego@Sun.COM 		return (-1);
3557348SJose.Borrego@Sun.COM 	}
3567348SJose.Borrego@Sun.COM 
3577348SJose.Borrego@Sun.COM 	(void) ldap_unbind(ld);
3587348SJose.Borrego@Sun.COM 
3595331Samw 	return (0);
3605331Samw }
3615331Samw 
3625331Samw /*
3639832Samw@Sun.COM  * The cached ADS host is no longer valid if one of the following criteria
3649832Samw@Sun.COM  * is satisfied:
3657961SNatalie.Li@Sun.COM  *
3669832Samw@Sun.COM  * 1) not in the specified domain
3679832Samw@Sun.COM  * 2) not the sought host (if specified)
3689832Samw@Sun.COM  * 3) not reachable
3699832Samw@Sun.COM  *
3709832Samw@Sun.COM  * The caller is responsible for acquiring the smb_ads_cached_host_mtx lock
3719832Samw@Sun.COM  * prior to calling this function.
3729832Samw@Sun.COM  *
3739832Samw@Sun.COM  * Return B_TRUE if the cache host is still valid. Otherwise, return B_FALSE.
3745331Samw  */
3759832Samw@Sun.COM static boolean_t
smb_ads_validate_cache_host(char * domain,char * srv)3769832Samw@Sun.COM smb_ads_validate_cache_host(char *domain, char *srv)
3775331Samw {
3787961SNatalie.Li@Sun.COM 	if (!smb_ads_cached_host_info)
3799832Samw@Sun.COM 		return (B_FALSE);
3809832Samw@Sun.COM 
3819832Samw@Sun.COM 	if (!smb_ads_is_same_domain(smb_ads_cached_host_info->name, domain))
3829832Samw@Sun.COM 		return (B_FALSE);
3839832Samw@Sun.COM 
3849832Samw@Sun.COM 	if (smb_ads_ldap_ping(smb_ads_cached_host_info) == 0) {
3859832Samw@Sun.COM 		if (!srv)
3869832Samw@Sun.COM 			return (B_TRUE);
3879832Samw@Sun.COM 
3889832Samw@Sun.COM 		if (smb_ads_is_sought_host(smb_ads_cached_host_info, srv))
3899832Samw@Sun.COM 			return (B_TRUE);
3909832Samw@Sun.COM 	}
3919832Samw@Sun.COM 
3929832Samw@Sun.COM 	return (B_FALSE);
3935331Samw }
3945331Samw 
3955331Samw /*
3967961SNatalie.Li@Sun.COM  * smb_ads_is_sought_host
3977961SNatalie.Li@Sun.COM  *
3987961SNatalie.Li@Sun.COM  * Returns true, if the sought host name matches the input host (host) name.
3997961SNatalie.Li@Sun.COM  * The sought host is expected to be in Fully Qualified Domain Name (FQDN)
4007961SNatalie.Li@Sun.COM  * format.
4015331Samw  */
4027961SNatalie.Li@Sun.COM static boolean_t
smb_ads_is_sought_host(smb_ads_host_info_t * host,char * sought_host_name)4037961SNatalie.Li@Sun.COM smb_ads_is_sought_host(smb_ads_host_info_t *host, char *sought_host_name)
4045331Samw {
4057961SNatalie.Li@Sun.COM 	if ((host == NULL) || (sought_host_name == NULL))
4067961SNatalie.Li@Sun.COM 		return (B_FALSE);
4077961SNatalie.Li@Sun.COM 
40810966SJordan.Brown@Sun.COM 	if (smb_strcasecmp(host->name, sought_host_name, 0))
4097961SNatalie.Li@Sun.COM 		return (B_FALSE);
4107961SNatalie.Li@Sun.COM 
4117961SNatalie.Li@Sun.COM 	return (B_TRUE);
4127961SNatalie.Li@Sun.COM }
4137961SNatalie.Li@Sun.COM 
4147961SNatalie.Li@Sun.COM /*
4157961SNatalie.Li@Sun.COM  * smb_ads_match_hosts_same_domain
4167961SNatalie.Li@Sun.COM  *
4177961SNatalie.Li@Sun.COM  * Returns true, if the cached ADS host is in the same domain as the
4187961SNatalie.Li@Sun.COM  * current (given) domain.
4197961SNatalie.Li@Sun.COM  */
4207961SNatalie.Li@Sun.COM static boolean_t
smb_ads_is_same_domain(char * cached_host_name,char * current_domain)4217961SNatalie.Li@Sun.COM smb_ads_is_same_domain(char *cached_host_name, char *current_domain)
4227961SNatalie.Li@Sun.COM {
4237961SNatalie.Li@Sun.COM 	char *cached_host_domain;
4247961SNatalie.Li@Sun.COM 
4257961SNatalie.Li@Sun.COM 	if ((cached_host_name == NULL) || (current_domain == NULL))
4267961SNatalie.Li@Sun.COM 		return (B_FALSE);
4277961SNatalie.Li@Sun.COM 
4287961SNatalie.Li@Sun.COM 	cached_host_domain = strchr(cached_host_name, '.');
4297961SNatalie.Li@Sun.COM 	if (cached_host_domain == NULL)
4307961SNatalie.Li@Sun.COM 		return (B_FALSE);
4317961SNatalie.Li@Sun.COM 
4327961SNatalie.Li@Sun.COM 	++cached_host_domain;
43310966SJordan.Brown@Sun.COM 	if (smb_strcasecmp(cached_host_domain, current_domain, 0))
4347961SNatalie.Li@Sun.COM 		return (B_FALSE);
4357961SNatalie.Li@Sun.COM 
4367961SNatalie.Li@Sun.COM 	return (B_TRUE);
4375331Samw }
4387052Samw 
4395331Samw /*
4407052Samw  * smb_ads_skip_ques_sec
4417052Samw  * Skips the question section.
4427052Samw  */
4437052Samw static int
smb_ads_skip_ques_sec(int qcnt,uchar_t ** ptr,uchar_t * eom)4447052Samw smb_ads_skip_ques_sec(int qcnt, uchar_t **ptr, uchar_t *eom)
4457052Samw {
4467052Samw 	int i, len;
4477052Samw 
4487052Samw 	for (i = 0; i < qcnt; i++) {
4497961SNatalie.Li@Sun.COM 		if ((len = dn_skipname(*ptr, eom)) < 0)
4507052Samw 			return (-1);
4517961SNatalie.Li@Sun.COM 
4527052Samw 		*ptr += len + QFIXEDSZ;
4537052Samw 	}
4547052Samw 
4557052Samw 	return (0);
4567052Samw }
4577052Samw 
4587052Samw /*
4597052Samw  * smb_ads_decode_host_ans_sec
4607348SJose.Borrego@Sun.COM  * Decodes ADS hosts, priority, weight and port number from the answer
4617348SJose.Borrego@Sun.COM  * section based on the current buffer pointer.
4627052Samw  */
4637052Samw static int
smb_ads_decode_host_ans_sec(int ans_cnt,uchar_t ** ptr,uchar_t * eom,uchar_t * buf,smb_ads_host_info_t * ads_host_list)4647052Samw smb_ads_decode_host_ans_sec(int ans_cnt, uchar_t **ptr, uchar_t *eom,
4657052Samw     uchar_t *buf, smb_ads_host_info_t *ads_host_list)
4667052Samw {
4677052Samw 	int i, len;
4687052Samw 	smb_ads_host_info_t *ads_host;
4697052Samw 
4707052Samw 	for (i = 0; i < ans_cnt; i++) {
4717052Samw 		ads_host = &ads_host_list[i];
4727052Samw 
4737961SNatalie.Li@Sun.COM 		if ((len = dn_skipname(*ptr, eom)) < 0)
4747052Samw 			return (-1);
4757961SNatalie.Li@Sun.COM 
4767052Samw 
4777052Samw 		*ptr += len;
4787052Samw 
4797052Samw 		/* skip type, class, ttl */
4807052Samw 		*ptr += 8;
4817052Samw 		/* data size */
4827052Samw 		*ptr += 2;
4837052Samw 
4847348SJose.Borrego@Sun.COM 		/* Get priority, weight */
4857348SJose.Borrego@Sun.COM 		/* LINTED: E_CONSTANT_CONDITION */
4867348SJose.Borrego@Sun.COM 		NS_GET16(ads_host->priority, *ptr);
4877348SJose.Borrego@Sun.COM 		/* LINTED: E_CONSTANT_CONDITION */
4887348SJose.Borrego@Sun.COM 		NS_GET16(ads_host->weight, *ptr);
4897348SJose.Borrego@Sun.COM 
4907052Samw 		/* port */
4917052Samw 		/* LINTED: E_CONSTANT_CONDITION */
4927052Samw 		NS_GET16(ads_host->port, *ptr);
4937052Samw 		/* domain name */
4947052Samw 		len = dn_expand(buf, eom, *ptr, ads_host->name, MAXHOSTNAMELEN);
4957961SNatalie.Li@Sun.COM 		if (len < 0)
4967052Samw 			return (-1);
4977961SNatalie.Li@Sun.COM 
4987052Samw 		*ptr += len;
4997052Samw 	}
5007052Samw 
5017052Samw 	return (0);
5027052Samw }
5037052Samw 
5047052Samw /*
5057052Samw  * smb_ads_skip_auth_sec
5067052Samw  * Skips the authority section.
5077052Samw  */
5087052Samw static int
smb_ads_skip_auth_sec(int ns_cnt,uchar_t ** ptr,uchar_t * eom)5097052Samw smb_ads_skip_auth_sec(int ns_cnt, uchar_t **ptr, uchar_t *eom)
5107052Samw {
5117052Samw 	int i, len;
5127052Samw 	uint16_t size;
5137052Samw 
5147052Samw 	for (i = 0; i < ns_cnt; i++) {
5157961SNatalie.Li@Sun.COM 		if ((len = dn_skipname(*ptr, eom)) < 0)
5167052Samw 			return (-1);
5177961SNatalie.Li@Sun.COM 
5187052Samw 		*ptr += len;
5197052Samw 		/* skip type, class, ttl */
5207052Samw 		*ptr += 8;
5217052Samw 		/* get len of data */
5227052Samw 		/* LINTED: E_CONSTANT_CONDITION */
5237052Samw 		NS_GET16(size, *ptr);
5247961SNatalie.Li@Sun.COM 		if ((*ptr + size) > eom)
5257052Samw 			return (-1);
5267052Samw 
5277052Samw 		*ptr += size;
5287052Samw 	}
5297052Samw 
5307052Samw 	return (0);
5317052Samw }
5327052Samw 
5337052Samw /*
5347961SNatalie.Li@Sun.COM  * smb_ads_decode_host_ip
5357961SNatalie.Li@Sun.COM  *
5367052Samw  * Decodes ADS hosts and IP Addresses from the additional section based
5377052Samw  * on the current buffer pointer.
5387052Samw  */
5397052Samw static int
smb_ads_decode_host_ip(int addit_cnt,int ans_cnt,uchar_t ** ptr,uchar_t * eom,uchar_t * buf,smb_ads_host_info_t * ads_host_list)5407961SNatalie.Li@Sun.COM smb_ads_decode_host_ip(int addit_cnt, int ans_cnt, uchar_t **ptr,
5417961SNatalie.Li@Sun.COM     uchar_t *eom, uchar_t *buf, smb_ads_host_info_t *ads_host_list)
5427052Samw {
5437961SNatalie.Li@Sun.COM 	int i, j, len;
5448670SJose.Borrego@Sun.COM 	smb_inaddr_t ipaddr;
5457961SNatalie.Li@Sun.COM 	char hostname[MAXHOSTNAMELEN];
5467961SNatalie.Li@Sun.COM 	char *name;
5478670SJose.Borrego@Sun.COM 	uint16_t size = 0;
5487052Samw 
5497052Samw 	for (i = 0; i < addit_cnt; i++) {
5507052Samw 
5517052Samw 		/* domain name */
5527961SNatalie.Li@Sun.COM 		len = dn_expand(buf, eom, *ptr, hostname, MAXHOSTNAMELEN);
5537961SNatalie.Li@Sun.COM 		if (len < 0)
5547052Samw 			return (-1);
5557961SNatalie.Li@Sun.COM 
5567052Samw 		*ptr += len;
5577052Samw 
5587052Samw 		/* skip type, class, TTL, data len */
5598670SJose.Borrego@Sun.COM 		*ptr += 8;
5607052Samw 		/* LINTED: E_CONSTANT_CONDITION */
5618670SJose.Borrego@Sun.COM 		NS_GET16(size, *ptr);
5628670SJose.Borrego@Sun.COM 
5638670SJose.Borrego@Sun.COM 		if (size == INADDRSZ) {
5648670SJose.Borrego@Sun.COM 			/* LINTED: E_CONSTANT_CONDITION */
5658670SJose.Borrego@Sun.COM 			NS_GET32(ipaddr.a_ipv4, *ptr);
5668670SJose.Borrego@Sun.COM 			ipaddr.a_ipv4 = htonl(ipaddr.a_ipv4);
5678670SJose.Borrego@Sun.COM 			ipaddr.a_family = AF_INET;
5688670SJose.Borrego@Sun.COM 		} else if (size == IN6ADDRSZ) {
5698670SJose.Borrego@Sun.COM #ifdef BIG_ENDIAN
5708670SJose.Borrego@Sun.COM 			bcopy(*ptr, &ipaddr.a_ipv6, IN6ADDRSZ);
5718670SJose.Borrego@Sun.COM #else
5728670SJose.Borrego@Sun.COM 			for (i = 0; i < IN6ADDRSZ; i++)
5738670SJose.Borrego@Sun.COM 				(uint8_t *)(ipaddr.a_ipv6)
5748670SJose.Borrego@Sun.COM 				    [IN6ADDRSZ-1-i] = *(*ptr+i);
5758670SJose.Borrego@Sun.COM #endif
5768670SJose.Borrego@Sun.COM 			ipaddr.a_family = AF_INET6;
57712065SKeyur.Desai@Sun.COM 			*ptr += size;
5788670SJose.Borrego@Sun.COM 		}
5797052Samw 
5807961SNatalie.Li@Sun.COM 		/*
5817961SNatalie.Li@Sun.COM 		 * find the host in the list of DC records from
5827961SNatalie.Li@Sun.COM 		 * the answer section, that matches the host in the
5837961SNatalie.Li@Sun.COM 		 * additional section, and set its IP address.
5847961SNatalie.Li@Sun.COM 		 */
5857961SNatalie.Li@Sun.COM 		for (j = 0; j < ans_cnt; j++) {
5867961SNatalie.Li@Sun.COM 			if ((name = ads_host_list[j].name) == NULL)
5877961SNatalie.Li@Sun.COM 				continue;
58810966SJordan.Brown@Sun.COM 			if (smb_strcasecmp(name, hostname, 0) == 0) {
5898670SJose.Borrego@Sun.COM 				ads_host_list[j].ipaddr = ipaddr;
5908670SJose.Borrego@Sun.COM 			}
5917961SNatalie.Li@Sun.COM 		}
5927052Samw 	}
5937052Samw 	return (0);
5947052Samw }
5957052Samw 
5967961SNatalie.Li@Sun.COM /*
5977961SNatalie.Li@Sun.COM  * smb_ads_dup_host_info
5987961SNatalie.Li@Sun.COM  *
5997961SNatalie.Li@Sun.COM  * Duplicates the passed smb_ads_host_info_t structure.
6007961SNatalie.Li@Sun.COM  * Caller must free memory allocated by this method.
6017961SNatalie.Li@Sun.COM  *
6027961SNatalie.Li@Sun.COM  * Returns a reference to the duplicated smb_ads_host_info_t structure.
6037961SNatalie.Li@Sun.COM  * Returns NULL on error.
6047961SNatalie.Li@Sun.COM  */
6057961SNatalie.Li@Sun.COM static smb_ads_host_info_t *
smb_ads_dup_host_info(smb_ads_host_info_t * ads_host)6067961SNatalie.Li@Sun.COM smb_ads_dup_host_info(smb_ads_host_info_t *ads_host)
6077052Samw {
6087961SNatalie.Li@Sun.COM 	smb_ads_host_info_t *dup_host;
6097961SNatalie.Li@Sun.COM 
6107961SNatalie.Li@Sun.COM 	if (ads_host == NULL)
6117961SNatalie.Li@Sun.COM 		return (NULL);
6127961SNatalie.Li@Sun.COM 
6137961SNatalie.Li@Sun.COM 	dup_host = malloc(sizeof (smb_ads_host_info_t));
6147961SNatalie.Li@Sun.COM 
6157961SNatalie.Li@Sun.COM 	if (dup_host != NULL)
6167961SNatalie.Li@Sun.COM 		bcopy(ads_host, dup_host, sizeof (smb_ads_host_info_t));
6177961SNatalie.Li@Sun.COM 
6187961SNatalie.Li@Sun.COM 	return (dup_host);
6197348SJose.Borrego@Sun.COM }
6207052Samw 
6217348SJose.Borrego@Sun.COM /*
6227348SJose.Borrego@Sun.COM  * smb_ads_hlist_alloc
6237348SJose.Borrego@Sun.COM  */
62410717Samw@Sun.COM static smb_ads_host_list_t *
smb_ads_hlist_alloc(int count)6257348SJose.Borrego@Sun.COM smb_ads_hlist_alloc(int count)
6267348SJose.Borrego@Sun.COM {
6277348SJose.Borrego@Sun.COM 	int size;
6287348SJose.Borrego@Sun.COM 	smb_ads_host_list_t *hlist;
6297348SJose.Borrego@Sun.COM 
6307961SNatalie.Li@Sun.COM 	if (count == 0)
6317961SNatalie.Li@Sun.COM 		return (NULL);
6327961SNatalie.Li@Sun.COM 
6337348SJose.Borrego@Sun.COM 	size = sizeof (smb_ads_host_info_t) * count;
6347348SJose.Borrego@Sun.COM 	hlist = (smb_ads_host_list_t *)malloc(sizeof (smb_ads_host_list_t));
6357348SJose.Borrego@Sun.COM 	if (hlist == NULL)
6367052Samw 		return (NULL);
6377052Samw 
6387348SJose.Borrego@Sun.COM 	hlist->ah_cnt = count;
6397348SJose.Borrego@Sun.COM 	hlist->ah_list = (smb_ads_host_info_t *)malloc(size);
6407348SJose.Borrego@Sun.COM 	if (hlist->ah_list == NULL) {
6417348SJose.Borrego@Sun.COM 		free(hlist);
6427348SJose.Borrego@Sun.COM 		return (NULL);
6437348SJose.Borrego@Sun.COM 	}
6447348SJose.Borrego@Sun.COM 
6457348SJose.Borrego@Sun.COM 	bzero(hlist->ah_list, size);
6467348SJose.Borrego@Sun.COM 	return (hlist);
6477348SJose.Borrego@Sun.COM }
6487052Samw 
6497348SJose.Borrego@Sun.COM /*
6507348SJose.Borrego@Sun.COM  * smb_ads_hlist_free
6517348SJose.Borrego@Sun.COM  */
6527348SJose.Borrego@Sun.COM static void
smb_ads_hlist_free(smb_ads_host_list_t * host_list)6537348SJose.Borrego@Sun.COM smb_ads_hlist_free(smb_ads_host_list_t *host_list)
6547348SJose.Borrego@Sun.COM {
6557348SJose.Borrego@Sun.COM 	if (host_list == NULL)
6567348SJose.Borrego@Sun.COM 		return;
6577348SJose.Borrego@Sun.COM 
6587348SJose.Borrego@Sun.COM 	free(host_list->ah_list);
6597348SJose.Borrego@Sun.COM 	free(host_list);
6607052Samw }
6617052Samw 
6627052Samw /*
6637961SNatalie.Li@Sun.COM  * smb_ads_query_dns_server
6647348SJose.Borrego@Sun.COM  *
6657052Samw  * This routine sends a DNS service location (SRV) query message to the
6667961SNatalie.Li@Sun.COM  * DNS server via TCP to query it for a list of ADS server(s). Once a reply
6677961SNatalie.Li@Sun.COM  * is received, the reply message is parsed to get the hostname. If there are IP
6687052Samw  * addresses populated in the additional section then the additional section
6697052Samw  * is parsed to obtain the IP addresses.
6705331Samw  *
6715331Samw  * The service location of _ldap._tcp.dc.msdcs.<ADS domain> is used to
6725331Samw  * guarantee that Microsoft domain controllers are returned.  Microsoft domain
6735331Samw  * controllers are also ADS servers.
6745331Samw  *
6755331Samw  * The ADS hostnames are stored in the answer section of the DNS reply message.
6767052Samw  * The IP addresses are stored in the additional section.
6775331Samw  *
6785331Samw  * The DNS reply message may be in compress formed.  The compression is done
6795331Samw  * on repeating domain name label in the message.  i.e hostname.
6807348SJose.Borrego@Sun.COM  *
6817961SNatalie.Li@Sun.COM  * Upon successful completion, host list of ADS server(s) is returned.
6825331Samw  */
6837961SNatalie.Li@Sun.COM static smb_ads_host_list_t *
smb_ads_query_dns_server(char * domain,char * msdcs_svc_name)6847961SNatalie.Li@Sun.COM smb_ads_query_dns_server(char *domain, char *msdcs_svc_name)
6855331Samw {
6867961SNatalie.Li@Sun.COM 	smb_ads_host_list_t *hlist = NULL;
6877052Samw 	int len, qcnt, ans_cnt, ns_cnt, addit_cnt;
6887052Samw 	uchar_t *ptr, *eom;
6897052Samw 	struct __res_state res_state;
6907052Samw 	union {
6917052Samw 		HEADER hdr;
6927052Samw 		uchar_t buf[NS_MAXMSG];
6937052Samw 	} msg;
6945331Samw 
6957052Samw 	bzero(&res_state, sizeof (struct __res_state));
6967052Samw 	if (res_ninit(&res_state) < 0)
6975331Samw 		return (NULL);
6985331Samw 
6997052Samw 	/* use TCP */
7007052Samw 	res_state.options |= RES_USEVC;
7017052Samw 
7027961SNatalie.Li@Sun.COM 	len = res_nquerydomain(&res_state, msdcs_svc_name, domain,
7037961SNatalie.Li@Sun.COM 	    C_IN, T_SRV, msg.buf, sizeof (msg.buf));
7047961SNatalie.Li@Sun.COM 
7057961SNatalie.Li@Sun.COM 	if (len < 0) {
7069021Samw@Sun.COM 		syslog(LOG_NOTICE, "DNS query for %s failed: %s",
7077961SNatalie.Li@Sun.COM 		    msdcs_svc_name, hstrerror(res_state.res_h_errno));
7087961SNatalie.Li@Sun.COM 		res_ndestroy(&res_state);
7097961SNatalie.Li@Sun.COM 		return (NULL);
7107961SNatalie.Li@Sun.COM 	}
7117961SNatalie.Li@Sun.COM 
7127961SNatalie.Li@Sun.COM 	if (len > sizeof (msg.buf)) {
7139021Samw@Sun.COM 		syslog(LOG_NOTICE,
7149021Samw@Sun.COM 		    "DNS query for %s failed: too big", msdcs_svc_name);
7157961SNatalie.Li@Sun.COM 		res_ndestroy(&res_state);
7167961SNatalie.Li@Sun.COM 		return (NULL);
7177961SNatalie.Li@Sun.COM 	}
7187961SNatalie.Li@Sun.COM 
7197961SNatalie.Li@Sun.COM 	/* parse the reply, skip header and question sections */
7207961SNatalie.Li@Sun.COM 	ptr = msg.buf + sizeof (msg.hdr);
7217961SNatalie.Li@Sun.COM 	eom = msg.buf + len;
7227961SNatalie.Li@Sun.COM 
7237961SNatalie.Li@Sun.COM 	/* check truncated message bit */
7247961SNatalie.Li@Sun.COM 	if (msg.hdr.tc)
7259021Samw@Sun.COM 		syslog(LOG_NOTICE,
7269021Samw@Sun.COM 		    "DNS query for %s failed: truncated", msdcs_svc_name);
7277961SNatalie.Li@Sun.COM 
7287961SNatalie.Li@Sun.COM 	qcnt = ntohs(msg.hdr.qdcount);
7297961SNatalie.Li@Sun.COM 	ans_cnt = ntohs(msg.hdr.ancount);
7307961SNatalie.Li@Sun.COM 	ns_cnt = ntohs(msg.hdr.nscount);
7317961SNatalie.Li@Sun.COM 	addit_cnt = ntohs(msg.hdr.arcount);
7327961SNatalie.Li@Sun.COM 
7337961SNatalie.Li@Sun.COM 	if (smb_ads_skip_ques_sec(qcnt, &ptr, eom) != 0) {
7347961SNatalie.Li@Sun.COM 		res_ndestroy(&res_state);
7357961SNatalie.Li@Sun.COM 		return (NULL);
7367961SNatalie.Li@Sun.COM 	}
7377961SNatalie.Li@Sun.COM 
7387961SNatalie.Li@Sun.COM 	hlist = smb_ads_hlist_alloc(ans_cnt);
7397961SNatalie.Li@Sun.COM 	if (hlist == NULL) {
7407961SNatalie.Li@Sun.COM 		res_ndestroy(&res_state);
7417961SNatalie.Li@Sun.COM 		return (NULL);
7427961SNatalie.Li@Sun.COM 	}
7437961SNatalie.Li@Sun.COM 
7447961SNatalie.Li@Sun.COM 	/* walk through the answer section */
7457961SNatalie.Li@Sun.COM 	if (smb_ads_decode_host_ans_sec(ans_cnt, &ptr, eom, msg.buf,
7467961SNatalie.Li@Sun.COM 	    hlist->ah_list) != 0) {
7477961SNatalie.Li@Sun.COM 		smb_ads_hlist_free(hlist);
7487961SNatalie.Li@Sun.COM 		res_ndestroy(&res_state);
7497961SNatalie.Li@Sun.COM 		return (NULL);
7507961SNatalie.Li@Sun.COM 	}
7517961SNatalie.Li@Sun.COM 
7527961SNatalie.Li@Sun.COM 	/* check authority section */
7537961SNatalie.Li@Sun.COM 	if (ns_cnt > 0) {
7547961SNatalie.Li@Sun.COM 		if (smb_ads_skip_auth_sec(ns_cnt, &ptr, eom) != 0) {
7557961SNatalie.Li@Sun.COM 			smb_ads_hlist_free(hlist);
7567961SNatalie.Li@Sun.COM 			res_ndestroy(&res_state);
7577961SNatalie.Li@Sun.COM 			return (NULL);
7587961SNatalie.Li@Sun.COM 		}
7597961SNatalie.Li@Sun.COM 	}
7607961SNatalie.Li@Sun.COM 
7617961SNatalie.Li@Sun.COM 	/*
7627961SNatalie.Li@Sun.COM 	 * Check additional section to get IP address of ADS host.
7637961SNatalie.Li@Sun.COM 	 */
7647961SNatalie.Li@Sun.COM 	if (addit_cnt > 0) {
7657961SNatalie.Li@Sun.COM 		if (smb_ads_decode_host_ip(addit_cnt, ans_cnt,
7667961SNatalie.Li@Sun.COM 		    &ptr, eom, msg.buf, hlist->ah_list) != 0) {
7677961SNatalie.Li@Sun.COM 			smb_ads_hlist_free(hlist);
7687961SNatalie.Li@Sun.COM 			res_ndestroy(&res_state);
7697961SNatalie.Li@Sun.COM 			return (NULL);
7707961SNatalie.Li@Sun.COM 		}
7717961SNatalie.Li@Sun.COM 	}
7727961SNatalie.Li@Sun.COM 
7737961SNatalie.Li@Sun.COM 	res_ndestroy(&res_state);
7747961SNatalie.Li@Sun.COM 	return (hlist);
7757961SNatalie.Li@Sun.COM }
7767961SNatalie.Li@Sun.COM 
7777961SNatalie.Li@Sun.COM /*
7789021Samw@Sun.COM  * smb_ads_get_site_service
7797961SNatalie.Li@Sun.COM  *
7809021Samw@Sun.COM  * Gets the msdcs SRV RR for the specified site.
7817961SNatalie.Li@Sun.COM  */
7827961SNatalie.Li@Sun.COM static void
smb_ads_get_site_service(char * site_service,size_t len)7839021Samw@Sun.COM smb_ads_get_site_service(char *site_service, size_t len)
7847961SNatalie.Li@Sun.COM {
7859832Samw@Sun.COM 	(void) mutex_lock(&smb_ads_cfg.c_mtx);
7869832Samw@Sun.COM 	if (*smb_ads_cfg.c_site == '\0')
7877052Samw 		*site_service = '\0';
7887052Samw 	else
7899021Samw@Sun.COM 		(void) snprintf(site_service, len,
7909832Samw@Sun.COM 		    SMB_ADS_MSDCS_SRV_SITE_RR, smb_ads_cfg.c_site);
7919832Samw@Sun.COM 
7929832Samw@Sun.COM 	(void) mutex_unlock(&smb_ads_cfg.c_mtx);
7937961SNatalie.Li@Sun.COM }
7947961SNatalie.Li@Sun.COM 
7957961SNatalie.Li@Sun.COM /*
7967961SNatalie.Li@Sun.COM  * smb_ads_getipnodebyname
7977961SNatalie.Li@Sun.COM  *
7987961SNatalie.Li@Sun.COM  * This method gets the IP address by doing a host name lookup.
7997961SNatalie.Li@Sun.COM  */
8007961SNatalie.Li@Sun.COM static int
smb_ads_getipnodebyname(smb_ads_host_info_t * hentry)8017961SNatalie.Li@Sun.COM smb_ads_getipnodebyname(smb_ads_host_info_t *hentry)
8027961SNatalie.Li@Sun.COM {
8037961SNatalie.Li@Sun.COM 	struct hostent *h;
8047961SNatalie.Li@Sun.COM 	int error;
8057961SNatalie.Li@Sun.COM 
8068670SJose.Borrego@Sun.COM 	switch (hentry->ipaddr.a_family) {
8078670SJose.Borrego@Sun.COM 	case AF_INET6:
8088670SJose.Borrego@Sun.COM 		h = getipnodebyname(hentry->name, hentry->ipaddr.a_family,
8098670SJose.Borrego@Sun.COM 		    AI_DEFAULT, &error);
8108670SJose.Borrego@Sun.COM 		if (h == NULL || h->h_length != IPV6_ADDR_LEN)
8118670SJose.Borrego@Sun.COM 			return (-1);
8128670SJose.Borrego@Sun.COM 		break;
8138670SJose.Borrego@Sun.COM 
8148670SJose.Borrego@Sun.COM 	case AF_INET:
8158670SJose.Borrego@Sun.COM 		h = getipnodebyname(hentry->name, hentry->ipaddr.a_family,
8168670SJose.Borrego@Sun.COM 		    0, &error);
8178670SJose.Borrego@Sun.COM 		if (h == NULL || h->h_length != INADDRSZ)
8188670SJose.Borrego@Sun.COM 			return (-1);
8198670SJose.Borrego@Sun.COM 		break;
8208670SJose.Borrego@Sun.COM 
8218670SJose.Borrego@Sun.COM 	default:
8227961SNatalie.Li@Sun.COM 		return (-1);
8238670SJose.Borrego@Sun.COM 	}
8248670SJose.Borrego@Sun.COM 	bcopy(*(h->h_addr_list), &hentry->ipaddr.a_ip, h->h_length);
8257961SNatalie.Li@Sun.COM 	freehostent(h);
8267961SNatalie.Li@Sun.COM 	return (0);
8277961SNatalie.Li@Sun.COM }
8287961SNatalie.Li@Sun.COM 
8297961SNatalie.Li@Sun.COM /*
83011633SJoyce.McIntosh@Sun.COM  *  Checks the IP address to see if it is zero.  If so, then do a host
83111633SJoyce.McIntosh@Sun.COM  *  lookup by hostname to get the IP address based on the IP family.
83211633SJoyce.McIntosh@Sun.COM  *
83311633SJoyce.McIntosh@Sun.COM  *  If the family is unknown then do a lookup by hostame based on the
83411633SJoyce.McIntosh@Sun.COM  *  setting of the SMB_CI_IPV6_ENABLE property.
83511633SJoyce.McIntosh@Sun.COM  */
83611633SJoyce.McIntosh@Sun.COM static int
smb_ads_set_ipaddr(smb_ads_host_info_t * hentry)83711633SJoyce.McIntosh@Sun.COM smb_ads_set_ipaddr(smb_ads_host_info_t *hentry)
83811633SJoyce.McIntosh@Sun.COM {
83911633SJoyce.McIntosh@Sun.COM 	if (smb_inet_iszero(&hentry->ipaddr)) {
84011633SJoyce.McIntosh@Sun.COM 		if (smb_ads_getipnodebyname(hentry) < 0)
84111633SJoyce.McIntosh@Sun.COM 			return (-1);
84211633SJoyce.McIntosh@Sun.COM 	} else if (SMB_ADS_AF_UNKNOWN(hentry)) {
84311633SJoyce.McIntosh@Sun.COM 		hentry->ipaddr.a_family =
84411633SJoyce.McIntosh@Sun.COM 		    smb_config_getbool(SMB_CI_IPV6_ENABLE) ? AF_INET6 : AF_INET;
84511633SJoyce.McIntosh@Sun.COM 
84611633SJoyce.McIntosh@Sun.COM 		if (smb_ads_getipnodebyname(hentry) < 0) {
84711633SJoyce.McIntosh@Sun.COM 			hentry->ipaddr.a_family = 0;
84811633SJoyce.McIntosh@Sun.COM 			return (-1);
84911633SJoyce.McIntosh@Sun.COM 		}
85011633SJoyce.McIntosh@Sun.COM 	}
85111633SJoyce.McIntosh@Sun.COM 
85211633SJoyce.McIntosh@Sun.COM 	return (0);
85311633SJoyce.McIntosh@Sun.COM }
85411633SJoyce.McIntosh@Sun.COM 
85511633SJoyce.McIntosh@Sun.COM /*
8567961SNatalie.Li@Sun.COM  * smb_ads_find_host
8577961SNatalie.Li@Sun.COM  *
8589832Samw@Sun.COM  * Finds an ADS host in a given domain.
8599832Samw@Sun.COM  *
8609832Samw@Sun.COM  * If the cached host is valid, it will be used. Otherwise, a DC will
8619832Samw@Sun.COM  * be selected based on the following criteria:
8629832Samw@Sun.COM  *
8639832Samw@Sun.COM  * 1) pdc (aka preferred DC) configuration
8649832Samw@Sun.COM  * 2) AD site configuration - the scope of the DNS lookup will be
8659832Samw@Sun.COM  * restricted to the specified site.
8669832Samw@Sun.COM  * 3) DC on the same subnet
8679832Samw@Sun.COM  * 4) DC with the lowest priority/highest weight
8689832Samw@Sun.COM  *
8699832Samw@Sun.COM  * The above items are listed in decreasing preference order. The selected
8709832Samw@Sun.COM  * DC must be online.
8719832Samw@Sun.COM  *
8729832Samw@Sun.COM  * If this function is called during domain join, the specified kpasswd server
8739832Samw@Sun.COM  * takes precedence over preferred DC, AD site, and so on.
8747961SNatalie.Li@Sun.COM  *
8757961SNatalie.Li@Sun.COM  * Parameters:
8769832Samw@Sun.COM  *   domain: fully-qualified domain name.
8779832Samw@Sun.COM  *   kpasswd_srv: fully-quailifed hostname of the kpasswd server.
8787961SNatalie.Li@Sun.COM  *
8797961SNatalie.Li@Sun.COM  * Returns:
8809832Samw@Sun.COM  *   A copy of the cached host info is returned. The caller is responsible
8819832Samw@Sun.COM  *   for deallocating the memory returned by this function.
8827961SNatalie.Li@Sun.COM  */
8837961SNatalie.Li@Sun.COM /*ARGSUSED*/
8847961SNatalie.Li@Sun.COM smb_ads_host_info_t *
smb_ads_find_host(char * domain,char * kpasswd_srv)8859832Samw@Sun.COM smb_ads_find_host(char *domain, char *kpasswd_srv)
8867961SNatalie.Li@Sun.COM {
8877961SNatalie.Li@Sun.COM 	int i;
8887961SNatalie.Li@Sun.COM 	char site_service[MAXHOSTNAMELEN];
8899832Samw@Sun.COM 	smb_ads_host_list_t *hlist, *hlist2;
8909832Samw@Sun.COM 	smb_ads_host_info_t *hlistp = NULL, *host = NULL;
8919832Samw@Sun.COM 	smb_ads_host_info_t *found_kpasswd_srv = NULL;
8929832Samw@Sun.COM 	smb_ads_host_info_t *found_pdc = NULL;
8939832Samw@Sun.COM 
8949832Samw@Sun.COM 	if ((kpasswd_srv) && (*kpasswd_srv == '\0'))
8959832Samw@Sun.COM 		kpasswd_srv = NULL;
8969832Samw@Sun.COM 
8979832Samw@Sun.COM 	(void) mutex_lock(&smb_ads_cached_host_mtx);
8989832Samw@Sun.COM 	if (smb_ads_validate_cache_host(domain, kpasswd_srv)) {
8999832Samw@Sun.COM 		host = smb_ads_dup_host_info(smb_ads_cached_host_info);
9009832Samw@Sun.COM 		(void) mutex_unlock(&smb_ads_cached_host_mtx);
9019832Samw@Sun.COM 		return (host);
9027961SNatalie.Li@Sun.COM 	}
9037961SNatalie.Li@Sun.COM 
9049832Samw@Sun.COM 	(void) mutex_unlock(&smb_ads_cached_host_mtx);
9059832Samw@Sun.COM 	smb_ads_free_cached_host();
9065331Samw 
9077052Samw 	/*
9087052Samw 	 * First look for ADS hosts in ADS site if configured.  Then try
9097052Samw 	 * without ADS site info.
9107052Samw 	 */
9119021Samw@Sun.COM 	hlist = NULL;
9129021Samw@Sun.COM 	smb_ads_get_site_service(site_service, MAXHOSTNAMELEN);
9139832Samw@Sun.COM 
9149832Samw@Sun.COM 	/*
9159832Samw@Sun.COM 	 * If we're given an AD, the DNS SRV RR lookup should not be restricted
9169832Samw@Sun.COM 	 * to the specified site since there is no guarantee that the specified
9179832Samw@Sun.COM 	 * AD is in the specified site.
9189832Samw@Sun.COM 	 */
9199832Samw@Sun.COM 	if (*site_service != '\0' && !kpasswd_srv &&
9209832Samw@Sun.COM 	    !smb_ads_is_pdc_configured())
9219021Samw@Sun.COM 		hlist = smb_ads_query_dns_server(domain, site_service);
9229021Samw@Sun.COM 
9239021Samw@Sun.COM 	if (!hlist)
9249021Samw@Sun.COM 		hlist = smb_ads_query_dns_server(domain,
9259021Samw@Sun.COM 		    SMB_ADS_MSDCS_SRV_DC_RR);
9267961SNatalie.Li@Sun.COM 
9277961SNatalie.Li@Sun.COM 	if ((hlist == NULL) || (hlist->ah_list == NULL) || (hlist->ah_cnt == 0))
9287961SNatalie.Li@Sun.COM 		return (NULL);
9299832Samw@Sun.COM 
9309832Samw@Sun.COM 	for (i = 0, hlistp = hlist->ah_list; i < hlist->ah_cnt; i++) {
93111633SJoyce.McIntosh@Sun.COM 		if (smb_ads_set_ipaddr(&hlistp[i]) < 0)
93211633SJoyce.McIntosh@Sun.COM 			continue;
9335331Samw 
9349832Samw@Sun.COM 		if (smb_ads_is_sought_host(&hlistp[i], kpasswd_srv))
9359832Samw@Sun.COM 			found_kpasswd_srv = &hlistp[i];
9369832Samw@Sun.COM 
9379832Samw@Sun.COM 		if (smb_ads_match_pdc(&hlistp[i]))
9389832Samw@Sun.COM 			found_pdc = &hlistp[i];
9399832Samw@Sun.COM 	}
9409832Samw@Sun.COM 
9419832Samw@Sun.COM 	if (found_kpasswd_srv && smb_ads_ldap_ping(found_kpasswd_srv) == 0) {
9429832Samw@Sun.COM 		host = found_kpasswd_srv;
9439832Samw@Sun.COM 		goto update_cache;
9449832Samw@Sun.COM 	}
9459832Samw@Sun.COM 
9469832Samw@Sun.COM 	if (found_pdc && smb_ads_ldap_ping(found_pdc) == 0) {
9479832Samw@Sun.COM 		host = found_pdc;
9489832Samw@Sun.COM 		goto update_cache;
9499832Samw@Sun.COM 	}
9509832Samw@Sun.COM 
9519832Samw@Sun.COM 	/*
9529832Samw@Sun.COM 	 * If the specified DC (kpasswd_srv or pdc) is not found, fallback
9539832Samw@Sun.COM 	 * to find a DC in the specified AD site.
9549832Samw@Sun.COM 	 */
9559832Samw@Sun.COM 	if (*site_service != '\0' &&
9569832Samw@Sun.COM 	    (kpasswd_srv || smb_ads_is_pdc_configured())) {
9579832Samw@Sun.COM 		hlist2 = smb_ads_query_dns_server(domain, site_service);
9589832Samw@Sun.COM 		if (hlist2 && hlist2->ah_list && hlist2->ah_cnt != 0) {
9597961SNatalie.Li@Sun.COM 			smb_ads_hlist_free(hlist);
9609832Samw@Sun.COM 			hlist = hlist2;
9619832Samw@Sun.COM 			hlistp = hlist->ah_list;
9629832Samw@Sun.COM 
96311633SJoyce.McIntosh@Sun.COM 			for (i = 0; i < hlist->ah_cnt; i++)
96411633SJoyce.McIntosh@Sun.COM 				(void) smb_ads_set_ipaddr(&hlistp[i]);
9656849Sjb150015 		}
9665331Samw 	}
9677961SNatalie.Li@Sun.COM 
9687961SNatalie.Li@Sun.COM 	/* Select DC from DC list */
9699832Samw@Sun.COM 	host = smb_ads_select_dc(hlist);
9709832Samw@Sun.COM 
9719832Samw@Sun.COM update_cache:
9729832Samw@Sun.COM 	if (host) {
9739832Samw@Sun.COM 		(void) mutex_lock(&smb_ads_cached_host_mtx);
9749832Samw@Sun.COM 		if (!smb_ads_cached_host_info)
9759832Samw@Sun.COM 			smb_ads_cached_host_info = smb_ads_dup_host_info(host);
9769832Samw@Sun.COM 		host = smb_ads_dup_host_info(smb_ads_cached_host_info);
9779832Samw@Sun.COM 		(void) mutex_unlock(&smb_ads_cached_host_mtx);
9787961SNatalie.Li@Sun.COM 	}
9797961SNatalie.Li@Sun.COM 
9807961SNatalie.Li@Sun.COM 	smb_ads_hlist_free(hlist);
9819832Samw@Sun.COM 	return (host);
9825331Samw }
9835331Samw 
9845331Samw /*
9857961SNatalie.Li@Sun.COM  * Return the number of dots in a string.
9865331Samw  */
9877961SNatalie.Li@Sun.COM static int
smb_ads_count_dots(const char * s)9887961SNatalie.Li@Sun.COM smb_ads_count_dots(const char *s)
9895331Samw {
9907961SNatalie.Li@Sun.COM 	int ndots = 0;
9917961SNatalie.Li@Sun.COM 
9927961SNatalie.Li@Sun.COM 	while (*s) {
9937961SNatalie.Li@Sun.COM 		if (*s++ == '.')
9947961SNatalie.Li@Sun.COM 			ndots++;
9955331Samw 	}
9965331Samw 
9977961SNatalie.Li@Sun.COM 	return (ndots);
9985331Samw }
9995331Samw 
10005331Samw /*
10017961SNatalie.Li@Sun.COM  * Convert a domain name in dot notation to distinguished name format,
10027961SNatalie.Li@Sun.COM  * for example: sun.com -> dc=sun,dc=com.
10037961SNatalie.Li@Sun.COM  *
10047961SNatalie.Li@Sun.COM  * Returns a pointer to an allocated buffer containing the distinguished
10057961SNatalie.Li@Sun.COM  * name.
10067961SNatalie.Li@Sun.COM  */
10077961SNatalie.Li@Sun.COM static char *
smb_ads_convert_domain(const char * domain_name)10087961SNatalie.Li@Sun.COM smb_ads_convert_domain(const char *domain_name)
10097961SNatalie.Li@Sun.COM {
10107961SNatalie.Li@Sun.COM 	const char *s;
10117961SNatalie.Li@Sun.COM 	char *dn_name;
10127961SNatalie.Li@Sun.COM 	char buf[2];
10137961SNatalie.Li@Sun.COM 	int ndots;
10147961SNatalie.Li@Sun.COM 	int len;
10157961SNatalie.Li@Sun.COM 
10167961SNatalie.Li@Sun.COM 	if (domain_name == NULL || *domain_name == 0)
10177961SNatalie.Li@Sun.COM 		return (NULL);
10187961SNatalie.Li@Sun.COM 
10197961SNatalie.Li@Sun.COM 	ndots = smb_ads_count_dots(domain_name);
10207961SNatalie.Li@Sun.COM 	++ndots;
10217961SNatalie.Li@Sun.COM 	len = strlen(domain_name) + (ndots * SMB_ADS_DN_PREFIX_LEN) + 1;
10227961SNatalie.Li@Sun.COM 
10237961SNatalie.Li@Sun.COM 	if ((dn_name = malloc(len)) == NULL)
10247961SNatalie.Li@Sun.COM 		return (NULL);
10257961SNatalie.Li@Sun.COM 
10267961SNatalie.Li@Sun.COM 	bzero(dn_name, len);
10277961SNatalie.Li@Sun.COM 	(void) strlcpy(dn_name, "dc=", len);
10287961SNatalie.Li@Sun.COM 
10297961SNatalie.Li@Sun.COM 	buf[1] = '\0';
10307961SNatalie.Li@Sun.COM 	s = domain_name;
10317961SNatalie.Li@Sun.COM 
10327961SNatalie.Li@Sun.COM 	while (*s) {
10337961SNatalie.Li@Sun.COM 		if (*s == '.') {
10347961SNatalie.Li@Sun.COM 			(void) strlcat(dn_name, ",dc=", len);
10357961SNatalie.Li@Sun.COM 		} else {
10367961SNatalie.Li@Sun.COM 			buf[0] = *s;
10377961SNatalie.Li@Sun.COM 			(void) strlcat(dn_name, buf, len);
10387961SNatalie.Li@Sun.COM 		}
10397961SNatalie.Li@Sun.COM 		++s;
10407961SNatalie.Li@Sun.COM 	}
10417961SNatalie.Li@Sun.COM 
10427961SNatalie.Li@Sun.COM 	return (dn_name);
10437961SNatalie.Li@Sun.COM }
10447961SNatalie.Li@Sun.COM 
10457961SNatalie.Li@Sun.COM /*
10467961SNatalie.Li@Sun.COM  * smb_ads_free_cached_host
10477961SNatalie.Li@Sun.COM  *
10487961SNatalie.Li@Sun.COM  * Free the memory use by the global smb_ads_cached_host_info & set it to NULL.
10495331Samw  */
10505772Sas200622 static void
smb_ads_free_cached_host(void)10517961SNatalie.Li@Sun.COM smb_ads_free_cached_host(void)
10525331Samw {
10537961SNatalie.Li@Sun.COM 	(void) mutex_lock(&smb_ads_cached_host_mtx);
10547961SNatalie.Li@Sun.COM 	if (smb_ads_cached_host_info) {
10557961SNatalie.Li@Sun.COM 		free(smb_ads_cached_host_info);
10567961SNatalie.Li@Sun.COM 		smb_ads_cached_host_info = NULL;
10575331Samw 	}
10587961SNatalie.Li@Sun.COM 	(void) mutex_unlock(&smb_ads_cached_host_mtx);
10595331Samw }
10605331Samw 
10615331Samw /*
10627052Samw  * smb_ads_open
10635521Sas200622  * Open a LDAP connection to an ADS server if the system is in domain mode.
10645521Sas200622  * Acquire both Kerberos TGT and LDAP service tickets for the host principal.
10655521Sas200622  *
10665521Sas200622  * This function should only be called after the system is successfully joined
10675521Sas200622  * to a domain.
10685521Sas200622  */
10697052Samw smb_ads_handle_t *
smb_ads_open(void)10707052Samw smb_ads_open(void)
10715521Sas200622 {
10725772Sas200622 	char domain[MAXHOSTNAMELEN];
10735521Sas200622 
10745772Sas200622 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
10755521Sas200622 		return (NULL);
10765521Sas200622 
10775772Sas200622 	if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0)
10785772Sas200622 		return (NULL);
10795772Sas200622 
10807052Samw 	return (smb_ads_open_main(domain, NULL, NULL));
10815521Sas200622 }
10825521Sas200622 
108310717Samw@Sun.COM static int
smb_ads_saslcallback(LDAP * ld,unsigned flags,void * defaults,void * prompts)108410717Samw@Sun.COM smb_ads_saslcallback(LDAP *ld, unsigned flags, void *defaults, void *prompts)
108510717Samw@Sun.COM {
108610717Samw@Sun.COM 	NOTE(ARGUNUSED(ld, defaults));
108710717Samw@Sun.COM 	sasl_interact_t *interact;
108810717Samw@Sun.COM 
108910717Samw@Sun.COM 	if (prompts == NULL || flags != LDAP_SASL_INTERACTIVE)
109010717Samw@Sun.COM 		return (LDAP_PARAM_ERROR);
109110717Samw@Sun.COM 
109210717Samw@Sun.COM 	/* There should be no extra arguemnts for SASL/GSSAPI authentication */
109310717Samw@Sun.COM 	for (interact = prompts; interact->id != SASL_CB_LIST_END;
109410717Samw@Sun.COM 	    interact++) {
109510717Samw@Sun.COM 		interact->result = NULL;
109610717Samw@Sun.COM 		interact->len = 0;
109710717Samw@Sun.COM 	}
109810717Samw@Sun.COM 	return (LDAP_SUCCESS);
109910717Samw@Sun.COM }
110010717Samw@Sun.COM 
11015521Sas200622 /*
11027052Samw  * smb_ads_open_main
11035331Samw  * Open a LDAP connection to an ADS server.
11045521Sas200622  * If ADS is enabled and the administrative username, password, and
11055331Samw  * ADS domain are defined then query DNS to find an ADS server if this is the
11065331Samw  * very first call to this routine.  After an ADS server is found then this
11075331Samw  * server will be used everytime this routine is called until the system is
11085331Samw  * rebooted or the ADS server becomes unavailable then an ADS server will
11097052Samw  * be queried again.  After the connection is made then an ADS handle
11105331Samw  * is created to be returned.
11115331Samw  *
11125331Samw  * After the LDAP connection, the LDAP version will be set to 3 using
11135331Samw  * ldap_set_option().
11145331Samw  *
111510717Samw@Sun.COM  * The LDAP connection is bound before the ADS handle is returned.
11165331Samw  * Parameters:
11175772Sas200622  *   domain - fully-qualified domain name
11185772Sas200622  *   user   - the user account for whom the Kerberos TGT ticket and ADS
11195772Sas200622  *            service tickets are acquired.
11205772Sas200622  *   password - password of the specified user
11215772Sas200622  *
11225331Samw  * Returns:
11237052Samw  *   NULL              : can't connect to ADS server or other errors
11247052Samw  *   smb_ads_handle_t* : handle to ADS server
11255331Samw  */
11267052Samw static smb_ads_handle_t *
smb_ads_open_main(char * domain,char * user,char * password)11277052Samw smb_ads_open_main(char *domain, char *user, char *password)
11285331Samw {
11297052Samw 	smb_ads_handle_t *ah;
11305331Samw 	LDAP *ld;
11317961SNatalie.Li@Sun.COM 	int version = 3;
11327052Samw 	smb_ads_host_info_t *ads_host = NULL;
113310717Samw@Sun.COM 	int rc;
113410717Samw@Sun.COM 
113510717Samw@Sun.COM 	if (user != NULL) {
113610717Samw@Sun.COM 		if (smb_kinit(user, password) == 0)
113710717Samw@Sun.COM 			return (NULL);
113810717Samw@Sun.COM 		user = NULL;
113910717Samw@Sun.COM 		password = NULL;
114010717Samw@Sun.COM 	}
11415331Samw 
11427961SNatalie.Li@Sun.COM 	ads_host = smb_ads_find_host(domain, NULL);
11437961SNatalie.Li@Sun.COM 	if (ads_host == NULL)
11447961SNatalie.Li@Sun.COM 		return (NULL);
11457961SNatalie.Li@Sun.COM 
11467052Samw 	ah = (smb_ads_handle_t *)malloc(sizeof (smb_ads_handle_t));
11479832Samw@Sun.COM 	if (ah == NULL) {
11489832Samw@Sun.COM 		free(ads_host);
11495331Samw 		return (NULL);
11509832Samw@Sun.COM 	}
11519832Samw@Sun.COM 
11527052Samw 	(void) memset(ah, 0, sizeof (smb_ads_handle_t));
11535331Samw 
11548670SJose.Borrego@Sun.COM 	if ((ld = ldap_init(ads_host->name, ads_host->port)) == NULL) {
11557961SNatalie.Li@Sun.COM 		smb_ads_free_cached_host();
11565331Samw 		free(ah);
11579832Samw@Sun.COM 		free(ads_host);
11585331Samw 		return (NULL);
11595331Samw 	}
11605331Samw 
11615331Samw 	if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version)
11625331Samw 	    != LDAP_SUCCESS) {
11637961SNatalie.Li@Sun.COM 		smb_ads_free_cached_host();
11645331Samw 		free(ah);
11659832Samw@Sun.COM 		free(ads_host);
11665331Samw 		(void) ldap_unbind(ld);
11675331Samw 		return (NULL);
11685331Samw 	}
11695331Samw 
11707348SJose.Borrego@Sun.COM 	(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
11715331Samw 	ah->ld = ld;
11725331Samw 	ah->domain = strdup(domain);
11735331Samw 
11745521Sas200622 	if (ah->domain == NULL) {
11757052Samw 		smb_ads_close(ah);
11769832Samw@Sun.COM 		free(ads_host);
11775331Samw 		return (NULL);
11785331Samw 	}
11795331Samw 
118011963SAfshin.Ardakani@Sun.COM 	/*
118111963SAfshin.Ardakani@Sun.COM 	 * ah->domain is often used for generating service principal name.
118211963SAfshin.Ardakani@Sun.COM 	 * Convert it to lower case for RFC 4120 section 6.2.1 conformance.
118311963SAfshin.Ardakani@Sun.COM 	 */
118411963SAfshin.Ardakani@Sun.COM 	(void) smb_strlwr(ah->domain);
11857052Samw 	ah->domain_dn = smb_ads_convert_domain(domain);
11865331Samw 	if (ah->domain_dn == NULL) {
11877052Samw 		smb_ads_close(ah);
11889832Samw@Sun.COM 		free(ads_host);
11895331Samw 		return (NULL);
11905331Samw 	}
11915331Samw 
11925331Samw 	ah->hostname = strdup(ads_host->name);
11935331Samw 	if (ah->hostname == NULL) {
11947052Samw 		smb_ads_close(ah);
11959832Samw@Sun.COM 		free(ads_host);
11965331Samw 		return (NULL);
11975331Samw 	}
11989832Samw@Sun.COM 	(void) mutex_lock(&smb_ads_cfg.c_mtx);
11999832Samw@Sun.COM 	if (*smb_ads_cfg.c_site != '\0') {
12009832Samw@Sun.COM 		if ((ah->site = strdup(smb_ads_cfg.c_site)) == NULL) {
12017052Samw 			smb_ads_close(ah);
12029832Samw@Sun.COM 			(void) mutex_unlock(&smb_ads_cfg.c_mtx);
12039832Samw@Sun.COM 			free(ads_host);
12045331Samw 			return (NULL);
12055331Samw 		}
12065331Samw 	} else {
12075331Samw 		ah->site = NULL;
12085331Samw 	}
12099832Samw@Sun.COM 	(void) mutex_unlock(&smb_ads_cfg.c_mtx);
12105331Samw 
121110717Samw@Sun.COM 	rc = ldap_sasl_interactive_bind_s(ah->ld, "", "GSSAPI", NULL, NULL,
121210717Samw@Sun.COM 	    LDAP_SASL_INTERACTIVE, &smb_ads_saslcallback, NULL);
121310717Samw@Sun.COM 	if (rc != LDAP_SUCCESS) {
121410717Samw@Sun.COM 		syslog(LOG_ERR, "ldal_sasl_interactive_bind_s failed (%s)",
121510717Samw@Sun.COM 		    ldap_err2string(rc));
12167052Samw 		smb_ads_close(ah);
12179832Samw@Sun.COM 		free(ads_host);
12185331Samw 		return (NULL);
12195331Samw 	}
12205331Samw 
12219832Samw@Sun.COM 	free(ads_host);
12225331Samw 	return (ah);
12235331Samw }
12245331Samw 
12255331Samw /*
12267052Samw  * smb_ads_close
12275331Samw  * Close connection to ADS server and free memory allocated for ADS handle.
12285331Samw  * LDAP unbind is called here.
12295331Samw  * Parameters:
12305331Samw  *   ah: handle to ADS server
12315331Samw  * Returns:
12325331Samw  *   void
12335331Samw  */
12345331Samw void
smb_ads_close(smb_ads_handle_t * ah)12357052Samw smb_ads_close(smb_ads_handle_t *ah)
12365331Samw {
12375331Samw 	if (ah == NULL)
12385331Samw 		return;
12395331Samw 	/* close and free connection resources */
12405331Samw 	if (ah->ld)
12415331Samw 		(void) ldap_unbind(ah->ld);
12425331Samw 
12435331Samw 	free(ah->domain);
12445331Samw 	free(ah->domain_dn);
12455331Samw 	free(ah->hostname);
12465331Samw 	free(ah->site);
12475331Samw 	free(ah);
12485331Samw }
12495331Samw 
12505331Samw /*
12517052Samw  * smb_ads_alloc_attr
12526432Sas200622  *
12536432Sas200622  * Since the attrs is a null-terminated array, all elements
12546432Sas200622  * in the array (except the last one) will point to allocated
12556432Sas200622  * memory.
12566432Sas200622  */
12576432Sas200622 static int
smb_ads_alloc_attr(LDAPMod * attrs[],int num)12587052Samw smb_ads_alloc_attr(LDAPMod *attrs[], int num)
12596432Sas200622 {
12606432Sas200622 	int i;
12616432Sas200622 
12626432Sas200622 	bzero(attrs, num * sizeof (LDAPMod *));
12636432Sas200622 	for (i = 0; i < (num - 1); i++) {
12646432Sas200622 		attrs[i] = (LDAPMod *)malloc(sizeof (LDAPMod));
12656432Sas200622 		if (attrs[i] == NULL) {
12667052Samw 			smb_ads_free_attr(attrs);
12676432Sas200622 			return (-1);
12686432Sas200622 		}
12696432Sas200622 	}
12706432Sas200622 
12716432Sas200622 	return (0);
12726432Sas200622 }
12736432Sas200622 
12746432Sas200622 /*
12757052Samw  * smb_ads_free_attr
12765331Samw  * Free memory allocated when publishing a share.
12775331Samw  * Parameters:
12785521Sas200622  *   attrs: an array of LDAPMod pointers
12795331Samw  * Returns:
12805331Samw  *   None
12815331Samw  */
12825331Samw static void
smb_ads_free_attr(LDAPMod * attrs[])12837052Samw smb_ads_free_attr(LDAPMod *attrs[])
12845331Samw {
12855331Samw 	int i;
12865521Sas200622 	for (i = 0; attrs[i]; i++) {
12875521Sas200622 		free(attrs[i]);
12885331Samw 	}
12895331Samw }
12905331Samw 
12915331Samw /*
129211337SWilliam.Krier@Sun.COM  * Returns share DN in an allocated buffer.  The format of the DN is
129311337SWilliam.Krier@Sun.COM  * cn=<sharename>,<container RDNs>,<domain DN>
129411337SWilliam.Krier@Sun.COM  *
129511337SWilliam.Krier@Sun.COM  * If the domain DN is not included in the container parameter,
129611337SWilliam.Krier@Sun.COM  * then it will be appended to create the share DN.
129711337SWilliam.Krier@Sun.COM  *
129811337SWilliam.Krier@Sun.COM  * The caller must free the allocated buffer.
129911337SWilliam.Krier@Sun.COM  */
130011337SWilliam.Krier@Sun.COM static char *
smb_ads_get_sharedn(const char * sharename,const char * container,const char * domain_dn)130111337SWilliam.Krier@Sun.COM smb_ads_get_sharedn(const char *sharename, const char *container,
130211337SWilliam.Krier@Sun.COM     const char *domain_dn)
130311337SWilliam.Krier@Sun.COM {
130411337SWilliam.Krier@Sun.COM 	char *share_dn;
130511337SWilliam.Krier@Sun.COM 	int rc, offset, container_len, domain_len;
130611337SWilliam.Krier@Sun.COM 	boolean_t append_domain = B_TRUE;
130711337SWilliam.Krier@Sun.COM 
130811337SWilliam.Krier@Sun.COM 	container_len = strlen(container);
130911337SWilliam.Krier@Sun.COM 	domain_len = strlen(domain_dn);
131011337SWilliam.Krier@Sun.COM 
131111337SWilliam.Krier@Sun.COM 	if (container_len >= domain_len) {
131211337SWilliam.Krier@Sun.COM 
131311337SWilliam.Krier@Sun.COM 		/* offset to last domain_len characters */
131411337SWilliam.Krier@Sun.COM 		offset = container_len - domain_len;
131511337SWilliam.Krier@Sun.COM 
131611337SWilliam.Krier@Sun.COM 		if (smb_strcasecmp(container + offset,
131711337SWilliam.Krier@Sun.COM 		    domain_dn, domain_len) == 0)
131811337SWilliam.Krier@Sun.COM 			append_domain = B_FALSE;
131911337SWilliam.Krier@Sun.COM 	}
132011337SWilliam.Krier@Sun.COM 
132111337SWilliam.Krier@Sun.COM 	if (append_domain)
132211337SWilliam.Krier@Sun.COM 		rc = asprintf(&share_dn, "cn=%s,%s,%s", sharename,
132311337SWilliam.Krier@Sun.COM 		    container, domain_dn);
132411337SWilliam.Krier@Sun.COM 	else
132511337SWilliam.Krier@Sun.COM 		rc = asprintf(&share_dn, "cn=%s,%s", sharename,
132611337SWilliam.Krier@Sun.COM 		    container);
132711337SWilliam.Krier@Sun.COM 
132811337SWilliam.Krier@Sun.COM 	return ((rc == -1) ? NULL : share_dn);
132911337SWilliam.Krier@Sun.COM }
133011337SWilliam.Krier@Sun.COM 
133111337SWilliam.Krier@Sun.COM /*
13327052Samw  * smb_ads_add_share
13337052Samw  * Call by smb_ads_publish_share to create share object in ADS.
13345331Samw  * This routine specifies the attributes of an ADS LDAP share object. The first
13355331Samw  * attribute and values define the type of ADS object, the share object.  The
13365331Samw  * second attribute and value define the UNC of the share data for the share
13375331Samw  * object. The LDAP synchronous add command is used to add the object into ADS.
13385331Samw  * The container location to add the object needs to specified.
13395331Samw  * Parameters:
13405331Samw  *   ah          : handle to ADS server
13415331Samw  *   adsShareName: name of share object to be created in ADS
13425331Samw  *   shareUNC    : share name on NetForce
13435331Samw  *   adsContainer: location in ADS to create share object
13445331Samw  *
13455331Samw  * Returns:
13465331Samw  *   -1          : error
13475331Samw  *    0          : success
13485331Samw  */
13495331Samw int
smb_ads_add_share(smb_ads_handle_t * ah,const char * adsShareName,const char * unc_name,const char * adsContainer)13507052Samw smb_ads_add_share(smb_ads_handle_t *ah, const char *adsShareName,
13515331Samw     const char *unc_name, const char *adsContainer)
13525331Samw {
13537052Samw 	LDAPMod *attrs[SMB_ADS_SHARE_NUM_ATTR];
13545521Sas200622 	int j = 0;
13555331Samw 	char *share_dn;
135611337SWilliam.Krier@Sun.COM 	int ret;
13578670SJose.Borrego@Sun.COM 	char *unc_names[] = {(char *)unc_name, NULL};
13585331Samw 
135911337SWilliam.Krier@Sun.COM 	if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer,
136011337SWilliam.Krier@Sun.COM 	    ah->domain_dn)) == NULL)
13615331Samw 		return (-1);
13625331Samw 
13637052Samw 	if (smb_ads_alloc_attr(attrs, SMB_ADS_SHARE_NUM_ATTR) != 0) {
13646432Sas200622 		free(share_dn);
13656432Sas200622 		return (-1);
13665521Sas200622 	}
13675521Sas200622 
13685521Sas200622 	attrs[j]->mod_op = LDAP_MOD_ADD;
13695521Sas200622 	attrs[j]->mod_type = "objectClass";
13708670SJose.Borrego@Sun.COM 	attrs[j]->mod_values = smb_ads_share_objcls;
13715331Samw 
13725521Sas200622 	attrs[++j]->mod_op = LDAP_MOD_ADD;
13735521Sas200622 	attrs[j]->mod_type = "uNCName";
13748670SJose.Borrego@Sun.COM 	attrs[j]->mod_values = unc_names;
13755331Samw 
13765521Sas200622 	if ((ret = ldap_add_s(ah->ld, share_dn, attrs)) != LDAP_SUCCESS) {
137711963SAfshin.Ardakani@Sun.COM 		if (ret == LDAP_NO_SUCH_OBJECT) {
137811963SAfshin.Ardakani@Sun.COM 			syslog(LOG_ERR, "Failed to publish share %s in" \
137911963SAfshin.Ardakani@Sun.COM 			    " AD.  Container does not exist: %s.\n",
138011963SAfshin.Ardakani@Sun.COM 			    adsShareName, share_dn);
138111963SAfshin.Ardakani@Sun.COM 
138211963SAfshin.Ardakani@Sun.COM 		} else {
138311963SAfshin.Ardakani@Sun.COM 			syslog(LOG_ERR, "Failed to publish share %s in" \
138411963SAfshin.Ardakani@Sun.COM 			    " AD: %s (%s).\n", adsShareName, share_dn,
138511963SAfshin.Ardakani@Sun.COM 			    ldap_err2string(ret));
138611963SAfshin.Ardakani@Sun.COM 		}
13877052Samw 		smb_ads_free_attr(attrs);
13885331Samw 		free(share_dn);
13895331Samw 		return (ret);
13905331Samw 	}
13915331Samw 	free(share_dn);
13927052Samw 	smb_ads_free_attr(attrs);
13935331Samw 
13945331Samw 	return (0);
13955331Samw }
13965331Samw 
13975331Samw /*
13987052Samw  * smb_ads_del_share
13997052Samw  * Call by smb_ads_remove_share to remove share object from ADS.  The container
14005331Samw  * location to remove the object needs to specified.  The LDAP synchronous
14015331Samw  * delete command is used.
14025331Samw  * Parameters:
14035331Samw  *   ah          : handle to ADS server
14045331Samw  *   adsShareName: name of share object in ADS to be removed
14055331Samw  *   adsContainer: location of share object in ADS
14065331Samw  * Returns:
14075331Samw  *   -1          : error
14085331Samw  *    0          : success
14095331Samw  */
14105331Samw static int
smb_ads_del_share(smb_ads_handle_t * ah,const char * adsShareName,const char * adsContainer)14117052Samw smb_ads_del_share(smb_ads_handle_t *ah, const char *adsShareName,
14125331Samw     const char *adsContainer)
14135331Samw {
14147961SNatalie.Li@Sun.COM 	char *share_dn;
141511337SWilliam.Krier@Sun.COM 	int ret;
14165331Samw 
141711337SWilliam.Krier@Sun.COM 	if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer,
141811337SWilliam.Krier@Sun.COM 	    ah->domain_dn)) == NULL)
14195331Samw 		return (-1);
14205331Samw 
14215331Samw 	if ((ret = ldap_delete_s(ah->ld, share_dn)) != LDAP_SUCCESS) {
14229021Samw@Sun.COM 		smb_tracef("ldap_delete: %s", ldap_err2string(ret));
14235331Samw 		free(share_dn);
14245331Samw 		return (-1);
14255331Samw 	}
14265331Samw 	free(share_dn);
14275331Samw 
14285331Samw 	return (0);
14295331Samw }
14305331Samw 
14315331Samw 
14325331Samw /*
14337052Samw  * smb_ads_escape_search_filter_chars
14345331Samw  *
14355331Samw  * This routine will escape the special characters found in a string
14365331Samw  * that will later be passed to the ldap search filter.
14375331Samw  *
14385331Samw  * RFC 1960 - A String Representation of LDAP Search Filters
14395331Samw  * 3.  String Search Filter Definition
14405331Samw  * If a value must contain one of the characters '*' OR '(' OR ')',
14415331Samw  * these characters
14425331Samw  * should be escaped by preceding them with the backslash '\' character.
14435331Samw  *
14445331Samw  * RFC 2252 - LDAP Attribute Syntax Definitions
14455331Samw  * a backslash quoting mechanism is used to escape
14465331Samw  * the following separator symbol character (such as "'", "$" or "#") if
14475331Samw  * it should occur in that string.
14485331Samw  */
14495331Samw static int
smb_ads_escape_search_filter_chars(const char * src,char * dst)14507052Samw smb_ads_escape_search_filter_chars(const char *src, char *dst)
14515331Samw {
14527052Samw 	int avail = SMB_ADS_MAXBUFLEN - 1; /* reserve a space for NULL char */
14535331Samw 
14545331Samw 	if (src == NULL || dst == NULL)
14555331Samw 		return (-1);
14565331Samw 
14575331Samw 	while (*src) {
14585331Samw 		if (!avail) {
14595331Samw 			*dst = 0;
14605331Samw 			return (-1);
14615331Samw 		}
14625331Samw 
14635331Samw 		switch (*src) {
14645331Samw 		case '\\':
14655331Samw 		case '\'':
14665331Samw 		case '$':
14675331Samw 		case '#':
14685331Samw 		case '*':
14695331Samw 		case '(':
14705331Samw 		case ')':
14715331Samw 			*dst++ = '\\';
14725331Samw 			avail--;
14735331Samw 			/* fall through */
14745331Samw 
14755331Samw 		default:
14765331Samw 			*dst++ = *src++;
14775331Samw 			avail--;
14785331Samw 		}
14795331Samw 	}
14805331Samw 
14815331Samw 	*dst = 0;
14825331Samw 
14835331Samw 	return (0);
14845331Samw }
14855331Samw 
14865331Samw /*
14877052Samw  * smb_ads_lookup_share
14885331Samw  * The search filter is set to search for a specific share name in the
14895331Samw  * specified ADS container.  The LDSAP synchronous search command is used.
14905331Samw  * Parameters:
14915331Samw  *   ah          : handle to ADS server
14925331Samw  *   adsShareName: name of share object in ADS to be searched
14935331Samw  *   adsContainer: location of share object in ADS
14945331Samw  * Returns:
14955331Samw  *   -1          : error
14965331Samw  *    0          : not found
14975331Samw  *    1          : found
14985331Samw  */
14995331Samw int
smb_ads_lookup_share(smb_ads_handle_t * ah,const char * adsShareName,const char * adsContainer,char * unc_name)15007052Samw smb_ads_lookup_share(smb_ads_handle_t *ah, const char *adsShareName,
15015331Samw     const char *adsContainer, char *unc_name)
15025331Samw {
15037052Samw 	char *attrs[4], filter[SMB_ADS_MAXBUFLEN];
15045331Samw 	char *share_dn;
150511337SWilliam.Krier@Sun.COM 	int ret;
15065331Samw 	LDAPMessage *res;
15077052Samw 	char tmpbuf[SMB_ADS_MAXBUFLEN];
15085331Samw 
15095331Samw 	if (adsShareName == NULL || adsContainer == NULL)
15105331Samw 		return (-1);
15115331Samw 
151211337SWilliam.Krier@Sun.COM 	if ((share_dn = smb_ads_get_sharedn(adsShareName, adsContainer,
151311337SWilliam.Krier@Sun.COM 	    ah->domain_dn)) == NULL)
15145331Samw 		return (-1);
15155331Samw 
15165331Samw 	res = NULL;
15175331Samw 	attrs[0] = "cn";
15185331Samw 	attrs[1] = "objectClass";
15195331Samw 	attrs[2] = "uNCName";
15205331Samw 	attrs[3] = NULL;
15215331Samw 
15227052Samw 	if (smb_ads_escape_search_filter_chars(unc_name, tmpbuf) != 0) {
15235331Samw 		free(share_dn);
15245331Samw 		return (-1);
15255331Samw 	}
15265331Samw 
15275331Samw 	(void) snprintf(filter, sizeof (filter),
15285331Samw 	    "(&(objectClass=volume)(uNCName=%s))", tmpbuf);
15295331Samw 
15305331Samw 	if ((ret = ldap_search_s(ah->ld, share_dn,
15315331Samw 	    LDAP_SCOPE_BASE, filter, attrs, 0, &res)) != LDAP_SUCCESS) {
15327961SNatalie.Li@Sun.COM 		if (ret != LDAP_NO_SUCH_OBJECT)
15339021Samw@Sun.COM 			smb_tracef("%s: ldap_search: %s", share_dn,
15349021Samw@Sun.COM 			    ldap_err2string(ret));
15357961SNatalie.Li@Sun.COM 
15365331Samw 		(void) ldap_msgfree(res);
15375331Samw 		free(share_dn);
15385331Samw 		return (0);
15395331Samw 	}
15405331Samw 
15415331Samw 	(void) free(share_dn);
15425331Samw 
15435331Samw 	/* no match is found */
15445331Samw 	if (ldap_count_entries(ah->ld, res) == 0) {
15455331Samw 		(void) ldap_msgfree(res);
15465331Samw 		return (0);
15475331Samw 	}
15485331Samw 
15495331Samw 	/* free the search results */
15505331Samw 	(void) ldap_msgfree(res);
15515331Samw 
15525331Samw 	return (1);
15535331Samw }
15545331Samw 
15555331Samw /*
15567052Samw  * smb_ads_publish_share
15575331Samw  * Publish share into ADS.  If a share name already exist in ADS in the same
15585331Samw  * container then the existing share object is removed before adding the new
15595331Samw  * share object.
15605331Samw  * Parameters:
15617052Samw  *   ah          : handle return from smb_ads_open
15625331Samw  *   adsShareName: name of share to be added to ADS directory
15635331Samw  *   shareUNC    : name of share on client, can be NULL to use the same name
15645331Samw  *                 as adsShareName
15655331Samw  *   adsContainer: location for share to be added in ADS directory, ie
15665331Samw  *                   ou=share_folder
15675331Samw  *   uncType     : use UNC_HOSTNAME to use hostname for UNC, use UNC_HOSTADDR
15685331Samw  *                   to use host ip addr for UNC.
15695331Samw  * Returns:
15705331Samw  *   -1          : error
15715331Samw  *    0          : success
15725331Samw  */
15735331Samw int
smb_ads_publish_share(smb_ads_handle_t * ah,const char * adsShareName,const char * shareUNC,const char * adsContainer,const char * hostname)15747052Samw smb_ads_publish_share(smb_ads_handle_t *ah, const char *adsShareName,
15755331Samw     const char *shareUNC, const char *adsContainer, const char *hostname)
15765331Samw {
15775331Samw 	int ret;
15787052Samw 	char unc_name[SMB_ADS_MAXBUFLEN];
15795331Samw 
15805331Samw 	if (adsShareName == NULL || adsContainer == NULL)
15815331Samw 		return (-1);
15825331Samw 
15835331Samw 	if (shareUNC == 0 || *shareUNC == 0)
15845331Samw 		shareUNC = adsShareName;
15855331Samw 
15867052Samw 	if (smb_ads_build_unc_name(unc_name, sizeof (unc_name),
15877961SNatalie.Li@Sun.COM 	    hostname, shareUNC) < 0)
15885331Samw 		return (-1);
15895331Samw 
15907052Samw 	ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name);
15915331Samw 
15925331Samw 	switch (ret) {
15935331Samw 	case 1:
15947052Samw 		(void) smb_ads_del_share(ah, adsShareName, adsContainer);
15957052Samw 		ret = smb_ads_add_share(ah, adsShareName, unc_name,
15967052Samw 		    adsContainer);
15975331Samw 		break;
15985331Samw 
15995331Samw 	case 0:
16007052Samw 		ret = smb_ads_add_share(ah, adsShareName, unc_name,
16017052Samw 		    adsContainer);
16027961SNatalie.Li@Sun.COM 		if (ret == LDAP_ALREADY_EXISTS)
16035331Samw 			ret = -1;
16047961SNatalie.Li@Sun.COM 
16055331Samw 		break;
16065331Samw 
16075331Samw 	case -1:
16085331Samw 	default:
16095331Samw 		/* return with error code */
16105331Samw 		ret = -1;
16115331Samw 	}
16125331Samw 
16135331Samw 	return (ret);
16145331Samw }
16155331Samw 
16165331Samw /*
16177052Samw  * smb_ads_remove_share
16185331Samw  * Remove share from ADS.  A search is done first before explicitly removing
16195331Samw  * the share.
16205331Samw  * Parameters:
16217052Samw  *   ah          : handle return from smb_ads_open
16225331Samw  *   adsShareName: name of share to be removed from ADS directory
16235331Samw  *   adsContainer: location for share to be removed from ADS directory, ie
16245331Samw  *                   ou=share_folder
16255331Samw  * Returns:
16265331Samw  *   -1          : error
16275331Samw  *    0          : success
16285331Samw  */
16295331Samw int
smb_ads_remove_share(smb_ads_handle_t * ah,const char * adsShareName,const char * shareUNC,const char * adsContainer,const char * hostname)16307052Samw smb_ads_remove_share(smb_ads_handle_t *ah, const char *adsShareName,
16317052Samw     const char *shareUNC, const char *adsContainer, const char *hostname)
16325331Samw {
16335331Samw 	int ret;
16347052Samw 	char unc_name[SMB_ADS_MAXBUFLEN];
16355331Samw 
16365331Samw 	if (adsShareName == NULL || adsContainer == NULL)
16375331Samw 		return (-1);
16385331Samw 	if (shareUNC == 0 || *shareUNC == 0)
16395331Samw 		shareUNC = adsShareName;
16405331Samw 
16417052Samw 	if (smb_ads_build_unc_name(unc_name, sizeof (unc_name),
16427961SNatalie.Li@Sun.COM 	    hostname, shareUNC) < 0)
16435331Samw 		return (-1);
16445331Samw 
16457052Samw 	ret = smb_ads_lookup_share(ah, adsShareName, adsContainer, unc_name);
16465331Samw 	if (ret == 0)
16475331Samw 		return (0);
16485331Samw 	if (ret == -1)
16495331Samw 		return (-1);
16505331Samw 
16517052Samw 	return (smb_ads_del_share(ah, adsShareName, adsContainer));
16525331Samw }
16535331Samw 
16545331Samw /*
16557961SNatalie.Li@Sun.COM  * smb_ads_get_default_comp_container_dn
16565331Samw  *
16577961SNatalie.Li@Sun.COM  * Build the distinguished name for the default computer conatiner (i.e. the
16587961SNatalie.Li@Sun.COM  * pre-defined Computers container).
16595331Samw  */
16605331Samw static void
smb_ads_get_default_comp_container_dn(smb_ads_handle_t * ah,char * buf,size_t buflen)16617961SNatalie.Li@Sun.COM smb_ads_get_default_comp_container_dn(smb_ads_handle_t *ah, char *buf,
16627961SNatalie.Li@Sun.COM     size_t buflen)
16635331Samw {
16647961SNatalie.Li@Sun.COM 	(void) snprintf(buf, buflen, "cn=%s,%s", SMB_ADS_COMPUTERS_CN,
16657961SNatalie.Li@Sun.COM 	    ah->domain_dn);
16667961SNatalie.Li@Sun.COM }
16677961SNatalie.Li@Sun.COM 
16687961SNatalie.Li@Sun.COM /*
16697961SNatalie.Li@Sun.COM  * smb_ads_get_default_comp_dn
16707961SNatalie.Li@Sun.COM  *
16717961SNatalie.Li@Sun.COM  * Build the distinguished name for this system.
16727961SNatalie.Li@Sun.COM  */
16737961SNatalie.Li@Sun.COM static void
smb_ads_get_default_comp_dn(smb_ads_handle_t * ah,char * buf,size_t buflen)16747961SNatalie.Li@Sun.COM smb_ads_get_default_comp_dn(smb_ads_handle_t *ah, char *buf, size_t buflen)
16757961SNatalie.Li@Sun.COM {
16767961SNatalie.Li@Sun.COM 	char nbname[NETBIOS_NAME_SZ];
16777961SNatalie.Li@Sun.COM 	char container_dn[SMB_ADS_DN_MAX];
16787961SNatalie.Li@Sun.COM 
16797961SNatalie.Li@Sun.COM 	(void) smb_getnetbiosname(nbname, sizeof (nbname));
16807961SNatalie.Li@Sun.COM 	smb_ads_get_default_comp_container_dn(ah, container_dn, SMB_ADS_DN_MAX);
16817961SNatalie.Li@Sun.COM 	(void) snprintf(buf, buflen, "cn=%s,%s", nbname, container_dn);
16825331Samw }
16835331Samw 
16845331Samw /*
16857052Samw  * smb_ads_add_computer
16867052Samw  *
16877052Samw  * Returns 0 upon success. Otherwise, returns -1.
16887052Samw  */
16897052Samw static int
smb_ads_add_computer(smb_ads_handle_t * ah,int dclevel,char * dn)16907348SJose.Borrego@Sun.COM smb_ads_add_computer(smb_ads_handle_t *ah, int dclevel, char *dn)
16917052Samw {
16927348SJose.Borrego@Sun.COM 	return (smb_ads_computer_op(ah, LDAP_MOD_ADD, dclevel, dn));
16937052Samw }
16947052Samw 
16957052Samw /*
16967052Samw  * smb_ads_modify_computer
16975331Samw  *
16985331Samw  * Returns 0 upon success. Otherwise, returns -1.
16995331Samw  */
17005331Samw static int
smb_ads_modify_computer(smb_ads_handle_t * ah,int dclevel,char * dn)17017348SJose.Borrego@Sun.COM smb_ads_modify_computer(smb_ads_handle_t *ah, int dclevel, char *dn)
17025331Samw {
17037348SJose.Borrego@Sun.COM 	return (smb_ads_computer_op(ah, LDAP_MOD_REPLACE, dclevel, dn));
17045521Sas200622 }
17055521Sas200622 
17065521Sas200622 /*
17077052Samw  * smb_ads_get_dc_level
17085521Sas200622  *
17097052Samw  * Returns the functional level of the DC upon success.
17107052Samw  * Otherwise, -1 is returned.
17115521Sas200622  */
17125521Sas200622 static int
smb_ads_get_dc_level(smb_ads_handle_t * ah)17137052Samw smb_ads_get_dc_level(smb_ads_handle_t *ah)
17145521Sas200622 {
17157052Samw 	LDAPMessage *res, *entry;
17167052Samw 	char *attr[2];
17177052Samw 	char **vals;
17187052Samw 	int rc = -1;
17197052Samw 
17207052Samw 	res = NULL;
17217052Samw 	attr[0] = SMB_ADS_ATTR_DCLEVEL;
17227052Samw 	attr[1] = NULL;
17237052Samw 	if (ldap_search_s(ah->ld, "", LDAP_SCOPE_BASE, NULL, attr,
17247052Samw 	    0, &res) != LDAP_SUCCESS) {
17257052Samw 		(void) ldap_msgfree(res);
17267052Samw 		return (-1);
17277052Samw 	}
17287052Samw 
17297052Samw 	/* no match for the specified attribute is found */
17307052Samw 	if (ldap_count_entries(ah->ld, res) == 0) {
17317052Samw 		(void) ldap_msgfree(res);
17327052Samw 		return (-1);
17337052Samw 	}
17347052Samw 
17357052Samw 	entry = ldap_first_entry(ah->ld, res);
17367052Samw 	if (entry) {
17377052Samw 		if ((vals = ldap_get_values(ah->ld, entry,
17387052Samw 		    SMB_ADS_ATTR_DCLEVEL)) == NULL) {
17397052Samw 			/*
17407052Samw 			 * Observed the values aren't populated
17417052Samw 			 * by the Windows 2000 server.
17427052Samw 			 */
17437052Samw 			(void) ldap_msgfree(res);
17447052Samw 			return (SMB_ADS_DCLEVEL_W2K);
17457052Samw 		}
17467052Samw 
17477052Samw 		if (vals[0] != NULL)
17487052Samw 			rc = atoi(vals[0]);
17497052Samw 
17507052Samw 		ldap_value_free(vals);
17517052Samw 	}
17527052Samw 
17537052Samw 	(void) ldap_msgfree(res);
17547052Samw 	return (rc);
17555521Sas200622 }
17565521Sas200622 
175711963SAfshin.Ardakani@Sun.COM /*
175811963SAfshin.Ardakani@Sun.COM  * The fully-qualified hostname returned by this function is often used for
175911963SAfshin.Ardakani@Sun.COM  * constructing service principal name.  Return the fully-qualified hostname
176011963SAfshin.Ardakani@Sun.COM  * in lower case for RFC 4120 section 6.2.1 conformance.
176111963SAfshin.Ardakani@Sun.COM  */
17625521Sas200622 static int
smb_ads_getfqhostname(smb_ads_handle_t * ah,char * fqhost,int len)17637961SNatalie.Li@Sun.COM smb_ads_getfqhostname(smb_ads_handle_t *ah, char *fqhost, int len)
17647961SNatalie.Li@Sun.COM {
176511963SAfshin.Ardakani@Sun.COM 	if (smb_gethostname(fqhost, len, SMB_CASE_LOWER) != 0)
17667961SNatalie.Li@Sun.COM 		return (-1);
17677961SNatalie.Li@Sun.COM 
17687961SNatalie.Li@Sun.COM 	(void) snprintf(fqhost, len, "%s.%s", fqhost,
17697961SNatalie.Li@Sun.COM 	    ah->domain);
17707961SNatalie.Li@Sun.COM 
17717961SNatalie.Li@Sun.COM 	return (0);
17727961SNatalie.Li@Sun.COM }
17737961SNatalie.Li@Sun.COM 
17747961SNatalie.Li@Sun.COM static int
smb_ads_computer_op(smb_ads_handle_t * ah,int op,int dclevel,char * dn)17757348SJose.Borrego@Sun.COM smb_ads_computer_op(smb_ads_handle_t *ah, int op, int dclevel, char *dn)
17765521Sas200622 {
17777052Samw 	LDAPMod *attrs[SMB_ADS_COMPUTER_NUM_ATTR];
1778*12508Samw@Sun.COM 	char *sam_val[2];
1779*12508Samw@Sun.COM 	char *ctl_val[2], *fqh_val[2];
17807052Samw 	char *encrypt_val[2];
17816432Sas200622 	int j = -1;
17825331Samw 	int ret, usrctl_flags = 0;
17837961SNatalie.Li@Sun.COM 	char sam_acct[SMB_SAMACCT_MAXLEN];
17845331Samw 	char fqhost[MAXHOSTNAMELEN];
17855331Samw 	char usrctl_buf[16];
17867052Samw 	char encrypt_buf[16];
17875521Sas200622 	int max;
1788*12508Samw@Sun.COM 	smb_krb5_pn_set_t spn, upn;
17895331Samw 
17907961SNatalie.Li@Sun.COM 	if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0)
17915331Samw 		return (-1);
17925331Samw 
17937961SNatalie.Li@Sun.COM 	if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN))
17947961SNatalie.Li@Sun.COM 		return (-1);
17955331Samw 
1796*12508Samw@Sun.COM 	/* The SPN attribute is multi-valued and must be 1 or greater */
1797*12508Samw@Sun.COM 	if (smb_krb5_get_pn_set(&spn, SMB_PN_SPN_ATTR, ah->domain) == 0)
17986432Sas200622 		return (-1);
17996432Sas200622 
1800*12508Samw@Sun.COM 	/* The UPN attribute is single-valued and cannot be zero */
1801*12508Samw@Sun.COM 	if (smb_krb5_get_pn_set(&upn, SMB_PN_UPN_ATTR, ah->domain) != 1) {
1802*12508Samw@Sun.COM 		smb_krb5_free_pn_set(&spn);
1803*12508Samw@Sun.COM 		smb_krb5_free_pn_set(&upn);
18045331Samw 		return (-1);
18055331Samw 	}
18065331Samw 
18077052Samw 	max = (SMB_ADS_COMPUTER_NUM_ATTR - ((op != LDAP_MOD_ADD) ? 1 : 0))
18089914Samw@Sun.COM 	    - (dclevel >= SMB_ADS_DCLEVEL_W2K8 ?  0 : 1);
18097052Samw 
18107052Samw 	if (smb_ads_alloc_attr(attrs, max) != 0) {
1811*12508Samw@Sun.COM 		smb_krb5_free_pn_set(&spn);
1812*12508Samw@Sun.COM 		smb_krb5_free_pn_set(&upn);
18136432Sas200622 		return (-1);
18145521Sas200622 	}
18155331Samw 
18165521Sas200622 	/* objectClass attribute is not modifiable. */
18175521Sas200622 	if (op == LDAP_MOD_ADD) {
18185521Sas200622 		attrs[++j]->mod_op = op;
18195521Sas200622 		attrs[j]->mod_type = "objectClass";
18208670SJose.Borrego@Sun.COM 		attrs[j]->mod_values = smb_ads_computer_objcls;
18215521Sas200622 	}
18225331Samw 
18235521Sas200622 	attrs[++j]->mod_op = op;
18247052Samw 	attrs[j]->mod_type = SMB_ADS_ATTR_SAMACCT;
18255331Samw 	sam_val[0] = sam_acct;
18265331Samw 	sam_val[1] = 0;
18275521Sas200622 	attrs[j]->mod_values = sam_val;
18285331Samw 
18295521Sas200622 	attrs[++j]->mod_op = op;
18307052Samw 	attrs[j]->mod_type = SMB_ADS_ATTR_UPN;
1831*12508Samw@Sun.COM 	attrs[j]->mod_values = upn.s_pns;
18325331Samw 
18335521Sas200622 	attrs[++j]->mod_op = op;
18347052Samw 	attrs[j]->mod_type = SMB_ADS_ATTR_SPN;
1835*12508Samw@Sun.COM 	attrs[j]->mod_values =  spn.s_pns;
18365331Samw 
18375521Sas200622 	attrs[++j]->mod_op = op;
18387052Samw 	attrs[j]->mod_type = SMB_ADS_ATTR_CTL;
18397052Samw 	usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
18407052Samw 	    SMB_ADS_USER_ACCT_CTL_PASSWD_NOTREQD |
18417052Samw 	    SMB_ADS_USER_ACCT_CTL_ACCOUNTDISABLE);
18425331Samw 	(void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", usrctl_flags);
18435331Samw 	ctl_val[0] = usrctl_buf;
18445331Samw 	ctl_val[1] = 0;
18455521Sas200622 	attrs[j]->mod_values = ctl_val;
18465331Samw 
18475521Sas200622 	attrs[++j]->mod_op = op;
18487052Samw 	attrs[j]->mod_type = SMB_ADS_ATTR_DNSHOST;
18495331Samw 	fqh_val[0] = fqhost;
18505331Samw 	fqh_val[1] = 0;
18515521Sas200622 	attrs[j]->mod_values = fqh_val;
18525521Sas200622 
18537052Samw 	/* enctypes support starting in Windows Server 2008 */
18547052Samw 	if (dclevel > SMB_ADS_DCLEVEL_W2K3) {
18557052Samw 		attrs[++j]->mod_op = op;
18567052Samw 		attrs[j]->mod_type = SMB_ADS_ATTR_ENCTYPES;
18577052Samw 		(void) snprintf(encrypt_buf, sizeof (encrypt_buf), "%d",
18587052Samw 		    SMB_ADS_ENC_AES256 + SMB_ADS_ENC_AES128 + SMB_ADS_ENC_RC4 +
18597052Samw 		    SMB_ADS_ENC_DES_MD5 + SMB_ADS_ENC_DES_CRC);
18607052Samw 		encrypt_val[0] = encrypt_buf;
18617052Samw 		encrypt_val[1] = 0;
18627052Samw 		attrs[j]->mod_values = encrypt_val;
18637052Samw 	}
18647052Samw 
18655521Sas200622 	switch (op) {
18665521Sas200622 	case LDAP_MOD_ADD:
18675521Sas200622 		if ((ret = ldap_add_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) {
18689021Samw@Sun.COM 			syslog(LOG_NOTICE, "ldap_add: %s",
18695521Sas200622 			    ldap_err2string(ret));
18705521Sas200622 			ret = -1;
18715521Sas200622 		}
18725521Sas200622 		break;
18735521Sas200622 
18745521Sas200622 	case LDAP_MOD_REPLACE:
18755521Sas200622 		if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) {
18769021Samw@Sun.COM 			syslog(LOG_NOTICE, "ldap_modify: %s",
18775521Sas200622 			    ldap_err2string(ret));
18785521Sas200622 			ret = -1;
18795521Sas200622 		}
18805521Sas200622 		break;
18815521Sas200622 
18825521Sas200622 	default:
18835331Samw 		ret = -1;
18845521Sas200622 
18855331Samw 	}
18865331Samw 
18877052Samw 	smb_ads_free_attr(attrs);
1888*12508Samw@Sun.COM 	smb_krb5_free_pn_set(&spn);
1889*12508Samw@Sun.COM 	smb_krb5_free_pn_set(&upn);
18905331Samw 
18915331Samw 	return (ret);
18925331Samw }
18935331Samw 
18945331Samw /*
18955331Samw  * Delete an ADS computer account.
18965331Samw  */
18975331Samw static void
smb_ads_del_computer(smb_ads_handle_t * ah,char * dn)18987348SJose.Borrego@Sun.COM smb_ads_del_computer(smb_ads_handle_t *ah, char *dn)
18995331Samw {
19005331Samw 	int rc;
19015331Samw 
19027052Samw 	if ((rc = ldap_delete_s(ah->ld, dn)) != LDAP_SUCCESS)
19039021Samw@Sun.COM 		smb_tracef("ldap_delete: %s", ldap_err2string(rc));
19047961SNatalie.Li@Sun.COM }
19057961SNatalie.Li@Sun.COM 
19067961SNatalie.Li@Sun.COM /*
19077961SNatalie.Li@Sun.COM  * Gets the value of the given attribute.
19087961SNatalie.Li@Sun.COM  */
19097961SNatalie.Li@Sun.COM static smb_ads_qstat_t
smb_ads_getattr(LDAP * ld,LDAPMessage * entry,smb_ads_avpair_t * avpair)19107961SNatalie.Li@Sun.COM smb_ads_getattr(LDAP *ld, LDAPMessage *entry, smb_ads_avpair_t *avpair)
19117961SNatalie.Li@Sun.COM {
19127961SNatalie.Li@Sun.COM 	char **vals;
19137961SNatalie.Li@Sun.COM 	smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND;
19147961SNatalie.Li@Sun.COM 
19157961SNatalie.Li@Sun.COM 	assert(avpair);
19167961SNatalie.Li@Sun.COM 	avpair->avp_val = NULL;
19177961SNatalie.Li@Sun.COM 	vals = ldap_get_values(ld, entry, avpair->avp_attr);
19187961SNatalie.Li@Sun.COM 	if (!vals)
19197961SNatalie.Li@Sun.COM 		return (SMB_ADS_STAT_NOT_FOUND);
19207961SNatalie.Li@Sun.COM 
19217961SNatalie.Li@Sun.COM 	if (!vals[0]) {
19227961SNatalie.Li@Sun.COM 		ldap_value_free(vals);
19237961SNatalie.Li@Sun.COM 		return (SMB_ADS_STAT_NOT_FOUND);
19247961SNatalie.Li@Sun.COM 	}
19257961SNatalie.Li@Sun.COM 
19267961SNatalie.Li@Sun.COM 	avpair->avp_val = strdup(vals[0]);
19277961SNatalie.Li@Sun.COM 	if (!avpair->avp_val)
19287961SNatalie.Li@Sun.COM 		rc = SMB_ADS_STAT_ERR;
19297961SNatalie.Li@Sun.COM 
19307961SNatalie.Li@Sun.COM 	ldap_value_free(vals);
19317961SNatalie.Li@Sun.COM 	return (rc);
19327961SNatalie.Li@Sun.COM }
19337961SNatalie.Li@Sun.COM 
19347961SNatalie.Li@Sun.COM /*
19357961SNatalie.Li@Sun.COM  * Process query's result.
19367961SNatalie.Li@Sun.COM  */
19377961SNatalie.Li@Sun.COM static smb_ads_qstat_t
smb_ads_get_qstat(smb_ads_handle_t * ah,LDAPMessage * res,smb_ads_avpair_t * avpair)19387961SNatalie.Li@Sun.COM smb_ads_get_qstat(smb_ads_handle_t *ah, LDAPMessage *res,
19397961SNatalie.Li@Sun.COM     smb_ads_avpair_t *avpair)
19407961SNatalie.Li@Sun.COM {
19417961SNatalie.Li@Sun.COM 	char fqhost[MAXHOSTNAMELEN];
19427961SNatalie.Li@Sun.COM 	smb_ads_avpair_t dnshost_avp;
19437961SNatalie.Li@Sun.COM 	smb_ads_qstat_t rc = SMB_ADS_STAT_FOUND;
19447961SNatalie.Li@Sun.COM 	LDAPMessage *entry;
19457961SNatalie.Li@Sun.COM 
19467961SNatalie.Li@Sun.COM 	if (smb_ads_getfqhostname(ah, fqhost, MAXHOSTNAMELEN))
19477961SNatalie.Li@Sun.COM 		return (SMB_ADS_STAT_ERR);
19487961SNatalie.Li@Sun.COM 
19497961SNatalie.Li@Sun.COM 	if (ldap_count_entries(ah->ld, res) == 0)
19507961SNatalie.Li@Sun.COM 		return (SMB_ADS_STAT_NOT_FOUND);
19517961SNatalie.Li@Sun.COM 
19527961SNatalie.Li@Sun.COM 	if ((entry = ldap_first_entry(ah->ld, res)) == NULL)
19537961SNatalie.Li@Sun.COM 		return (SMB_ADS_STAT_ERR);
19547961SNatalie.Li@Sun.COM 
19557961SNatalie.Li@Sun.COM 	dnshost_avp.avp_attr = SMB_ADS_ATTR_DNSHOST;
19567961SNatalie.Li@Sun.COM 	rc = smb_ads_getattr(ah->ld, entry, &dnshost_avp);
19577961SNatalie.Li@Sun.COM 
19587961SNatalie.Li@Sun.COM 	switch (rc) {
19597961SNatalie.Li@Sun.COM 	case SMB_ADS_STAT_FOUND:
19607961SNatalie.Li@Sun.COM 		/*
19617961SNatalie.Li@Sun.COM 		 * Returns SMB_ADS_STAT_DUP to avoid overwriting
19627961SNatalie.Li@Sun.COM 		 * the computer account of another system whose
19637961SNatalie.Li@Sun.COM 		 * NetBIOS name collides with that of the current
19647961SNatalie.Li@Sun.COM 		 * system.
19657961SNatalie.Li@Sun.COM 		 */
19667961SNatalie.Li@Sun.COM 		if (strcasecmp(dnshost_avp.avp_val, fqhost))
19677961SNatalie.Li@Sun.COM 			rc = SMB_ADS_STAT_DUP;
19687961SNatalie.Li@Sun.COM 
19697961SNatalie.Li@Sun.COM 		free(dnshost_avp.avp_val);
19707961SNatalie.Li@Sun.COM 		break;
19717961SNatalie.Li@Sun.COM 
19727961SNatalie.Li@Sun.COM 	case SMB_ADS_STAT_NOT_FOUND:
19737961SNatalie.Li@Sun.COM 		/*
19747961SNatalie.Li@Sun.COM 		 * Pre-created computer account doesn't have
19757961SNatalie.Li@Sun.COM 		 * the dNSHostname attribute. It's been observed
19767961SNatalie.Li@Sun.COM 		 * that the dNSHostname attribute is only set after
19777961SNatalie.Li@Sun.COM 		 * a successful domain join.
19787961SNatalie.Li@Sun.COM 		 * Returns SMB_ADS_STAT_FOUND as the account is
19797961SNatalie.Li@Sun.COM 		 * pre-created for the current system.
19807961SNatalie.Li@Sun.COM 		 */
19817961SNatalie.Li@Sun.COM 		rc = SMB_ADS_STAT_FOUND;
19827961SNatalie.Li@Sun.COM 		break;
19837961SNatalie.Li@Sun.COM 
19847961SNatalie.Li@Sun.COM 	default:
19857961SNatalie.Li@Sun.COM 		break;
19867961SNatalie.Li@Sun.COM 	}
19877961SNatalie.Li@Sun.COM 
19887961SNatalie.Li@Sun.COM 	if (rc != SMB_ADS_STAT_FOUND)
19897961SNatalie.Li@Sun.COM 		return (rc);
19907961SNatalie.Li@Sun.COM 
19917961SNatalie.Li@Sun.COM 	if (avpair)
19927961SNatalie.Li@Sun.COM 		rc = smb_ads_getattr(ah->ld, entry, avpair);
19937961SNatalie.Li@Sun.COM 
19947961SNatalie.Li@Sun.COM 	return (rc);
19957961SNatalie.Li@Sun.COM 
19965331Samw }
19975331Samw 
19985331Samw /*
19997052Samw  * smb_ads_lookup_computer_n_attr
20005331Samw  *
20017961SNatalie.Li@Sun.COM  * If avpair is NULL, checks the status of the specified computer account.
20027961SNatalie.Li@Sun.COM  * Otherwise, looks up the value of the specified computer account's attribute.
20037961SNatalie.Li@Sun.COM  * If found, the value field of the avpair will be allocated and set. The
20047961SNatalie.Li@Sun.COM  * caller should free the allocated buffer.
20055331Samw  *
20065331Samw  * Return:
20077961SNatalie.Li@Sun.COM  *  SMB_ADS_STAT_FOUND  - if both the computer and the specified attribute is
20087961SNatalie.Li@Sun.COM  *                        found.
20097961SNatalie.Li@Sun.COM  *  SMB_ADS_STAT_NOT_FOUND - if either the computer or the specified attribute
20107961SNatalie.Li@Sun.COM  *                           is not found.
20117961SNatalie.Li@Sun.COM  *  SMB_ADS_STAT_DUP - if the computer account is already used by other systems
20127961SNatalie.Li@Sun.COM  *                     in the AD. This could happen if the hostname of multiple
20137961SNatalie.Li@Sun.COM  *                     systems resolved to the same NetBIOS name.
20147961SNatalie.Li@Sun.COM  *  SMB_ADS_STAT_ERR - any failure.
20155331Samw  */
20167961SNatalie.Li@Sun.COM static smb_ads_qstat_t
smb_ads_lookup_computer_n_attr(smb_ads_handle_t * ah,smb_ads_avpair_t * avpair,int scope,char * dn)20177961SNatalie.Li@Sun.COM smb_ads_lookup_computer_n_attr(smb_ads_handle_t *ah, smb_ads_avpair_t *avpair,
20187348SJose.Borrego@Sun.COM     int scope, char *dn)
20195331Samw {
20207961SNatalie.Li@Sun.COM 	char *attrs[3], filter[SMB_ADS_MAXBUFLEN];
20217961SNatalie.Li@Sun.COM 	LDAPMessage *res;
20227961SNatalie.Li@Sun.COM 	char sam_acct[SMB_SAMACCT_MAXLEN], sam_acct2[SMB_SAMACCT_MAXLEN];
20237961SNatalie.Li@Sun.COM 	smb_ads_qstat_t rc;
20247961SNatalie.Li@Sun.COM 
20257961SNatalie.Li@Sun.COM 	if (smb_getsamaccount(sam_acct, sizeof (sam_acct)) != 0)
20267961SNatalie.Li@Sun.COM 		return (SMB_ADS_STAT_ERR);
20275331Samw 
20285331Samw 	res = NULL;
20297961SNatalie.Li@Sun.COM 	attrs[0] = SMB_ADS_ATTR_DNSHOST;
20305331Samw 	attrs[1] = NULL;
20317961SNatalie.Li@Sun.COM 	attrs[2] = NULL;
20327961SNatalie.Li@Sun.COM 
20337961SNatalie.Li@Sun.COM 	if (avpair) {
20347961SNatalie.Li@Sun.COM 		if (!avpair->avp_attr)
20357961SNatalie.Li@Sun.COM 			return (SMB_ADS_STAT_ERR);
20367961SNatalie.Li@Sun.COM 
20377961SNatalie.Li@Sun.COM 		attrs[1] = avpair->avp_attr;
20387961SNatalie.Li@Sun.COM 	}
20397961SNatalie.Li@Sun.COM 
20407961SNatalie.Li@Sun.COM 	if (smb_ads_escape_search_filter_chars(sam_acct, sam_acct2) != 0)
20417961SNatalie.Li@Sun.COM 		return (SMB_ADS_STAT_ERR);
20425331Samw 
20437348SJose.Borrego@Sun.COM 	(void) snprintf(filter, sizeof (filter),
20447961SNatalie.Li@Sun.COM 	    "(&(objectClass=computer)(%s=%s))", SMB_ADS_ATTR_SAMACCT,
20457961SNatalie.Li@Sun.COM 	    sam_acct2);
20465331Samw 
20477348SJose.Borrego@Sun.COM 	if (ldap_search_s(ah->ld, dn, scope, filter, attrs, 0,
20485331Samw 	    &res) != LDAP_SUCCESS) {
20495331Samw 		(void) ldap_msgfree(res);
20507961SNatalie.Li@Sun.COM 		return (SMB_ADS_STAT_NOT_FOUND);
20515331Samw 	}
20525331Samw 
20537961SNatalie.Li@Sun.COM 	rc = smb_ads_get_qstat(ah, res, avpair);
20545331Samw 	/* free the search results */
20555331Samw 	(void) ldap_msgfree(res);
20567961SNatalie.Li@Sun.COM 	return (rc);
20575331Samw }
20585331Samw 
20595331Samw /*
20607052Samw  * smb_ads_find_computer
20615331Samw  *
20627348SJose.Borrego@Sun.COM  * Starts by searching for the system's AD computer object in the default
20637348SJose.Borrego@Sun.COM  * container (i.e. cn=Computers).  If not found, searches the entire directory.
20647961SNatalie.Li@Sun.COM  * If found, 'dn' will be set to the distinguished name of the system's AD
20657961SNatalie.Li@Sun.COM  * computer object.
20665331Samw  */
20677961SNatalie.Li@Sun.COM static smb_ads_qstat_t
smb_ads_find_computer(smb_ads_handle_t * ah,char * dn)20687348SJose.Borrego@Sun.COM smb_ads_find_computer(smb_ads_handle_t *ah, char *dn)
20695331Samw {
20707961SNatalie.Li@Sun.COM 	smb_ads_qstat_t stat;
20717961SNatalie.Li@Sun.COM 	smb_ads_avpair_t avpair;
20727961SNatalie.Li@Sun.COM 
20737961SNatalie.Li@Sun.COM 	avpair.avp_attr = SMB_ADS_ATTR_DN;
20747961SNatalie.Li@Sun.COM 	smb_ads_get_default_comp_container_dn(ah, dn, SMB_ADS_DN_MAX);
20757961SNatalie.Li@Sun.COM 	stat = smb_ads_lookup_computer_n_attr(ah, &avpair, LDAP_SCOPE_ONELEVEL,
20767961SNatalie.Li@Sun.COM 	    dn);
20777961SNatalie.Li@Sun.COM 
20787961SNatalie.Li@Sun.COM 	if (stat == SMB_ADS_STAT_NOT_FOUND) {
20797348SJose.Borrego@Sun.COM 		(void) strlcpy(dn, ah->domain_dn, SMB_ADS_DN_MAX);
20807961SNatalie.Li@Sun.COM 		stat = smb_ads_lookup_computer_n_attr(ah, &avpair,
20817961SNatalie.Li@Sun.COM 		    LDAP_SCOPE_SUBTREE, dn);
20827348SJose.Borrego@Sun.COM 	}
20837348SJose.Borrego@Sun.COM 
20847961SNatalie.Li@Sun.COM 	if (stat == SMB_ADS_STAT_FOUND) {
20857961SNatalie.Li@Sun.COM 		(void) strlcpy(dn, avpair.avp_val, SMB_ADS_DN_MAX);
20867961SNatalie.Li@Sun.COM 		free(avpair.avp_val);
20877961SNatalie.Li@Sun.COM 	}
20887961SNatalie.Li@Sun.COM 
20897961SNatalie.Li@Sun.COM 	return (stat);
20905331Samw }
20915331Samw 
20925331Samw /*
20937052Samw  * smb_ads_update_computer_cntrl_attr
20945331Samw  *
20955331Samw  * Modify the user account control attribute of an existing computer
20965331Samw  * object on AD.
20975331Samw  *
20988670SJose.Borrego@Sun.COM  * Returns LDAP error code.
20995331Samw  */
21005331Samw static int
smb_ads_update_computer_cntrl_attr(smb_ads_handle_t * ah,int flags,char * dn)21018670SJose.Borrego@Sun.COM smb_ads_update_computer_cntrl_attr(smb_ads_handle_t *ah, int flags, char *dn)
21025331Samw {
21036432Sas200622 	LDAPMod *attrs[2];
21045331Samw 	char *ctl_val[2];
21058670SJose.Borrego@Sun.COM 	int ret = 0;
21065331Samw 	char usrctl_buf[16];
21075331Samw 
21087052Samw 	if (smb_ads_alloc_attr(attrs, sizeof (attrs) / sizeof (LDAPMod *)) != 0)
21098670SJose.Borrego@Sun.COM 		return (LDAP_NO_MEMORY);
21106432Sas200622 
21116432Sas200622 	attrs[0]->mod_op = LDAP_MOD_REPLACE;
21127052Samw 	attrs[0]->mod_type = SMB_ADS_ATTR_CTL;
21135331Samw 
21148670SJose.Borrego@Sun.COM 	(void) snprintf(usrctl_buf, sizeof (usrctl_buf), "%d", flags);
21155331Samw 	ctl_val[0] = usrctl_buf;
21165331Samw 	ctl_val[1] = 0;
21176432Sas200622 	attrs[0]->mod_values = ctl_val;
21185331Samw 	if ((ret = ldap_modify_s(ah->ld, dn, attrs)) != LDAP_SUCCESS) {
21199021Samw@Sun.COM 		syslog(LOG_NOTICE, "ldap_modify: %s", ldap_err2string(ret));
21205331Samw 	}
21215331Samw 
21227052Samw 	smb_ads_free_attr(attrs);
21235331Samw 	return (ret);
21245331Samw }
21255331Samw 
21265331Samw /*
21277052Samw  * smb_ads_lookup_computer_attr_kvno
21285331Samw  *
21295331Samw  * Lookup the value of the Kerberos version number attribute of the computer
21305331Samw  * account.
21315331Samw  */
21325331Samw static krb5_kvno
smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t * ah,char * dn)21337348SJose.Borrego@Sun.COM smb_ads_lookup_computer_attr_kvno(smb_ads_handle_t *ah, char *dn)
21345331Samw {
21357961SNatalie.Li@Sun.COM 	smb_ads_avpair_t avpair;
21365331Samw 	int kvno = 1;
21375331Samw 
21387961SNatalie.Li@Sun.COM 	avpair.avp_attr = SMB_ADS_ATTR_KVNO;
21397961SNatalie.Li@Sun.COM 	if (smb_ads_lookup_computer_n_attr(ah, &avpair,
21407961SNatalie.Li@Sun.COM 	    LDAP_SCOPE_BASE, dn) == SMB_ADS_STAT_FOUND) {
21417961SNatalie.Li@Sun.COM 		kvno = atoi(avpair.avp_val);
21427961SNatalie.Li@Sun.COM 		free(avpair.avp_val);
21435331Samw 	}
21445331Samw 
21455331Samw 	return (kvno);
21465331Samw }
21475331Samw 
214811571SShawn.Emery@Sun.COM static int
smb_ads_gen_machine_passwd(char * machine_passwd,size_t bufsz)214911571SShawn.Emery@Sun.COM smb_ads_gen_machine_passwd(char *machine_passwd, size_t bufsz)
21505331Samw {
215111571SShawn.Emery@Sun.COM 	int i;
215211571SShawn.Emery@Sun.COM 	size_t pwdlen;
215311571SShawn.Emery@Sun.COM 	uint8_t *random_bytes;
21545331Samw 
215511571SShawn.Emery@Sun.COM 	errno = 0;
215611571SShawn.Emery@Sun.COM 	if (machine_passwd == NULL || bufsz == 0) {
215711571SShawn.Emery@Sun.COM 		errno = EINVAL;
215811571SShawn.Emery@Sun.COM 		return (-1);
21595331Samw 	}
21605331Samw 
216111571SShawn.Emery@Sun.COM 	pwdlen = bufsz - 1;
216211571SShawn.Emery@Sun.COM 	random_bytes = calloc(1, pwdlen);
216311571SShawn.Emery@Sun.COM 	if (random_bytes == NULL)
216411571SShawn.Emery@Sun.COM 		return (-1);
216511571SShawn.Emery@Sun.COM 
216611571SShawn.Emery@Sun.COM 	if (pkcs11_get_random(random_bytes, pwdlen) != 0) {
216711571SShawn.Emery@Sun.COM 		free(random_bytes);
216811571SShawn.Emery@Sun.COM 		return (-1);
216911571SShawn.Emery@Sun.COM 	}
217011571SShawn.Emery@Sun.COM 
217111571SShawn.Emery@Sun.COM 	for (i = 0; i < pwdlen; i++)
217211571SShawn.Emery@Sun.COM 		machine_passwd[i] = (random_bytes[i] % SMB_ADS_PWD_CHAR_NUM) +
217311571SShawn.Emery@Sun.COM 		    SMB_ADS_PWD_CHAR_START;
217411571SShawn.Emery@Sun.COM 
217511571SShawn.Emery@Sun.COM 	machine_passwd[pwdlen] = 0;
217611571SShawn.Emery@Sun.COM 	bzero(random_bytes, pwdlen);
217711571SShawn.Emery@Sun.COM 	free(random_bytes);
217811571SShawn.Emery@Sun.COM 	return (0);
21795331Samw }
21805331Samw 
21815331Samw /*
21827052Samw  * smb_ads_join
21835331Samw  *
21845331Samw  * Besides the NT-4 style domain join (using MS-RPC), CIFS server also
21855331Samw  * provides the domain join using Kerberos Authentication, Keberos
21865521Sas200622  * Change & Set password, and LDAP protocols. Basically, AD join
21875331Samw  * operation would require the following tickets to be acquired for the
21885331Samw  * the user account that is provided for the domain join.
21895331Samw  *
21905331Samw  * 1) a Keberos TGT ticket,
21915331Samw  * 2) a ldap service ticket, and
21925331Samw  * 3) kadmin/changpw service ticket
21935331Samw  *
21945331Samw  * The ADS client first sends a ldap search request to find out whether
21955331Samw  * or not the workstation trust account already exists in the Active Directory.
21965331Samw  * The existing computer object for this workstation will be removed and
21975331Samw  * a new one will be added. The machine account password is randomly
21987052Samw  * generated and set for the newly created computer object using KPASSWD
21995331Samw  * protocol (See RFC 3244). Once the password is set, our ADS client
22005331Samw  * finalizes the machine account by modifying the user acount control
22015331Samw  * attribute of the computer object. Kerberos keys derived from the machine
22025331Samw  * account password will be stored locally in /etc/krb5/krb5.keytab file.
22035331Samw  * That would be needed while acquiring Kerberos TGT ticket for the host
22045331Samw  * principal after the domain join operation.
22055331Samw  */
22067052Samw smb_adjoin_status_t
smb_ads_join(char * domain,char * user,char * usr_passwd,char * machine_passwd,size_t len)22077052Samw smb_ads_join(char *domain, char *user, char *usr_passwd, char *machine_passwd,
220811571SShawn.Emery@Sun.COM     size_t len)
22095331Samw {
22107052Samw 	smb_ads_handle_t *ah = NULL;
22115331Samw 	krb5_context ctx = NULL;
2212*12508Samw@Sun.COM 	krb5_principal *krb5princs = NULL;
22135331Samw 	krb5_kvno kvno;
22145967Scp160787 	boolean_t des_only, delete = B_TRUE;
22157052Samw 	smb_adjoin_status_t rc = SMB_ADJOIN_SUCCESS;
22165521Sas200622 	boolean_t new_acct;
22178670SJose.Borrego@Sun.COM 	int dclevel, num, usrctl_flags = 0;
22187961SNatalie.Li@Sun.COM 	smb_ads_qstat_t qstat;
22197348SJose.Borrego@Sun.COM 	char dn[SMB_ADS_DN_MAX];
22208334SJose.Borrego@Sun.COM 	char *tmpfile;
2221*12508Samw@Sun.COM 	int cnt;
2222*12508Samw@Sun.COM 	smb_krb5_pn_set_t spns;
22237052Samw 
22247052Samw 	krb5_enctype *encptr;
22257052Samw 
22267052Samw 	if ((ah = smb_ads_open_main(domain, user, usr_passwd)) == NULL) {
22276139Sjb150015 		smb_ccache_remove(SMB_CCACHE_PATH);
22287052Samw 		return (SMB_ADJOIN_ERR_GET_HANDLE);
22295521Sas200622 	}
22305521Sas200622 
223111571SShawn.Emery@Sun.COM 	if (smb_ads_gen_machine_passwd(machine_passwd, len) != 0) {
223211571SShawn.Emery@Sun.COM 		syslog(LOG_NOTICE, "machine password generation: %m");
223311571SShawn.Emery@Sun.COM 		smb_ads_close(ah);
223411571SShawn.Emery@Sun.COM 		smb_ccache_remove(SMB_CCACHE_PATH);
223511571SShawn.Emery@Sun.COM 		return (SMB_ADJOIN_ERR_GEN_PWD);
223611571SShawn.Emery@Sun.COM 	}
22375331Samw 
22387052Samw 	if ((dclevel = smb_ads_get_dc_level(ah)) == -1) {
22397052Samw 		smb_ads_close(ah);
22407052Samw 		smb_ccache_remove(SMB_CCACHE_PATH);
22417052Samw 		return (SMB_ADJOIN_ERR_GET_DCLEVEL);
22427052Samw 	}
22437052Samw 
22447961SNatalie.Li@Sun.COM 	qstat = smb_ads_find_computer(ah, dn);
22457961SNatalie.Li@Sun.COM 	switch (qstat) {
22467961SNatalie.Li@Sun.COM 	case SMB_ADS_STAT_FOUND:
22475521Sas200622 		new_acct = B_FALSE;
22487348SJose.Borrego@Sun.COM 		if (smb_ads_modify_computer(ah, dclevel, dn) != 0) {
22497052Samw 			smb_ads_close(ah);
22506139Sjb150015 			smb_ccache_remove(SMB_CCACHE_PATH);
22517052Samw 			return (SMB_ADJOIN_ERR_MOD_TRUST_ACCT);
22525521Sas200622 		}
22537961SNatalie.Li@Sun.COM 		break;
22547961SNatalie.Li@Sun.COM 
22557961SNatalie.Li@Sun.COM 	case SMB_ADS_STAT_NOT_FOUND:
22565521Sas200622 		new_acct = B_TRUE;
22577961SNatalie.Li@Sun.COM 		smb_ads_get_default_comp_dn(ah, dn, SMB_ADS_DN_MAX);
22587348SJose.Borrego@Sun.COM 		if (smb_ads_add_computer(ah, dclevel, dn) != 0) {
22597052Samw 			smb_ads_close(ah);
22606139Sjb150015 			smb_ccache_remove(SMB_CCACHE_PATH);
22617052Samw 			return (SMB_ADJOIN_ERR_ADD_TRUST_ACCT);
22625521Sas200622 		}
22637961SNatalie.Li@Sun.COM 		break;
22647961SNatalie.Li@Sun.COM 
22657961SNatalie.Li@Sun.COM 	default:
22667961SNatalie.Li@Sun.COM 		if (qstat == SMB_ADS_STAT_DUP)
22677961SNatalie.Li@Sun.COM 			rc = SMB_ADJOIN_ERR_DUP_TRUST_ACCT;
22687961SNatalie.Li@Sun.COM 		else
22697961SNatalie.Li@Sun.COM 			rc = SMB_ADJOIN_ERR_TRUST_ACCT;
22707961SNatalie.Li@Sun.COM 		smb_ads_close(ah);
22717961SNatalie.Li@Sun.COM 		smb_ccache_remove(SMB_CCACHE_PATH);
22727961SNatalie.Li@Sun.COM 		return (rc);
22735521Sas200622 	}
22745521Sas200622 
22755521Sas200622 	des_only = B_FALSE;
22765331Samw 
22775331Samw 	if (smb_krb5_ctx_init(&ctx) != 0) {
22787052Samw 		rc = SMB_ADJOIN_ERR_INIT_KRB_CTX;
22795331Samw 		goto adjoin_cleanup;
22805331Samw 	}
22815331Samw 
2282*12508Samw@Sun.COM 	if (smb_krb5_get_pn_set(&spns, SMB_PN_KEYTAB_ENTRY, ah->domain) == 0) {
22837052Samw 		rc = SMB_ADJOIN_ERR_GET_SPNS;
22845331Samw 		goto adjoin_cleanup;
22855331Samw 	}
22865331Samw 
2287*12508Samw@Sun.COM 	if (smb_krb5_get_kprincs(ctx, spns.s_pns, spns.s_cnt, &krb5princs)
2288*12508Samw@Sun.COM 	    != 0) {
2289*12508Samw@Sun.COM 		smb_krb5_free_pn_set(&spns);
2290*12508Samw@Sun.COM 		rc = SMB_ADJOIN_ERR_GET_SPNS;
2291*12508Samw@Sun.COM 		goto adjoin_cleanup;
2292*12508Samw@Sun.COM 	}
2293*12508Samw@Sun.COM 
2294*12508Samw@Sun.COM 	cnt = spns.s_cnt;
2295*12508Samw@Sun.COM 	smb_krb5_free_pn_set(&spns);
2296*12508Samw@Sun.COM 
2297*12508Samw@Sun.COM 	if (smb_krb5_setpwd(ctx, ah->domain, machine_passwd) != 0) {
22987052Samw 		rc = SMB_ADJOIN_ERR_KSETPWD;
22997052Samw 		goto adjoin_cleanup;
23007052Samw 	}
23017052Samw 
23027348SJose.Borrego@Sun.COM 	kvno = smb_ads_lookup_computer_attr_kvno(ah, dn);
23037052Samw 
23048670SJose.Borrego@Sun.COM 	/*
23058670SJose.Borrego@Sun.COM 	 * Only members of Domain Admins and Enterprise Admins can set
23068670SJose.Borrego@Sun.COM 	 * the TRUSTED_FOR_DELEGATION userAccountControl flag.
23078670SJose.Borrego@Sun.COM 	 */
23088670SJose.Borrego@Sun.COM 	if (smb_ads_update_computer_cntrl_attr(ah,
230910001SJoyce.McIntosh@Sun.COM 	    SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
23108670SJose.Borrego@Sun.COM 	    SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION, dn)
23118670SJose.Borrego@Sun.COM 	    == LDAP_INSUFFICIENT_ACCESS) {
23128670SJose.Borrego@Sun.COM 		usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
23138670SJose.Borrego@Sun.COM 		    SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD);
23148670SJose.Borrego@Sun.COM 
23159021Samw@Sun.COM 		syslog(LOG_NOTICE, "Unable to set the "
23168670SJose.Borrego@Sun.COM 		    "TRUSTED_FOR_DELEGATION userAccountControl flag on "
23178670SJose.Borrego@Sun.COM 		    "the machine account in Active Directory.  Please refer "
23188670SJose.Borrego@Sun.COM 		    "to the Troubleshooting guide for more information.");
23198670SJose.Borrego@Sun.COM 
23208670SJose.Borrego@Sun.COM 	} else {
23218670SJose.Borrego@Sun.COM 		usrctl_flags |= (SMB_ADS_USER_ACCT_CTL_WKSTATION_TRUST_ACCT |
23228670SJose.Borrego@Sun.COM 		    SMB_ADS_USER_ACCT_CTL_TRUSTED_FOR_DELEGATION |
23238670SJose.Borrego@Sun.COM 		    SMB_ADS_USER_ACCT_CTL_DONT_EXPIRE_PASSWD);
23248670SJose.Borrego@Sun.COM 	}
23258670SJose.Borrego@Sun.COM 
23268670SJose.Borrego@Sun.COM 	if (des_only)
23278670SJose.Borrego@Sun.COM 		usrctl_flags |= SMB_ADS_USER_ACCT_CTL_USE_DES_KEY_ONLY;
23288670SJose.Borrego@Sun.COM 
23298670SJose.Borrego@Sun.COM 	if (smb_ads_update_computer_cntrl_attr(ah, usrctl_flags, dn)
23307348SJose.Borrego@Sun.COM 	    != 0) {
23317052Samw 		rc = SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR;
23325331Samw 		goto adjoin_cleanup;
23335331Samw 	}
23345331Samw 
23358334SJose.Borrego@Sun.COM 	tmpfile = mktemp(SMBNS_KRB5_KEYTAB_TMP);
23368334SJose.Borrego@Sun.COM 	if (tmpfile == NULL)
23378334SJose.Borrego@Sun.COM 		tmpfile = SMBNS_KRB5_KEYTAB_TMP;
23388334SJose.Borrego@Sun.COM 
2339*12508Samw@Sun.COM 	encptr = smb_ads_get_enctypes(dclevel, &num);
2340*12508Samw@Sun.COM 	if (smb_krb5_kt_populate(ctx, ah->domain, krb5princs, cnt,
2341*12508Samw@Sun.COM 	    tmpfile, kvno, machine_passwd, encptr, num) != 0) {
23427052Samw 		rc = SMB_ADJOIN_ERR_WRITE_KEYTAB;
23435331Samw 		goto adjoin_cleanup;
23445331Samw 	}
23455331Samw 
23465521Sas200622 	delete = B_FALSE;
23475331Samw adjoin_cleanup:
23485521Sas200622 	if (new_acct && delete)
23497348SJose.Borrego@Sun.COM 		smb_ads_del_computer(ah, dn);
23505331Samw 
23517052Samw 	if (rc != SMB_ADJOIN_ERR_INIT_KRB_CTX) {
23527052Samw 		if (rc != SMB_ADJOIN_ERR_GET_SPNS)
2353*12508Samw@Sun.COM 			smb_krb5_free_kprincs(ctx, krb5princs, cnt);
23545331Samw 		smb_krb5_ctx_fini(ctx);
23555967Scp160787 	}
23565331Samw 
23578334SJose.Borrego@Sun.COM 	/* commit keytab file */
23588334SJose.Borrego@Sun.COM 	if (rc == SMB_ADJOIN_SUCCESS) {
23598334SJose.Borrego@Sun.COM 		if (rename(tmpfile, SMBNS_KRB5_KEYTAB) != 0) {
23608334SJose.Borrego@Sun.COM 			(void) unlink(tmpfile);
23618334SJose.Borrego@Sun.COM 			rc = SMB_ADJOIN_ERR_COMMIT_KEYTAB;
23628334SJose.Borrego@Sun.COM 		} else {
23638334SJose.Borrego@Sun.COM 			/* Set IDMAP config */
23648334SJose.Borrego@Sun.COM 			if (smb_config_set_idmap_domain(ah->domain) != 0) {
23658334SJose.Borrego@Sun.COM 				rc = SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN;
23668334SJose.Borrego@Sun.COM 			} else {
23678334SJose.Borrego@Sun.COM 
23688334SJose.Borrego@Sun.COM 				/* Refresh IDMAP service */
23698334SJose.Borrego@Sun.COM 				if (smb_config_refresh_idmap() != 0)
23708334SJose.Borrego@Sun.COM 					rc = SMB_ADJOIN_ERR_IDMAP_REFRESH;
23718334SJose.Borrego@Sun.COM 			}
23728334SJose.Borrego@Sun.COM 		}
23738334SJose.Borrego@Sun.COM 	} else {
23748334SJose.Borrego@Sun.COM 		(void) unlink(tmpfile);
23758334SJose.Borrego@Sun.COM 	}
23768334SJose.Borrego@Sun.COM 
23777052Samw 	smb_ads_close(ah);
23786139Sjb150015 	smb_ccache_remove(SMB_CCACHE_PATH);
23795331Samw 	return (rc);
23805331Samw }
23815331Samw 
23825331Samw /*
23839021Samw@Sun.COM  * smb_ads_join_errmsg
23845331Samw  *
23855331Samw  * Display error message for the specific adjoin error code.
23865331Samw  */
23879021Samw@Sun.COM void
smb_ads_join_errmsg(smb_adjoin_status_t status)23889021Samw@Sun.COM smb_ads_join_errmsg(smb_adjoin_status_t status)
23895331Samw {
23909021Samw@Sun.COM 	int i;
23919021Samw@Sun.COM 	struct xlate_table {
23929021Samw@Sun.COM 		smb_adjoin_status_t status;
23939021Samw@Sun.COM 		char *msg;
23949021Samw@Sun.COM 	} adjoin_table[] = {
23959021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_GET_HANDLE, "Failed to connect to an "
23969021Samw@Sun.COM 		    "Active Directory server." },
239711571SShawn.Emery@Sun.COM 		{ SMB_ADJOIN_ERR_GEN_PWD, "Failed to generate machine "
239811571SShawn.Emery@Sun.COM 		    "password." },
23999021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_GET_DCLEVEL, "Unknown functional level of "
24009021Samw@Sun.COM 		    "the domain controller. The rootDSE attribute named "
24019021Samw@Sun.COM 		    "\"domainControllerFunctionality\" is missing from the "
24029021Samw@Sun.COM 		    "Active Directory." },
24039021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_ADD_TRUST_ACCT, "Failed to create the "
24049021Samw@Sun.COM 		    "workstation trust account." },
24059021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_MOD_TRUST_ACCT, "Failed to modify the "
24069021Samw@Sun.COM 		    "workstation trust account." },
24079021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_DUP_TRUST_ACCT, "Failed to create the "
24089021Samw@Sun.COM 		    "workstation trust account because its name is already "
24099021Samw@Sun.COM 		    "in use." },
24109021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_TRUST_ACCT, "Error in querying the "
24119021Samw@Sun.COM 		    "workstation trust account" },
24129021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_INIT_KRB_CTX, "Failed to initialize Kerberos "
24139021Samw@Sun.COM 		    "context." },
24149021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_GET_SPNS, "Failed to get Kerberos "
24159021Samw@Sun.COM 		    "principals." },
24169021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_KSETPWD, "Failed to set machine password." },
24179021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_UPDATE_CNTRL_ATTR,  "Failed to modify "
24189021Samw@Sun.COM 		    "userAccountControl attribute of the workstation trust "
24199021Samw@Sun.COM 		    "account." },
24209021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_WRITE_KEYTAB, "Error in writing to local "
24219021Samw@Sun.COM 		    "keytab file (i.e /etc/krb5/krb5.keytab)." },
24229021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_IDMAP_SET_DOMAIN, "Failed to update idmap "
24239021Samw@Sun.COM 		    "configuration." },
24249021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_IDMAP_REFRESH, "Failed to refresh idmap "
24259021Samw@Sun.COM 		    "service." },
24269021Samw@Sun.COM 		{ SMB_ADJOIN_ERR_COMMIT_KEYTAB, "Failed to commit changes to "
24279021Samw@Sun.COM 		    "local keytab file (i.e. /etc/krb5/krb5.keytab)." }
24289021Samw@Sun.COM 	};
24299021Samw@Sun.COM 
24309021Samw@Sun.COM 	for (i = 0; i < sizeof (adjoin_table) / sizeof (adjoin_table[0]); i++) {
24319021Samw@Sun.COM 		if (adjoin_table[i].status == status)
24329021Samw@Sun.COM 			syslog(LOG_NOTICE, "%s", adjoin_table[i].msg);
24339021Samw@Sun.COM 	}
24345331Samw }
24357348SJose.Borrego@Sun.COM 
24367348SJose.Borrego@Sun.COM /*
24379832Samw@Sun.COM  * smb_ads_match_pdc
24387961SNatalie.Li@Sun.COM  *
24399832Samw@Sun.COM  * Returns B_TRUE if the given host's IP address matches the preferred DC's
24409832Samw@Sun.COM  * IP address. Otherwise, returns B_FALSE.
24417961SNatalie.Li@Sun.COM  */
24429832Samw@Sun.COM static boolean_t
smb_ads_match_pdc(smb_ads_host_info_t * host)24439832Samw@Sun.COM smb_ads_match_pdc(smb_ads_host_info_t *host)
24447961SNatalie.Li@Sun.COM {
24459832Samw@Sun.COM 	boolean_t match = B_FALSE;
24469832Samw@Sun.COM 
24479832Samw@Sun.COM 	if (!host)
24489832Samw@Sun.COM 		return (match);
24499832Samw@Sun.COM 
24509832Samw@Sun.COM 	(void) mutex_lock(&smb_ads_cfg.c_mtx);
24519832Samw@Sun.COM 	if (smb_inet_equal(&host->ipaddr, &smb_ads_cfg.c_pdc))
24529832Samw@Sun.COM 		match = B_TRUE;
24539832Samw@Sun.COM 	(void) mutex_unlock(&smb_ads_cfg.c_mtx);
24549832Samw@Sun.COM 
24559832Samw@Sun.COM 	return (match);
24567961SNatalie.Li@Sun.COM }
24577961SNatalie.Li@Sun.COM 
24587961SNatalie.Li@Sun.COM /*
24597961SNatalie.Li@Sun.COM  * smb_ads_select_dcfromsubnet
24607961SNatalie.Li@Sun.COM  *
24617961SNatalie.Li@Sun.COM  * This method walks the list of DCs and returns the first DC record that
24627961SNatalie.Li@Sun.COM  * responds to ldap ping and is in the same subnet as the host.
24637961SNatalie.Li@Sun.COM  *
24647961SNatalie.Li@Sun.COM  * Returns a pointer to the found DC record.
24657961SNatalie.Li@Sun.COM  * Returns NULL, on error or if no DC record is found.
24667961SNatalie.Li@Sun.COM  */
24677961SNatalie.Li@Sun.COM static smb_ads_host_info_t *
smb_ads_select_dcfromsubnet(smb_ads_host_list_t * hlist)24687961SNatalie.Li@Sun.COM smb_ads_select_dcfromsubnet(smb_ads_host_list_t *hlist)
24697961SNatalie.Li@Sun.COM {
24707961SNatalie.Li@Sun.COM 	smb_ads_host_info_t *hentry;
24717961SNatalie.Li@Sun.COM 	smb_nic_t *lnic;
24727961SNatalie.Li@Sun.COM 	smb_niciter_t ni;
24737961SNatalie.Li@Sun.COM 	size_t cnt;
24747961SNatalie.Li@Sun.COM 	int i;
24757961SNatalie.Li@Sun.COM 
247611963SAfshin.Ardakani@Sun.COM 	if (smb_nic_getfirst(&ni) != SMB_NIC_SUCCESS)
24777961SNatalie.Li@Sun.COM 		return (NULL);
24787961SNatalie.Li@Sun.COM 	do {
24797961SNatalie.Li@Sun.COM 		lnic = &ni.ni_nic;
24807961SNatalie.Li@Sun.COM 		cnt = hlist->ah_cnt;
24817961SNatalie.Li@Sun.COM 
24827961SNatalie.Li@Sun.COM 		for (i = 0; i < cnt; i++) {
24837961SNatalie.Li@Sun.COM 			hentry = &hlist->ah_list[i];
24848670SJose.Borrego@Sun.COM 			if ((hentry->ipaddr.a_family == AF_INET) &&
24858670SJose.Borrego@Sun.COM 			    (lnic->nic_ip.a_family == AF_INET)) {
24868670SJose.Borrego@Sun.COM 				if ((hentry->ipaddr.a_ipv4 &
24878670SJose.Borrego@Sun.COM 				    lnic->nic_mask) ==
24888670SJose.Borrego@Sun.COM 				    (lnic->nic_ip.a_ipv4 &
24898670SJose.Borrego@Sun.COM 				    lnic->nic_mask))
24908670SJose.Borrego@Sun.COM 					if (smb_ads_ldap_ping(hentry) == 0)
24918670SJose.Borrego@Sun.COM 						return (hentry);
24928670SJose.Borrego@Sun.COM 			}
24937961SNatalie.Li@Sun.COM 		}
249411963SAfshin.Ardakani@Sun.COM 	} while (smb_nic_getnext(&ni) == SMB_NIC_SUCCESS);
24957961SNatalie.Li@Sun.COM 
24967961SNatalie.Li@Sun.COM 	return (NULL);
24977961SNatalie.Li@Sun.COM }
24987961SNatalie.Li@Sun.COM 
24997961SNatalie.Li@Sun.COM /*
25007961SNatalie.Li@Sun.COM  * smb_ads_select_dcfromlist
25017961SNatalie.Li@Sun.COM  *
25027961SNatalie.Li@Sun.COM  * This method walks the list of DCs and returns the first DC that
25037961SNatalie.Li@Sun.COM  * responds to ldap ping.
25047961SNatalie.Li@Sun.COM  *
25057961SNatalie.Li@Sun.COM  * Returns a pointer to the found DC record.
25067961SNatalie.Li@Sun.COM  * Returns NULL if no DC record is found.
25077961SNatalie.Li@Sun.COM  */
25087961SNatalie.Li@Sun.COM static smb_ads_host_info_t *
smb_ads_select_dcfromlist(smb_ads_host_list_t * hlist)25097961SNatalie.Li@Sun.COM smb_ads_select_dcfromlist(smb_ads_host_list_t *hlist)
25107961SNatalie.Li@Sun.COM {
25117961SNatalie.Li@Sun.COM 	smb_ads_host_info_t *hentry;
25127961SNatalie.Li@Sun.COM 	size_t cnt;
25137961SNatalie.Li@Sun.COM 	int i;
25147961SNatalie.Li@Sun.COM 
25157961SNatalie.Li@Sun.COM 	cnt = hlist->ah_cnt;
25167961SNatalie.Li@Sun.COM 	for (i = 0; i < cnt; i++) {
25177961SNatalie.Li@Sun.COM 		hentry = &hlist->ah_list[i];
25187961SNatalie.Li@Sun.COM 		if (smb_ads_ldap_ping(hentry) == 0)
25197961SNatalie.Li@Sun.COM 			return (hentry);
25207961SNatalie.Li@Sun.COM 	}
25217961SNatalie.Li@Sun.COM 
25227961SNatalie.Li@Sun.COM 	return (NULL);
25237961SNatalie.Li@Sun.COM }
25247961SNatalie.Li@Sun.COM 
25257961SNatalie.Li@Sun.COM /*
25267348SJose.Borrego@Sun.COM  * smb_ads_dc_compare
25277348SJose.Borrego@Sun.COM  *
25287348SJose.Borrego@Sun.COM  * Comparision function for sorting host entries (SRV records of DC) via qsort.
25297961SNatalie.Li@Sun.COM  * RFC 2052/2782 are taken as reference, while implementing this algorithm.
25307961SNatalie.Li@Sun.COM  *
25317961SNatalie.Li@Sun.COM  * Domain Controllers(DCs) with lowest priority in their SRV DNS records
25327961SNatalie.Li@Sun.COM  * are selected first. If they have equal priorities, then DC with highest
25337961SNatalie.Li@Sun.COM  * weight in its SRV DNS record is selected. If the priority and weight are
25347961SNatalie.Li@Sun.COM  * both equal, then the DC at the top of the list is selected.
25357348SJose.Borrego@Sun.COM  */
25367348SJose.Borrego@Sun.COM static int
smb_ads_dc_compare(const void * p,const void * q)25377348SJose.Borrego@Sun.COM smb_ads_dc_compare(const void *p, const void *q)
25387348SJose.Borrego@Sun.COM {
25397348SJose.Borrego@Sun.COM 	smb_ads_host_info_t *h1 = (smb_ads_host_info_t *)p;
25407348SJose.Borrego@Sun.COM 	smb_ads_host_info_t *h2 = (smb_ads_host_info_t *)q;
25417348SJose.Borrego@Sun.COM 
25427348SJose.Borrego@Sun.COM 	if (h1->priority < h2->priority)
25437348SJose.Borrego@Sun.COM 		return (-1);
25447348SJose.Borrego@Sun.COM 	if (h1->priority > h2->priority)
25457348SJose.Borrego@Sun.COM 		return (1);
25467348SJose.Borrego@Sun.COM 
25477348SJose.Borrego@Sun.COM 	/* Priorities are equal */
25487348SJose.Borrego@Sun.COM 	if (h1->weight < h2->weight)
25497348SJose.Borrego@Sun.COM 		return (1);
25507348SJose.Borrego@Sun.COM 	if (h1->weight > h2->weight)
25517348SJose.Borrego@Sun.COM 		return (-1);
25527348SJose.Borrego@Sun.COM 
25537348SJose.Borrego@Sun.COM 	return (0);
25547348SJose.Borrego@Sun.COM }
25557348SJose.Borrego@Sun.COM 
25567348SJose.Borrego@Sun.COM /*
25577348SJose.Borrego@Sun.COM  * smb_ads_select_dc
25587348SJose.Borrego@Sun.COM  *
25597961SNatalie.Li@Sun.COM  * The list of ADS hosts returned by ADS lookup, is sorted by lowest priority
25607961SNatalie.Li@Sun.COM  * and highest weight. On this sorted list, following additional rules are
25617961SNatalie.Li@Sun.COM  * applied, to select a DC.
25627348SJose.Borrego@Sun.COM  *
25637961SNatalie.Li@Sun.COM  *  - If there is a DC in the same subnet, then return the DC,
25647961SNatalie.Li@Sun.COM  *    if it responds to ldap ping.
25657961SNatalie.Li@Sun.COM  *  - Else, return first DC that responds to ldap ping.
25667961SNatalie.Li@Sun.COM  *
25677961SNatalie.Li@Sun.COM  * A reference to the host entry from input host list is returned.
25687348SJose.Borrego@Sun.COM  *
25697348SJose.Borrego@Sun.COM  * Returns NULL on error.
25707348SJose.Borrego@Sun.COM  */
25717348SJose.Borrego@Sun.COM static smb_ads_host_info_t *
smb_ads_select_dc(smb_ads_host_list_t * hlist)25727348SJose.Borrego@Sun.COM smb_ads_select_dc(smb_ads_host_list_t *hlist)
25737348SJose.Borrego@Sun.COM {
25747961SNatalie.Li@Sun.COM 	smb_ads_host_info_t *hentry = NULL;
25757961SNatalie.Li@Sun.COM 
25767961SNatalie.Li@Sun.COM 	if (hlist->ah_cnt == 0)
25777348SJose.Borrego@Sun.COM 		return (NULL);
25787348SJose.Borrego@Sun.COM 
25797961SNatalie.Li@Sun.COM 	if (hlist->ah_cnt == 1) {
25807961SNatalie.Li@Sun.COM 		hentry = hlist->ah_list;
25817961SNatalie.Li@Sun.COM 		if (smb_ads_ldap_ping(hentry) == 0)
25827961SNatalie.Li@Sun.COM 			return (hentry);
25837348SJose.Borrego@Sun.COM 	}
25847348SJose.Borrego@Sun.COM 
25857961SNatalie.Li@Sun.COM 	/* Sort the list by priority and weight */
25867961SNatalie.Li@Sun.COM 	qsort(hlist->ah_list, hlist->ah_cnt,
25877961SNatalie.Li@Sun.COM 	    sizeof (smb_ads_host_info_t), smb_ads_dc_compare);
25887961SNatalie.Li@Sun.COM 
25897961SNatalie.Li@Sun.COM 	if ((hentry = smb_ads_select_dcfromsubnet(hlist)) != NULL)
25907961SNatalie.Li@Sun.COM 		return (hentry);
25917961SNatalie.Li@Sun.COM 
25927961SNatalie.Li@Sun.COM 	if ((hentry = smb_ads_select_dcfromlist(hlist)) != NULL)
25937961SNatalie.Li@Sun.COM 		return (hentry);
25947961SNatalie.Li@Sun.COM 
25957348SJose.Borrego@Sun.COM 	return (NULL);
25967348SJose.Borrego@Sun.COM }
25978334SJose.Borrego@Sun.COM 
25988334SJose.Borrego@Sun.COM /*
25998334SJose.Borrego@Sun.COM  * smb_ads_lookup_msdcs
26008334SJose.Borrego@Sun.COM  *
26018334SJose.Borrego@Sun.COM  * If server argument is set, try to locate the specified DC.
26028334SJose.Borrego@Sun.COM  * If it is set to empty string, locate any DCs in the specified domain.
26038334SJose.Borrego@Sun.COM  * Returns the discovered DC via buf.
26048334SJose.Borrego@Sun.COM  *
26058334SJose.Borrego@Sun.COM  * fqdn	  - fully-qualified domain name
26068334SJose.Borrego@Sun.COM  * server - fully-qualifed hostname of a DC
26078334SJose.Borrego@Sun.COM  * buf    - the hostname of the discovered DC
26088334SJose.Borrego@Sun.COM  */
26098334SJose.Borrego@Sun.COM boolean_t
smb_ads_lookup_msdcs(char * fqdn,char * server,char * buf,uint32_t buflen)26108334SJose.Borrego@Sun.COM smb_ads_lookup_msdcs(char *fqdn, char *server, char *buf, uint32_t buflen)
26118334SJose.Borrego@Sun.COM {
26128334SJose.Borrego@Sun.COM 	smb_ads_host_info_t *hinfo = NULL;
26138334SJose.Borrego@Sun.COM 	char *p;
26148334SJose.Borrego@Sun.COM 	char *sought_host;
26158670SJose.Borrego@Sun.COM 	char ipstr[INET6_ADDRSTRLEN];
26168334SJose.Borrego@Sun.COM 
26178334SJose.Borrego@Sun.COM 	if (!fqdn || !buf)
26188334SJose.Borrego@Sun.COM 		return (B_FALSE);
26198334SJose.Borrego@Sun.COM 
26209252SChristopher.Parker@Sun.COM 	ipstr[0] = '\0';
26218334SJose.Borrego@Sun.COM 	*buf = '\0';
26228334SJose.Borrego@Sun.COM 	sought_host = (*server == 0 ? NULL : server);
26238334SJose.Borrego@Sun.COM 	if ((hinfo = smb_ads_find_host(fqdn, sought_host)) == NULL)
26248334SJose.Borrego@Sun.COM 		return (B_FALSE);
26258334SJose.Borrego@Sun.COM 
26269252SChristopher.Parker@Sun.COM 	(void) smb_inet_ntop(&hinfo->ipaddr, ipstr,
26279252SChristopher.Parker@Sun.COM 	    SMB_IPSTRLEN(hinfo->ipaddr.a_family));
26289252SChristopher.Parker@Sun.COM 	smb_tracef("msdcsLookupADS: %s [%s]", hinfo->name, ipstr);
26298334SJose.Borrego@Sun.COM 
26308334SJose.Borrego@Sun.COM 	(void) strlcpy(buf, hinfo->name, buflen);
26318334SJose.Borrego@Sun.COM 	/*
26328334SJose.Borrego@Sun.COM 	 * Remove the domain extension
26338334SJose.Borrego@Sun.COM 	 */
26348334SJose.Borrego@Sun.COM 	if ((p = strchr(buf, '.')) != 0)
26358334SJose.Borrego@Sun.COM 		*p = '\0';
26368334SJose.Borrego@Sun.COM 
26379832Samw@Sun.COM 	free(hinfo);
26388334SJose.Borrego@Sun.COM 	return (B_TRUE);
26398334SJose.Borrego@Sun.COM }
2640*12508Samw@Sun.COM 
2641*12508Samw@Sun.COM static krb5_enctype *
smb_ads_get_enctypes(int dclevel,int * num)2642*12508Samw@Sun.COM smb_ads_get_enctypes(int dclevel, int *num)
2643*12508Samw@Sun.COM {
2644*12508Samw@Sun.COM 	krb5_enctype *encptr;
2645*12508Samw@Sun.COM 
2646*12508Samw@Sun.COM 	if (dclevel >= SMB_ADS_DCLEVEL_W2K8) {
2647*12508Samw@Sun.COM 		*num = sizeof (w2k8enctypes) / sizeof (krb5_enctype);
2648*12508Samw@Sun.COM 		encptr = w2k8enctypes;
2649*12508Samw@Sun.COM 	} else {
2650*12508Samw@Sun.COM 		*num = sizeof (pre_w2k8enctypes) / sizeof (krb5_enctype);
2651*12508Samw@Sun.COM 		encptr = pre_w2k8enctypes;
2652*12508Samw@Sun.COM 	}
2653*12508Samw@Sun.COM 
2654*12508Samw@Sun.COM 	return (encptr);
2655*12508Samw@Sun.COM }
2656