xref: /onnv-gate/usr/src/cmd/smbsrv/smbd/smbd_join.c (revision 5521:cf62335046cd)
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 /*
225331Samw  * Copyright 2007 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];
1125331Samw 
1135331Samw 	if (info->mode == SMB_SECMODE_WORKGRP) {
1145331Samw 		smb_config_wrlock();
1155331Samw 		(void) smb_config_set_secmode(info->mode);
1165331Samw 		(void) smb_config_set(SMB_CI_DOMAIN_NAME, info->domain_name);
1175331Samw 		smb_config_unlock();
1185331Samw 		return (NT_STATUS_SUCCESS);
1195331Samw 	}
1205331Samw 
1215331Samw 	/*
1225331Samw 	 * Ensure that any previous membership of this domain has
1235331Samw 	 * been cleared from the environment before we start. This
1245331Samw 	 * will ensure that we don't attempt a NETLOGON_SAMLOGON
1255331Samw 	 * when attempting to find the PDC.
1265331Samw 	 */
1275331Samw 	smb_set_domain_member(0);
1285331Samw 
1295331Samw 	(void) strcpy(plain_user, info->domain_username);
1305331Samw 	(void) strcpy(plain_passwd, info->domain_passwd);
1315331Samw 
1325331Samw 	if (smb_auth_ntlm_hash(plain_passwd, passwd_hash) != SMBAUTH_SUCCESS) {
1335331Samw 		status = NT_STATUS_INTERNAL_ERROR;
1345331Samw 		syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'",
1355331Samw 		    plain_user);
1365331Samw 		return (status);
1375331Samw 	}
1385331Samw 
1395331Samw 	smbrdr_ipc_set(plain_user, passwd_hash);
1405331Samw 
1415331Samw 	if (locate_resource_pdc(info->domain_name)) {
1425331Samw 		if ((pi = smb_getdomaininfo(0)) == 0) {
1435331Samw 			status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
1445331Samw 			syslog(LOG_ERR, "smbd: could not get domain controller"
1455331Samw 			    "information for '%s'", info->domain_name);
1465331Samw 			return (status);
1475331Samw 		}
1485331Samw 
1495331Samw 		/*
1505331Samw 		 * Temporary delay before creating
1515331Samw 		 * the workstation trust account.
1525331Samw 		 */
1535331Samw 		(void) sleep(2);
154*5521Sas200622 		status = mlsvc_join(pi->server, pi->domain,
1555331Samw 		    plain_user, plain_passwd);
1565331Samw 
1575331Samw 		if (status == NT_STATUS_SUCCESS) {
1585331Samw 			smb_config_wrlock();
1595331Samw 			(void) smb_config_set_secmode(SMB_SECMODE_DOMAIN);
1605331Samw 			(void) smb_config_set(SMB_CI_DOMAIN_NAME,
1615331Samw 			    info->domain_name);
1625331Samw 			smb_config_unlock();
1635331Samw 			smbrdr_ipc_commit();
1645331Samw 			return (status);
1655331Samw 		}
1665331Samw 
1675331Samw 		smbrdr_ipc_rollback();
1685331Samw 		syslog(LOG_ERR, "smbd: failed joining %s (%s)",
1695331Samw 		    info->domain_name, xlate_nt_status(status));
1705331Samw 		return (status);
1715331Samw 	}
1725331Samw 
1735331Samw 	smbrdr_ipc_rollback();
1745331Samw 	syslog(LOG_ERR, "smbd: failed locating domain controller for %s",
1755331Samw 	    info->domain_name);
1765331Samw 	return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND);
1775331Samw }
1785331Samw 
1795331Samw /*
1805331Samw  * locate_resource_pdc
1815331Samw  *
1825331Samw  * This is the entry point for discovering a domain controller for the
1835331Samw  * specified domain. The caller may block here for around 30 seconds if
1845331Samw  * the system has to go to the network and find a domain controller.
1855331Samw  * Sometime it would be good to change this to smb_locate_pdc and allow
1865331Samw  * the caller to specify whether or not he wants to wait for a response.
1875331Samw  *
1885331Samw  * The actual work of discovering a DC is handled by other threads.
1895331Samw  * All we do here is signal the request and wait for a DC or a timeout.
1905331Samw  *
1915331Samw  * Returns B_TRUE if a domain controller is available.
1925331Samw  */
1935331Samw boolean_t
1945331Samw locate_resource_pdc(char *domain)
1955331Samw {
1965331Samw 	int rc;
1975331Samw 	timestruc_t to;
1985331Samw 
1995331Samw 	if (domain == NULL || *domain == '\0')
2005331Samw 		return (B_FALSE);
2015331Samw 
2025331Samw 	(void) mutex_lock(&smb_netlogon_info.snli_locate_mtx);
2035331Samw 
2045331Samw 	if ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 0) {
2055331Samw 		smb_netlogon_info.snli_flags |= SMB_NETLF_LOCATE_DC;
2065331Samw 		(void) strlcpy(smb_netlogon_info.snli_domain, domain,
2075331Samw 		    SMB_PI_MAX_DOMAIN);
2085331Samw 		(void) cond_broadcast(&smb_netlogon_info.snli_locate_cv);
2095331Samw 	}
2105331Samw 
2115331Samw 	while (smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) {
2125331Samw 		to.tv_sec = SMB_NETLOGON_TIMEOUT;
2135331Samw 		to.tv_nsec = 0;
2145331Samw 		rc = cond_reltimedwait(&smb_netlogon_info.snli_locate_cv,
2155331Samw 		    &smb_netlogon_info.snli_locate_mtx, &to);
2165331Samw 
2175331Samw 		if (rc == ETIME)
2185331Samw 			break;
2195331Samw 	}
2205331Samw 
2215331Samw 	(void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx);
2225331Samw 
2235331Samw 	return (smb_ntdomain_is_valid(0));
2245331Samw }
2255331Samw 
2265331Samw /*
2275331Samw  * smb_netlogon_init
2285331Samw  *
2295331Samw  * Initialization of the DC browser and LSA monitor threads.
2305331Samw  * Returns 0 on success, an error number if thread creation fails.
2315331Samw  */
2325331Samw int
2335331Samw smb_netlogon_init(void)
2345331Samw {
2355331Samw 	pthread_attr_t tattr;
2365331Samw 	int rc;
2375331Samw 
2385331Samw 	(void) pthread_attr_init(&tattr);
2395331Samw 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
2405331Samw 	rc = pthread_create(&lsa_monitor_thr, &tattr,
2415331Samw 	    smb_netlogon_lsa_monitor, 0);
2425331Samw 	if (rc != 0)
2435331Samw 		goto nli_exit;
2445331Samw 	rc = pthread_create(&dc_browser_thr, &tattr,
2455331Samw 	    smb_netlogon_dc_browser, 0);
2465331Samw 	if (rc != 0) {
2475331Samw 		(void) pthread_cancel(lsa_monitor_thr);
2485331Samw 		(void) pthread_join(lsa_monitor_thr, NULL);
2495331Samw 	}
2505331Samw 
2515331Samw nli_exit:
2525331Samw 	(void) pthread_attr_destroy(&tattr);
2535331Samw 	return (rc);
2545331Samw }
2555331Samw 
2565331Samw /*
2575331Samw  * smb_netlogon_dc_browser
2585331Samw  *
2595331Samw  * This is the DC browser thread: it gets woken up whenever someone
2605331Samw  * wants to locate a domain controller.
2615331Samw  *
2625331Samw  * With the introduction of Windows 2000, NetBIOS is no longer a
2635331Samw  * requirement for NT domains. If NetBIOS has been disabled on the
2645331Samw  * network there will be no browsers and we won't get any response
2655331Samw  * to netlogon requests. So we try to find a DC controller via ADS
2665331Samw  * first. If ADS is disabled or the DNS query fails, we drop back
2675331Samw  * to the netlogon protocol.
2685331Samw  *
2695331Samw  * This function will block for up to 30 seconds waiting for the PDC
2705331Samw  * to be discovered. Sometime it would be good to change this to
2715331Samw  * smb_locate_pdc and allow the caller to specify whether or not he
2725331Samw  * wants to wait for a response.
2735331Samw  *
2745331Samw  */
2755331Samw /*ARGSUSED*/
2765331Samw static void *
2775331Samw smb_netlogon_dc_browser(void *arg)
2785331Samw {
2795331Samw 	boolean_t rc;
2805331Samw 	char resource_domain[SMB_PI_MAX_DOMAIN];
2815331Samw 	int net, smb_nc_cnt;
2825331Samw 	int protocol;
2835331Samw 
2845331Samw 	for (;;) {
2855331Samw 		(void) mutex_lock(&smb_netlogon_info.snli_locate_mtx);
2865331Samw 
2875331Samw 		while ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) ==
2885331Samw 		    0) {
2895331Samw 			(void) cond_wait(&smb_netlogon_info.snli_locate_cv,
2905331Samw 			    &smb_netlogon_info.snli_locate_mtx);
2915331Samw 		}
2925331Samw 
2935331Samw 		(void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx);
2945331Samw 
2955331Samw 		(void) strlcpy(resource_domain, smb_netlogon_info.snli_domain,
2965331Samw 		    SMB_PI_MAX_DOMAIN);
2975331Samw 
2985331Samw 		smb_setdomaininfo(NULL, NULL, 0);
2995331Samw 		if (msdcs_lookup_ads() == 0) {
3005331Samw 			if (smb_is_domain_member())
3015331Samw 				protocol = NETLOGON_PROTO_SAMLOGON;
3025331Samw 			else
3035331Samw 				protocol = NETLOGON_PROTO_NETLOGON;
3045331Samw 
3055331Samw 			smb_nc_cnt = smb_nic_get_num();
3065331Samw 			for (net = 0; net < smb_nc_cnt; net++) {
3075331Samw 				smb_netlogon_request(net, protocol,
3085331Samw 				    resource_domain);
3095331Samw 			}
3105331Samw 		}
3115331Samw 
3125331Samw 		rc = smb_ntdomain_is_valid(SMB_NETLOGON_TIMEOUT);
3135331Samw 
3145331Samw 		(void) mutex_lock(&smb_netlogon_info.snli_locate_mtx);
3155331Samw 		smb_netlogon_info.snli_flags &= ~SMB_NETLF_LOCATE_DC;
3165331Samw 		(void) cond_broadcast(&smb_netlogon_info.snli_locate_cv);
3175331Samw 		(void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx);
3185331Samw 
3195331Samw 		if (rc != B_TRUE) {
3205331Samw 			/*
3215331Samw 			 * Notify the LSA monitor to update the
3225331Samw 			 * primary and trusted domain information.
3235331Samw 			 */
3245331Samw 			(void) mutex_lock(&smb_netlogon_info.snli_query_mtx);
3255331Samw 			smb_netlogon_info.snli_flags |= SMB_NETLF_LSA_QUERY;
3265331Samw 			(void) cond_broadcast(&smb_netlogon_info.snli_query_cv);
3275331Samw 			(void) mutex_unlock(&smb_netlogon_info.snli_query_mtx);
3285331Samw 		}
3295331Samw 	}
3305331Samw 
3315331Samw 	/*NOTREACHED*/
3325331Samw 	return (NULL);
3335331Samw }
3345331Samw 
3355331Samw /*
3365331Samw  * smb_netlogon_lsa_monitor
3375331Samw  *
3385331Samw  * This monitor should run as a separate thread. It waits on a condition
3395331Samw  * variable until someone indicates that the LSA domain information needs
3405331Samw  * to be refreshed. It then queries the DC for the NT domain information:
3415331Samw  * primary, account and trusted domains. The condition variable should be
3425331Samw  * signaled whenever a DC is selected.
3435331Samw  *
3445331Samw  * Note that the LSA query calls require the DC information and this task
3455331Samw  * may end up blocked on the DC location protocol, which is why this
3465331Samw  * monitor is run as a separate thread. This should only happen if the DC
3475331Samw  * goes down immediately after we located it.
3485331Samw  */
3495331Samw /*ARGSUSED*/
3505331Samw static void *
3515331Samw smb_netlogon_lsa_monitor(void *arg)
3525331Samw {
3535331Samw 	uint32_t status;
3545331Samw 
3555331Samw 	for (;;) {
3565331Samw 		(void) mutex_lock(&smb_netlogon_info.snli_query_mtx);
3575331Samw 
3585331Samw 		while ((smb_netlogon_info.snli_flags & SMB_NETLF_LSA_QUERY) ==
3595331Samw 		    0) {
3605331Samw 			(void) cond_wait(&smb_netlogon_info.snli_query_cv,
3615331Samw 			    &smb_netlogon_info.snli_query_mtx);
3625331Samw 		}
3635331Samw 
3645331Samw 		smb_netlogon_info.snli_flags &= ~SMB_NETLF_LSA_QUERY;
3655331Samw 		(void) mutex_unlock(&smb_netlogon_info.snli_query_mtx);
3665331Samw 
3675331Samw 		/*
3685331Samw 		 * Skip the LSA query if Authenticated IPC is supported
3695331Samw 		 * and the credential is not yet set.
3705331Samw 		 */
3715331Samw 		if (smbrdr_ipc_skip_lsa_query() == 0) {
3725331Samw 			status = lsa_query_primary_domain_info();
3735331Samw 			if (status == NT_STATUS_SUCCESS) {
3745331Samw 				if (lsa_query_account_domain_info()
3755331Samw 				    != NT_STATUS_SUCCESS) {
3765331Samw 					syslog(LOG_DEBUG,
3775331Samw 					    "NetlogonLSAMonitor: query "
3785331Samw 					    "account info failed");
3795331Samw 				}
3805331Samw 				if (lsa_enum_trusted_domains()
3815331Samw 				    != NT_STATUS_SUCCESS) {
3825331Samw 					syslog(LOG_DEBUG,
3835331Samw 					    "NetlogonLSAMonitor: enum "
3845331Samw 					    "trusted domain failed");
3855331Samw 				}
3865331Samw 			} else {
3875331Samw 				syslog(LOG_DEBUG,
3885331Samw 				    "NetlogonLSAMonitor: update failed");
3895331Samw 			}
3905331Samw 		}
3915331Samw 	}
3925331Samw 
3935331Samw 	/*NOTREACHED*/
3945331Samw 	return (NULL);
3955331Samw }
396