xref: /onnv-gate/usr/src/cmd/smbsrv/smbd/smbd_join.c (revision 5331:3047ad28a67b)
1*5331Samw /*
2*5331Samw  * CDDL HEADER START
3*5331Samw  *
4*5331Samw  * The contents of this file are subject to the terms of the
5*5331Samw  * Common Development and Distribution License (the "License").
6*5331Samw  * You may not use this file except in compliance with the License.
7*5331Samw  *
8*5331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5331Samw  * or http://www.opensolaris.org/os/licensing.
10*5331Samw  * See the License for the specific language governing permissions
11*5331Samw  * and limitations under the License.
12*5331Samw  *
13*5331Samw  * When distributing Covered Code, include this CDDL HEADER in each
14*5331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5331Samw  * If applicable, add the following below this CDDL HEADER, with the
16*5331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
17*5331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5331Samw  *
19*5331Samw  * CDDL HEADER END
20*5331Samw  */
21*5331Samw /*
22*5331Samw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*5331Samw  * Use is subject to license terms.
24*5331Samw  */
25*5331Samw 
26*5331Samw #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5331Samw 
28*5331Samw #include <syslog.h>
29*5331Samw #include <synch.h>
30*5331Samw #include <pthread.h>
31*5331Samw #include <unistd.h>
32*5331Samw #include <string.h>
33*5331Samw #include <strings.h>
34*5331Samw #include <sys/errno.h>
35*5331Samw 
36*5331Samw #include <smbsrv/libsmb.h>
37*5331Samw #include <smbsrv/libsmbrdr.h>
38*5331Samw #include <smbsrv/libsmbns.h>
39*5331Samw #include <smbsrv/libmlsvc.h>
40*5331Samw 
41*5331Samw #include <smbsrv/smbinfo.h>
42*5331Samw #include <smbsrv/ntstatus.h>
43*5331Samw #include <smbsrv/lsalib.h>
44*5331Samw 
45*5331Samw /*
46*5331Samw  * Local protocol flags used to indicate which version of the
47*5331Samw  * netlogon protocol to use when attempting to find the PDC.
48*5331Samw  */
49*5331Samw #define	NETLOGON_PROTO_NETLOGON			0x01
50*5331Samw #define	NETLOGON_PROTO_SAMLOGON			0x02
51*5331Samw 
52*5331Samw /*
53*5331Samw  * Maximum time to wait for a domain controller (30 seconds).
54*5331Samw  */
55*5331Samw #define	SMB_NETLOGON_TIMEOUT	30
56*5331Samw 
57*5331Samw /*
58*5331Samw  * Flags used in conjunction with the location and query condition
59*5331Samw  * variables.
60*5331Samw  */
61*5331Samw #define	SMB_NETLF_LOCATE_DC	0x00000001
62*5331Samw #define	SMB_NETLF_LSA_QUERY	0x00000002
63*5331Samw 
64*5331Samw typedef struct smb_netlogon_info {
65*5331Samw 	char snli_domain[SMB_PI_MAX_DOMAIN];
66*5331Samw 	unsigned snli_flags;
67*5331Samw 	mutex_t snli_locate_mtx;
68*5331Samw 	cond_t snli_locate_cv;
69*5331Samw 	mutex_t snli_query_mtx;
70*5331Samw 	cond_t snli_query_cv;
71*5331Samw 	uint32_t snli_status;
72*5331Samw } smb_netlogon_info_t;
73*5331Samw 
74*5331Samw static smb_netlogon_info_t smb_netlogon_info;
75*5331Samw 
76*5331Samw static pthread_t lsa_monitor_thr;
77*5331Samw static pthread_t dc_browser_thr;
78*5331Samw 
79*5331Samw static void *smb_netlogon_lsa_monitor(void *arg);
80*5331Samw static void *smb_netlogon_dc_browser(void *arg);
81*5331Samw 
82*5331Samw /*
83*5331Samw  * Inline convenience function to find out if the domain information is
84*5331Samw  * valid. The caller can decide whether or not to wait.
85*5331Samw  */
86*5331Samw static boolean_t
87*5331Samw smb_ntdomain_is_valid(uint32_t timeout)
88*5331Samw {
89*5331Samw 	smb_ntdomain_t *info;
90*5331Samw 
91*5331Samw 	if ((info = smb_getdomaininfo(timeout)) != 0) {
92*5331Samw 		if (info->ipaddr != 0)
93*5331Samw 			return (B_TRUE);
94*5331Samw 	}
95*5331Samw 
96*5331Samw 	return (B_FALSE);
97*5331Samw }
98*5331Samw 
99*5331Samw /*
100*5331Samw  * smbd_join
101*5331Samw  *
102*5331Samw  * Joins the specified domain/workgroup
103*5331Samw  */
104*5331Samw uint32_t
105*5331Samw smbd_join(smb_joininfo_t *info)
106*5331Samw {
107*5331Samw 	smb_ntdomain_t *pi;
108*5331Samw 	uint32_t status;
109*5331Samw 	unsigned char passwd_hash[SMBAUTH_HASH_SZ];
110*5331Samw 	char plain_passwd[PASS_LEN + 1];
111*5331Samw 	char plain_user[PASS_LEN + 1];
112*5331Samw 
113*5331Samw 	if (info->mode == SMB_SECMODE_WORKGRP) {
114*5331Samw 		smb_config_wrlock();
115*5331Samw 		(void) smb_config_set_secmode(info->mode);
116*5331Samw 		(void) smb_config_set(SMB_CI_DOMAIN_NAME, info->domain_name);
117*5331Samw 		smb_config_unlock();
118*5331Samw 		return (NT_STATUS_SUCCESS);
119*5331Samw 	}
120*5331Samw 
121*5331Samw 	/*
122*5331Samw 	 * Ensure that any previous membership of this domain has
123*5331Samw 	 * been cleared from the environment before we start. This
124*5331Samw 	 * will ensure that we don't attempt a NETLOGON_SAMLOGON
125*5331Samw 	 * when attempting to find the PDC.
126*5331Samw 	 */
127*5331Samw 	smb_set_domain_member(0);
128*5331Samw 
129*5331Samw 	(void) strcpy(plain_user, info->domain_username);
130*5331Samw 	(void) strcpy(plain_passwd, info->domain_passwd);
131*5331Samw 
132*5331Samw 	if (smb_auth_ntlm_hash(plain_passwd, passwd_hash) != SMBAUTH_SUCCESS) {
133*5331Samw 		status = NT_STATUS_INTERNAL_ERROR;
134*5331Samw 		syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'",
135*5331Samw 		    plain_user);
136*5331Samw 		return (status);
137*5331Samw 	}
138*5331Samw 
139*5331Samw 	smbrdr_ipc_set(plain_user, passwd_hash);
140*5331Samw 
141*5331Samw 	if (locate_resource_pdc(info->domain_name)) {
142*5331Samw 		if ((pi = smb_getdomaininfo(0)) == 0) {
143*5331Samw 			status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
144*5331Samw 			syslog(LOG_ERR, "smbd: could not get domain controller"
145*5331Samw 			    "information for '%s'", info->domain_name);
146*5331Samw 			return (status);
147*5331Samw 		}
148*5331Samw 
149*5331Samw 		/*
150*5331Samw 		 * Temporary delay before creating
151*5331Samw 		 * the workstation trust account.
152*5331Samw 		 */
153*5331Samw 		(void) sleep(2);
154*5331Samw 		status = mlsvc_validate_user(pi->server, pi->domain,
155*5331Samw 		    plain_user, plain_passwd);
156*5331Samw 
157*5331Samw 		if (status == NT_STATUS_SUCCESS) {
158*5331Samw 			smb_config_wrlock();
159*5331Samw 			(void) smb_config_set_secmode(SMB_SECMODE_DOMAIN);
160*5331Samw 			(void) smb_config_set(SMB_CI_DOMAIN_NAME,
161*5331Samw 			    info->domain_name);
162*5331Samw 			smb_config_unlock();
163*5331Samw 			smbrdr_ipc_commit();
164*5331Samw 			return (status);
165*5331Samw 		}
166*5331Samw 
167*5331Samw 		smbrdr_ipc_rollback();
168*5331Samw 		syslog(LOG_ERR, "smbd: failed joining %s (%s)",
169*5331Samw 		    info->domain_name, xlate_nt_status(status));
170*5331Samw 		return (status);
171*5331Samw 	}
172*5331Samw 
173*5331Samw 	smbrdr_ipc_rollback();
174*5331Samw 	syslog(LOG_ERR, "smbd: failed locating domain controller for %s",
175*5331Samw 	    info->domain_name);
176*5331Samw 	return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND);
177*5331Samw }
178*5331Samw 
179*5331Samw /*
180*5331Samw  * locate_resource_pdc
181*5331Samw  *
182*5331Samw  * This is the entry point for discovering a domain controller for the
183*5331Samw  * specified domain. The caller may block here for around 30 seconds if
184*5331Samw  * the system has to go to the network and find a domain controller.
185*5331Samw  * Sometime it would be good to change this to smb_locate_pdc and allow
186*5331Samw  * the caller to specify whether or not he wants to wait for a response.
187*5331Samw  *
188*5331Samw  * The actual work of discovering a DC is handled by other threads.
189*5331Samw  * All we do here is signal the request and wait for a DC or a timeout.
190*5331Samw  *
191*5331Samw  * Returns B_TRUE if a domain controller is available.
192*5331Samw  */
193*5331Samw boolean_t
194*5331Samw locate_resource_pdc(char *domain)
195*5331Samw {
196*5331Samw 	int rc;
197*5331Samw 	timestruc_t to;
198*5331Samw 
199*5331Samw 	if (domain == NULL || *domain == '\0')
200*5331Samw 		return (B_FALSE);
201*5331Samw 
202*5331Samw 	(void) mutex_lock(&smb_netlogon_info.snli_locate_mtx);
203*5331Samw 
204*5331Samw 	if ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 0) {
205*5331Samw 		smb_netlogon_info.snli_flags |= SMB_NETLF_LOCATE_DC;
206*5331Samw 		(void) strlcpy(smb_netlogon_info.snli_domain, domain,
207*5331Samw 		    SMB_PI_MAX_DOMAIN);
208*5331Samw 		(void) cond_broadcast(&smb_netlogon_info.snli_locate_cv);
209*5331Samw 	}
210*5331Samw 
211*5331Samw 	while (smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) {
212*5331Samw 		to.tv_sec = SMB_NETLOGON_TIMEOUT;
213*5331Samw 		to.tv_nsec = 0;
214*5331Samw 		rc = cond_reltimedwait(&smb_netlogon_info.snli_locate_cv,
215*5331Samw 		    &smb_netlogon_info.snli_locate_mtx, &to);
216*5331Samw 
217*5331Samw 		if (rc == ETIME)
218*5331Samw 			break;
219*5331Samw 	}
220*5331Samw 
221*5331Samw 	(void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx);
222*5331Samw 
223*5331Samw 	return (smb_ntdomain_is_valid(0));
224*5331Samw }
225*5331Samw 
226*5331Samw /*
227*5331Samw  * smb_netlogon_init
228*5331Samw  *
229*5331Samw  * Initialization of the DC browser and LSA monitor threads.
230*5331Samw  * Returns 0 on success, an error number if thread creation fails.
231*5331Samw  */
232*5331Samw int
233*5331Samw smb_netlogon_init(void)
234*5331Samw {
235*5331Samw 	pthread_attr_t tattr;
236*5331Samw 	int rc;
237*5331Samw 
238*5331Samw 	(void) pthread_attr_init(&tattr);
239*5331Samw 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
240*5331Samw 	rc = pthread_create(&lsa_monitor_thr, &tattr,
241*5331Samw 	    smb_netlogon_lsa_monitor, 0);
242*5331Samw 	if (rc != 0)
243*5331Samw 		goto nli_exit;
244*5331Samw 	rc = pthread_create(&dc_browser_thr, &tattr,
245*5331Samw 	    smb_netlogon_dc_browser, 0);
246*5331Samw 	if (rc != 0) {
247*5331Samw 		(void) pthread_cancel(lsa_monitor_thr);
248*5331Samw 		(void) pthread_join(lsa_monitor_thr, NULL);
249*5331Samw 	}
250*5331Samw 
251*5331Samw nli_exit:
252*5331Samw 	(void) pthread_attr_destroy(&tattr);
253*5331Samw 	return (rc);
254*5331Samw }
255*5331Samw 
256*5331Samw /*
257*5331Samw  * smb_netlogon_dc_browser
258*5331Samw  *
259*5331Samw  * This is the DC browser thread: it gets woken up whenever someone
260*5331Samw  * wants to locate a domain controller.
261*5331Samw  *
262*5331Samw  * With the introduction of Windows 2000, NetBIOS is no longer a
263*5331Samw  * requirement for NT domains. If NetBIOS has been disabled on the
264*5331Samw  * network there will be no browsers and we won't get any response
265*5331Samw  * to netlogon requests. So we try to find a DC controller via ADS
266*5331Samw  * first. If ADS is disabled or the DNS query fails, we drop back
267*5331Samw  * to the netlogon protocol.
268*5331Samw  *
269*5331Samw  * This function will block for up to 30 seconds waiting for the PDC
270*5331Samw  * to be discovered. Sometime it would be good to change this to
271*5331Samw  * smb_locate_pdc and allow the caller to specify whether or not he
272*5331Samw  * wants to wait for a response.
273*5331Samw  *
274*5331Samw  */
275*5331Samw /*ARGSUSED*/
276*5331Samw static void *
277*5331Samw smb_netlogon_dc_browser(void *arg)
278*5331Samw {
279*5331Samw 	boolean_t rc;
280*5331Samw 	char resource_domain[SMB_PI_MAX_DOMAIN];
281*5331Samw 	int net, smb_nc_cnt;
282*5331Samw 	int protocol;
283*5331Samw 
284*5331Samw 	for (;;) {
285*5331Samw 		(void) mutex_lock(&smb_netlogon_info.snli_locate_mtx);
286*5331Samw 
287*5331Samw 		while ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) ==
288*5331Samw 		    0) {
289*5331Samw 			(void) cond_wait(&smb_netlogon_info.snli_locate_cv,
290*5331Samw 			    &smb_netlogon_info.snli_locate_mtx);
291*5331Samw 		}
292*5331Samw 
293*5331Samw 		(void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx);
294*5331Samw 
295*5331Samw 		(void) strlcpy(resource_domain, smb_netlogon_info.snli_domain,
296*5331Samw 		    SMB_PI_MAX_DOMAIN);
297*5331Samw 
298*5331Samw 		smb_setdomaininfo(NULL, NULL, 0);
299*5331Samw 		if (msdcs_lookup_ads() == 0) {
300*5331Samw 			if (smb_is_domain_member())
301*5331Samw 				protocol = NETLOGON_PROTO_SAMLOGON;
302*5331Samw 			else
303*5331Samw 				protocol = NETLOGON_PROTO_NETLOGON;
304*5331Samw 
305*5331Samw 			smb_nc_cnt = smb_nic_get_num();
306*5331Samw 			for (net = 0; net < smb_nc_cnt; net++) {
307*5331Samw 				smb_netlogon_request(net, protocol,
308*5331Samw 				    resource_domain);
309*5331Samw 			}
310*5331Samw 		}
311*5331Samw 
312*5331Samw 		rc = smb_ntdomain_is_valid(SMB_NETLOGON_TIMEOUT);
313*5331Samw 
314*5331Samw 		(void) mutex_lock(&smb_netlogon_info.snli_locate_mtx);
315*5331Samw 		smb_netlogon_info.snli_flags &= ~SMB_NETLF_LOCATE_DC;
316*5331Samw 		(void) cond_broadcast(&smb_netlogon_info.snli_locate_cv);
317*5331Samw 		(void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx);
318*5331Samw 
319*5331Samw 		if (rc != B_TRUE) {
320*5331Samw 			/*
321*5331Samw 			 * Notify the LSA monitor to update the
322*5331Samw 			 * primary and trusted domain information.
323*5331Samw 			 */
324*5331Samw 			(void) mutex_lock(&smb_netlogon_info.snli_query_mtx);
325*5331Samw 			smb_netlogon_info.snli_flags |= SMB_NETLF_LSA_QUERY;
326*5331Samw 			(void) cond_broadcast(&smb_netlogon_info.snli_query_cv);
327*5331Samw 			(void) mutex_unlock(&smb_netlogon_info.snli_query_mtx);
328*5331Samw 		}
329*5331Samw 	}
330*5331Samw 
331*5331Samw 	/*NOTREACHED*/
332*5331Samw 	return (NULL);
333*5331Samw }
334*5331Samw 
335*5331Samw /*
336*5331Samw  * smb_netlogon_lsa_monitor
337*5331Samw  *
338*5331Samw  * This monitor should run as a separate thread. It waits on a condition
339*5331Samw  * variable until someone indicates that the LSA domain information needs
340*5331Samw  * to be refreshed. It then queries the DC for the NT domain information:
341*5331Samw  * primary, account and trusted domains. The condition variable should be
342*5331Samw  * signaled whenever a DC is selected.
343*5331Samw  *
344*5331Samw  * Note that the LSA query calls require the DC information and this task
345*5331Samw  * may end up blocked on the DC location protocol, which is why this
346*5331Samw  * monitor is run as a separate thread. This should only happen if the DC
347*5331Samw  * goes down immediately after we located it.
348*5331Samw  */
349*5331Samw /*ARGSUSED*/
350*5331Samw static void *
351*5331Samw smb_netlogon_lsa_monitor(void *arg)
352*5331Samw {
353*5331Samw 	uint32_t status;
354*5331Samw 
355*5331Samw 	for (;;) {
356*5331Samw 		(void) mutex_lock(&smb_netlogon_info.snli_query_mtx);
357*5331Samw 
358*5331Samw 		while ((smb_netlogon_info.snli_flags & SMB_NETLF_LSA_QUERY) ==
359*5331Samw 		    0) {
360*5331Samw 			(void) cond_wait(&smb_netlogon_info.snli_query_cv,
361*5331Samw 			    &smb_netlogon_info.snli_query_mtx);
362*5331Samw 		}
363*5331Samw 
364*5331Samw 		smb_netlogon_info.snli_flags &= ~SMB_NETLF_LSA_QUERY;
365*5331Samw 		(void) mutex_unlock(&smb_netlogon_info.snli_query_mtx);
366*5331Samw 
367*5331Samw 		/*
368*5331Samw 		 * Skip the LSA query if Authenticated IPC is supported
369*5331Samw 		 * and the credential is not yet set.
370*5331Samw 		 */
371*5331Samw 		if (smbrdr_ipc_skip_lsa_query() == 0) {
372*5331Samw 			status = lsa_query_primary_domain_info();
373*5331Samw 			if (status == NT_STATUS_SUCCESS) {
374*5331Samw 				if (lsa_query_account_domain_info()
375*5331Samw 				    != NT_STATUS_SUCCESS) {
376*5331Samw 					syslog(LOG_DEBUG,
377*5331Samw 					    "NetlogonLSAMonitor: query "
378*5331Samw 					    "account info failed");
379*5331Samw 				}
380*5331Samw 				if (lsa_enum_trusted_domains()
381*5331Samw 				    != NT_STATUS_SUCCESS) {
382*5331Samw 					syslog(LOG_DEBUG,
383*5331Samw 					    "NetlogonLSAMonitor: enum "
384*5331Samw 					    "trusted domain failed");
385*5331Samw 				}
386*5331Samw 			} else {
387*5331Samw 				syslog(LOG_DEBUG,
388*5331Samw 				    "NetlogonLSAMonitor: update failed");
389*5331Samw 			}
390*5331Samw 		}
391*5331Samw 	}
392*5331Samw 
393*5331Samw 	/*NOTREACHED*/
394*5331Samw 	return (NULL);
395*5331Samw }
396