xref: /onnv-gate/usr/src/cmd/smbsrv/smbd/smbd_join.c (revision 5772:237ac22142fe)
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 /*
22*5772Sas200622  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw #pragma ident	"%Z%%M%	%I%	%E% SMI"
275331Samw 
285331Samw #include <syslog.h>
295331Samw #include <synch.h>
305331Samw #include <pthread.h>
315331Samw #include <unistd.h>
325331Samw #include <string.h>
335331Samw #include <strings.h>
345331Samw #include <sys/errno.h>
355331Samw 
365331Samw #include <smbsrv/libsmb.h>
375331Samw #include <smbsrv/libsmbrdr.h>
385331Samw #include <smbsrv/libsmbns.h>
395331Samw #include <smbsrv/libmlsvc.h>
405331Samw 
415331Samw #include <smbsrv/smbinfo.h>
425331Samw #include <smbsrv/ntstatus.h>
435331Samw #include <smbsrv/lsalib.h>
445331Samw 
455331Samw /*
465331Samw  * Local protocol flags used to indicate which version of the
475331Samw  * netlogon protocol to use when attempting to find the PDC.
485331Samw  */
495331Samw #define	NETLOGON_PROTO_NETLOGON			0x01
505331Samw #define	NETLOGON_PROTO_SAMLOGON			0x02
515331Samw 
525331Samw /*
535331Samw  * Maximum time to wait for a domain controller (30 seconds).
545331Samw  */
555331Samw #define	SMB_NETLOGON_TIMEOUT	30
565331Samw 
575331Samw /*
585331Samw  * Flags used in conjunction with the location and query condition
595331Samw  * variables.
605331Samw  */
615331Samw #define	SMB_NETLF_LOCATE_DC	0x00000001
625331Samw #define	SMB_NETLF_LSA_QUERY	0x00000002
635331Samw 
645331Samw typedef struct smb_netlogon_info {
655331Samw 	char snli_domain[SMB_PI_MAX_DOMAIN];
665331Samw 	unsigned snli_flags;
675331Samw 	mutex_t snli_locate_mtx;
685331Samw 	cond_t snli_locate_cv;
695331Samw 	mutex_t snli_query_mtx;
705331Samw 	cond_t snli_query_cv;
715331Samw 	uint32_t snli_status;
725331Samw } smb_netlogon_info_t;
735331Samw 
745331Samw static smb_netlogon_info_t smb_netlogon_info;
755331Samw 
765331Samw static pthread_t lsa_monitor_thr;
775331Samw static pthread_t dc_browser_thr;
785331Samw 
795331Samw static void *smb_netlogon_lsa_monitor(void *arg);
805331Samw static void *smb_netlogon_dc_browser(void *arg);
815331Samw 
825331Samw /*
835331Samw  * Inline convenience function to find out if the domain information is
845331Samw  * valid. The caller can decide whether or not to wait.
855331Samw  */
865331Samw static boolean_t
875331Samw smb_ntdomain_is_valid(uint32_t timeout)
885331Samw {
895331Samw 	smb_ntdomain_t *info;
905331Samw 
915331Samw 	if ((info = smb_getdomaininfo(timeout)) != 0) {
925331Samw 		if (info->ipaddr != 0)
935331Samw 			return (B_TRUE);
945331Samw 	}
955331Samw 
965331Samw 	return (B_FALSE);
975331Samw }
985331Samw 
995331Samw /*
1005331Samw  * smbd_join
1015331Samw  *
1025331Samw  * Joins the specified domain/workgroup
1035331Samw  */
1045331Samw uint32_t
1055331Samw smbd_join(smb_joininfo_t *info)
1065331Samw {
1075331Samw 	smb_ntdomain_t *pi;
1085331Samw 	uint32_t status;
1095331Samw 	unsigned char passwd_hash[SMBAUTH_HASH_SZ];
1105331Samw 	char plain_passwd[PASS_LEN + 1];
1115331Samw 	char plain_user[PASS_LEN + 1];
112*5772Sas200622 	char nbt_domain[SMB_PI_MAX_DOMAIN];
113*5772Sas200622 	char fqdn[MAXHOSTNAMELEN];
1145331Samw 
1155331Samw 	if (info->mode == SMB_SECMODE_WORKGRP) {
116*5772Sas200622 		if (smb_config_get_secmode() == SMB_SECMODE_DOMAIN) {
117*5772Sas200622 			if (ads_domain_change_cleanup("") != 0) {
118*5772Sas200622 				syslog(LOG_ERR, "smbd: unable to remove the"
119*5772Sas200622 				    " old keys from the Kerberos keytab. "
120*5772Sas200622 				    "Please remove the old keys for your "
121*5772Sas200622 				    "host principal.");
122*5772Sas200622 			}
123*5772Sas200622 		}
1245331Samw 		(void) smb_config_set_secmode(info->mode);
125*5772Sas200622 		(void) smb_config_setstr(SMB_CI_DOMAIN_NAME, info->domain_name);
1265331Samw 		return (NT_STATUS_SUCCESS);
1275331Samw 	}
1285331Samw 
1295331Samw 	/*
1305331Samw 	 * Ensure that any previous membership of this domain has
1315331Samw 	 * been cleared from the environment before we start. This
1325331Samw 	 * will ensure that we don't attempt a NETLOGON_SAMLOGON
1335331Samw 	 * when attempting to find the PDC.
1345331Samw 	 */
135*5772Sas200622 	(void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE);
136*5772Sas200622 
137*5772Sas200622 	(void) strlcpy(plain_user, info->domain_username, sizeof (plain_user));
138*5772Sas200622 	(void) strlcpy(plain_passwd, info->domain_passwd,
139*5772Sas200622 	    sizeof (plain_passwd));
140*5772Sas200622 	(void) smb_resolve_netbiosname(info->domain_name, nbt_domain,
141*5772Sas200622 	    sizeof (nbt_domain));
1425331Samw 
143*5772Sas200622 	if (smb_resolve_fqdn(info->domain_name, fqdn, sizeof (fqdn)) != 1) {
144*5772Sas200622 		syslog(LOG_ERR, "smbd: fully-qualified domain name is unknown");
145*5772Sas200622 		return (NT_STATUS_INVALID_PARAMETER);
146*5772Sas200622 	}
147*5772Sas200622 
148*5772Sas200622 	if (ads_domain_change_cleanup(fqdn)) {
149*5772Sas200622 		syslog(LOG_ERR, "smbd: unable to remove the old keys from the"
150*5772Sas200622 		    " Kerberos keytab. Please remove the old keys for your "
151*5772Sas200622 		    "host principal.");
152*5772Sas200622 		return (NT_STATUS_INTERNAL_ERROR);
153*5772Sas200622 	}
1545331Samw 
1555331Samw 	if (smb_auth_ntlm_hash(plain_passwd, passwd_hash) != SMBAUTH_SUCCESS) {
1565331Samw 		status = NT_STATUS_INTERNAL_ERROR;
1575331Samw 		syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'",
1585331Samw 		    plain_user);
1595331Samw 		return (status);
1605331Samw 	}
1615331Samw 
1625331Samw 	smbrdr_ipc_set(plain_user, passwd_hash);
1635331Samw 
164*5772Sas200622 	if (locate_resource_pdc(nbt_domain)) {
1655331Samw 		if ((pi = smb_getdomaininfo(0)) == 0) {
1665331Samw 			status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1675331Samw 			syslog(LOG_ERR, "smbd: could not get domain controller"
1685331Samw 			    "information for '%s'", info->domain_name);
1695331Samw 			return (status);
1705331Samw 		}
1715331Samw 
1725331Samw 		/*
1735331Samw 		 * Temporary delay before creating
1745331Samw 		 * the workstation trust account.
1755331Samw 		 */
1765331Samw 		(void) sleep(2);
1775521Sas200622 		status = mlsvc_join(pi->server, pi->domain,
1785331Samw 		    plain_user, plain_passwd);
1795331Samw 
1805331Samw 		if (status == NT_STATUS_SUCCESS) {
1815331Samw 			(void) smb_config_set_secmode(SMB_SECMODE_DOMAIN);
182*5772Sas200622 			(void) smb_config_setstr(SMB_CI_DOMAIN_NAME,
1835331Samw 			    info->domain_name);
1845331Samw 			smbrdr_ipc_commit();
1855331Samw 			return (status);
1865331Samw 		}
1875331Samw 
1885331Samw 		smbrdr_ipc_rollback();
1895331Samw 		syslog(LOG_ERR, "smbd: failed joining %s (%s)",
1905331Samw 		    info->domain_name, xlate_nt_status(status));
1915331Samw 		return (status);
1925331Samw 	}
1935331Samw 
1945331Samw 	smbrdr_ipc_rollback();
1955331Samw 	syslog(LOG_ERR, "smbd: failed locating domain controller for %s",
1965331Samw 	    info->domain_name);
1975331Samw 	return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND);
1985331Samw }
1995331Samw 
2005331Samw /*
2015331Samw  * locate_resource_pdc
2025331Samw  *
2035331Samw  * This is the entry point for discovering a domain controller for the
2045331Samw  * specified domain. The caller may block here for around 30 seconds if
2055331Samw  * the system has to go to the network and find a domain controller.
2065331Samw  * Sometime it would be good to change this to smb_locate_pdc and allow
2075331Samw  * the caller to specify whether or not he wants to wait for a response.
2085331Samw  *
2095331Samw  * The actual work of discovering a DC is handled by other threads.
2105331Samw  * All we do here is signal the request and wait for a DC or a timeout.
2115331Samw  *
2125331Samw  * Returns B_TRUE if a domain controller is available.
2135331Samw  */
2145331Samw boolean_t
2155331Samw locate_resource_pdc(char *domain)
2165331Samw {
2175331Samw 	int rc;
2185331Samw 	timestruc_t to;
2195331Samw 
2205331Samw 	if (domain == NULL || *domain == '\0')
2215331Samw 		return (B_FALSE);
2225331Samw 
2235331Samw 	(void) mutex_lock(&smb_netlogon_info.snli_locate_mtx);
2245331Samw 
2255331Samw 	if ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 0) {
2265331Samw 		smb_netlogon_info.snli_flags |= SMB_NETLF_LOCATE_DC;
2275331Samw 		(void) strlcpy(smb_netlogon_info.snli_domain, domain,
2285331Samw 		    SMB_PI_MAX_DOMAIN);
2295331Samw 		(void) cond_broadcast(&smb_netlogon_info.snli_locate_cv);
2305331Samw 	}
2315331Samw 
2325331Samw 	while (smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) {
2335331Samw 		to.tv_sec = SMB_NETLOGON_TIMEOUT;
2345331Samw 		to.tv_nsec = 0;
2355331Samw 		rc = cond_reltimedwait(&smb_netlogon_info.snli_locate_cv,
2365331Samw 		    &smb_netlogon_info.snli_locate_mtx, &to);
2375331Samw 
2385331Samw 		if (rc == ETIME)
2395331Samw 			break;
2405331Samw 	}
2415331Samw 
2425331Samw 	(void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx);
2435331Samw 
2445331Samw 	return (smb_ntdomain_is_valid(0));
2455331Samw }
2465331Samw 
2475331Samw /*
2485331Samw  * smb_netlogon_init
2495331Samw  *
2505331Samw  * Initialization of the DC browser and LSA monitor threads.
2515331Samw  * Returns 0 on success, an error number if thread creation fails.
2525331Samw  */
2535331Samw int
2545331Samw smb_netlogon_init(void)
2555331Samw {
2565331Samw 	pthread_attr_t tattr;
2575331Samw 	int rc;
2585331Samw 
2595331Samw 	(void) pthread_attr_init(&tattr);
2605331Samw 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
2615331Samw 	rc = pthread_create(&lsa_monitor_thr, &tattr,
2625331Samw 	    smb_netlogon_lsa_monitor, 0);
2635331Samw 	if (rc != 0)
2645331Samw 		goto nli_exit;
2655331Samw 	rc = pthread_create(&dc_browser_thr, &tattr,
2665331Samw 	    smb_netlogon_dc_browser, 0);
2675331Samw 	if (rc != 0) {
2685331Samw 		(void) pthread_cancel(lsa_monitor_thr);
2695331Samw 		(void) pthread_join(lsa_monitor_thr, NULL);
2705331Samw 	}
2715331Samw 
2725331Samw nli_exit:
2735331Samw 	(void) pthread_attr_destroy(&tattr);
2745331Samw 	return (rc);
2755331Samw }
2765331Samw 
2775331Samw /*
2785331Samw  * smb_netlogon_dc_browser
2795331Samw  *
2805331Samw  * This is the DC browser thread: it gets woken up whenever someone
2815331Samw  * wants to locate a domain controller.
2825331Samw  *
2835331Samw  * With the introduction of Windows 2000, NetBIOS is no longer a
2845331Samw  * requirement for NT domains. If NetBIOS has been disabled on the
2855331Samw  * network there will be no browsers and we won't get any response
2865331Samw  * to netlogon requests. So we try to find a DC controller via ADS
2875331Samw  * first. If ADS is disabled or the DNS query fails, we drop back
2885331Samw  * to the netlogon protocol.
2895331Samw  *
2905331Samw  * This function will block for up to 30 seconds waiting for the PDC
2915331Samw  * to be discovered. Sometime it would be good to change this to
2925331Samw  * smb_locate_pdc and allow the caller to specify whether or not he
2935331Samw  * wants to wait for a response.
2945331Samw  *
2955331Samw  */
2965331Samw /*ARGSUSED*/
2975331Samw static void *
2985331Samw smb_netlogon_dc_browser(void *arg)
2995331Samw {
3005331Samw 	boolean_t rc;
3015331Samw 	char resource_domain[SMB_PI_MAX_DOMAIN];
3025331Samw 	int net, smb_nc_cnt;
3035331Samw 	int protocol;
3045331Samw 
3055331Samw 	for (;;) {
3065331Samw 		(void) mutex_lock(&smb_netlogon_info.snli_locate_mtx);
3075331Samw 
3085331Samw 		while ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) ==
3095331Samw 		    0) {
3105331Samw 			(void) cond_wait(&smb_netlogon_info.snli_locate_cv,
3115331Samw 			    &smb_netlogon_info.snli_locate_mtx);
3125331Samw 		}
3135331Samw 
3145331Samw 		(void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx);
3155331Samw 
3165331Samw 		(void) strlcpy(resource_domain, smb_netlogon_info.snli_domain,
3175331Samw 		    SMB_PI_MAX_DOMAIN);
3185331Samw 
3195331Samw 		smb_setdomaininfo(NULL, NULL, 0);
320*5772Sas200622 		if (msdcs_lookup_ads(resource_domain) == 0) {
321*5772Sas200622 			if (smb_config_getbool(SMB_CI_DOMAIN_MEMB))
3225331Samw 				protocol = NETLOGON_PROTO_SAMLOGON;
3235331Samw 			else
3245331Samw 				protocol = NETLOGON_PROTO_NETLOGON;
3255331Samw 
3265331Samw 			smb_nc_cnt = smb_nic_get_num();
3275331Samw 			for (net = 0; net < smb_nc_cnt; net++) {
3285331Samw 				smb_netlogon_request(net, protocol,
3295331Samw 				    resource_domain);
3305331Samw 			}
3315331Samw 		}
3325331Samw 
3335331Samw 		rc = smb_ntdomain_is_valid(SMB_NETLOGON_TIMEOUT);
3345331Samw 
3355331Samw 		(void) mutex_lock(&smb_netlogon_info.snli_locate_mtx);
3365331Samw 		smb_netlogon_info.snli_flags &= ~SMB_NETLF_LOCATE_DC;
3375331Samw 		(void) cond_broadcast(&smb_netlogon_info.snli_locate_cv);
3385331Samw 		(void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx);
3395331Samw 
3405331Samw 		if (rc != B_TRUE) {
3415331Samw 			/*
3425331Samw 			 * Notify the LSA monitor to update the
3435331Samw 			 * primary and trusted domain information.
3445331Samw 			 */
3455331Samw 			(void) mutex_lock(&smb_netlogon_info.snli_query_mtx);
3465331Samw 			smb_netlogon_info.snli_flags |= SMB_NETLF_LSA_QUERY;
3475331Samw 			(void) cond_broadcast(&smb_netlogon_info.snli_query_cv);
3485331Samw 			(void) mutex_unlock(&smb_netlogon_info.snli_query_mtx);
3495331Samw 		}
3505331Samw 	}
3515331Samw 
3525331Samw 	/*NOTREACHED*/
3535331Samw 	return (NULL);
3545331Samw }
3555331Samw 
3565331Samw /*
3575331Samw  * smb_netlogon_lsa_monitor
3585331Samw  *
3595331Samw  * This monitor should run as a separate thread. It waits on a condition
3605331Samw  * variable until someone indicates that the LSA domain information needs
3615331Samw  * to be refreshed. It then queries the DC for the NT domain information:
3625331Samw  * primary, account and trusted domains. The condition variable should be
3635331Samw  * signaled whenever a DC is selected.
3645331Samw  *
3655331Samw  * Note that the LSA query calls require the DC information and this task
3665331Samw  * may end up blocked on the DC location protocol, which is why this
3675331Samw  * monitor is run as a separate thread. This should only happen if the DC
3685331Samw  * goes down immediately after we located it.
3695331Samw  */
3705331Samw /*ARGSUSED*/
3715331Samw static void *
3725331Samw smb_netlogon_lsa_monitor(void *arg)
3735331Samw {
3745331Samw 	uint32_t status;
3755331Samw 
3765331Samw 	for (;;) {
3775331Samw 		(void) mutex_lock(&smb_netlogon_info.snli_query_mtx);
3785331Samw 
3795331Samw 		while ((smb_netlogon_info.snli_flags & SMB_NETLF_LSA_QUERY) ==
3805331Samw 		    0) {
3815331Samw 			(void) cond_wait(&smb_netlogon_info.snli_query_cv,
3825331Samw 			    &smb_netlogon_info.snli_query_mtx);
3835331Samw 		}
3845331Samw 
3855331Samw 		smb_netlogon_info.snli_flags &= ~SMB_NETLF_LSA_QUERY;
3865331Samw 		(void) mutex_unlock(&smb_netlogon_info.snli_query_mtx);
3875331Samw 
3885331Samw 		/*
3895331Samw 		 * Skip the LSA query if Authenticated IPC is supported
3905331Samw 		 * and the credential is not yet set.
3915331Samw 		 */
3925331Samw 		if (smbrdr_ipc_skip_lsa_query() == 0) {
3935331Samw 			status = lsa_query_primary_domain_info();
3945331Samw 			if (status == NT_STATUS_SUCCESS) {
3955331Samw 				if (lsa_query_account_domain_info()
3965331Samw 				    != NT_STATUS_SUCCESS) {
3975331Samw 					syslog(LOG_DEBUG,
3985331Samw 					    "NetlogonLSAMonitor: query "
3995331Samw 					    "account info failed");
4005331Samw 				}
4015331Samw 				if (lsa_enum_trusted_domains()
4025331Samw 				    != NT_STATUS_SUCCESS) {
4035331Samw 					syslog(LOG_DEBUG,
4045331Samw 					    "NetlogonLSAMonitor: enum "
4055331Samw 					    "trusted domain failed");
4065331Samw 				}
4075331Samw 			} else {
4085331Samw 				syslog(LOG_DEBUG,
4095331Samw 				    "NetlogonLSAMonitor: update failed");
4105331Samw 			}
4115331Samw 		}
4125331Samw 	}
4135331Samw 
4145331Samw 	/*NOTREACHED*/
4155331Samw 	return (NULL);
4165331Samw }
417