xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_join.c (revision dcd633c7)
1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21148c5f43SAlan Wright 
22da6c28aaSamw /*
23148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24b3700b07SGordon Ross  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
25*dcd633c7SMatt Barden  * Copyright 2023 RackTop Systems, Inc.
26da6c28aaSamw  */
27da6c28aaSamw 
28da6c28aaSamw #include <syslog.h>
29da6c28aaSamw #include <synch.h>
30da6c28aaSamw #include <pthread.h>
31da6c28aaSamw #include <unistd.h>
32da6c28aaSamw #include <string.h>
33da6c28aaSamw #include <strings.h>
34b3700b07SGordon Ross #include <errno.h>
35b3700b07SGordon Ross #include <netinet/in.h>
36b3700b07SGordon Ross #include <netinet/tcp.h>
37da6c28aaSamw 
38da6c28aaSamw #include <smbsrv/libsmb.h>
39da6c28aaSamw #include <smbsrv/libsmbns.h>
40da6c28aaSamw #include <smbsrv/libmlsvc.h>
41da6c28aaSamw #include <smbsrv/smbinfo.h>
428d7e4166Sjose borrego #include "smbd.h"
43faa1795aSjb 
441fdeec65Sjoyce mcintosh #define	SMBD_DC_MONITOR_ATTEMPTS		3
451fdeec65Sjoyce mcintosh #define	SMBD_DC_MONITOR_RETRY_INTERVAL		3	/* seconds */
461fdeec65Sjoyce mcintosh #define	SMBD_DC_MONITOR_INTERVAL		60	/* seconds */
47da6c28aaSamw 
481fdeec65Sjoyce mcintosh extern smbd_t smbd;
49da6c28aaSamw 
50fd9ee8b5Sjoyce mcintosh static mutex_t smbd_dc_mutex;
51fd9ee8b5Sjoyce mcintosh static cond_t smbd_dc_cv;
52fd9ee8b5Sjoyce mcintosh 
531fdeec65Sjoyce mcintosh static void *smbd_dc_monitor(void *);
541fdeec65Sjoyce mcintosh static void smbd_dc_update(void);
55b3700b07SGordon Ross static int smbd_dc_check(smb_domainex_t *);
561ed6b69aSGordon Ross /* Todo: static boolean_t smbd_set_netlogon_cred(void); */
57b3700b07SGordon Ross static void smbd_join_workgroup(smb_joininfo_t *, smb_joinres_t *);
58b3700b07SGordon Ross static void smbd_join_domain(smb_joininfo_t *, smb_joinres_t *);
59faa1795aSjb 
601fdeec65Sjoyce mcintosh /*
611fdeec65Sjoyce mcintosh  * Launch the DC discovery and monitor thread.
621fdeec65Sjoyce mcintosh  */
631fdeec65Sjoyce mcintosh int
smbd_dc_monitor_init(void)641fdeec65Sjoyce mcintosh smbd_dc_monitor_init(void)
651fdeec65Sjoyce mcintosh {
661fdeec65Sjoyce mcintosh 	pthread_attr_t	attr;
671fdeec65Sjoyce mcintosh 	int		rc;
681fdeec65Sjoyce mcintosh 
691fdeec65Sjoyce mcintosh 	smb_ads_init();
701fdeec65Sjoyce mcintosh 
711fdeec65Sjoyce mcintosh 	if (smbd.s_secmode != SMB_SECMODE_DOMAIN)
721fdeec65Sjoyce mcintosh 		return (0);
731fdeec65Sjoyce mcintosh 
741fdeec65Sjoyce mcintosh 	(void) pthread_attr_init(&attr);
751fdeec65Sjoyce mcintosh 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
761fdeec65Sjoyce mcintosh 	rc = pthread_create(&smbd.s_dc_monitor_tid, &attr, smbd_dc_monitor,
771fdeec65Sjoyce mcintosh 	    NULL);
781fdeec65Sjoyce mcintosh 	(void) pthread_attr_destroy(&attr);
791fdeec65Sjoyce mcintosh 	return (rc);
801fdeec65Sjoyce mcintosh }
811fdeec65Sjoyce mcintosh 
82b3700b07SGordon Ross /*
83b3700b07SGordon Ross  * Refresh the DC monitor.  Called from SMF refresh and when idmap
84b3700b07SGordon Ross  * finds a different DC from what we were using previously.
85b3700b07SGordon Ross  * Update our domain (and current DC) information.
86b3700b07SGordon Ross  */
87fd9ee8b5Sjoyce mcintosh void
smbd_dc_monitor_refresh(void)88fd9ee8b5Sjoyce mcintosh smbd_dc_monitor_refresh(void)
89fd9ee8b5Sjoyce mcintosh {
90fd9ee8b5Sjoyce mcintosh 
91b3700b07SGordon Ross 	syslog(LOG_INFO, "smbd_dc_monitor_refresh");
92b3700b07SGordon Ross 
93b3700b07SGordon Ross 	smb_ddiscover_refresh();
94fd9ee8b5Sjoyce mcintosh 
95fd9ee8b5Sjoyce mcintosh 	(void) mutex_lock(&smbd_dc_mutex);
96fd9ee8b5Sjoyce mcintosh 
97*dcd633c7SMatt Barden 	smbd.s_dc_changed = B_TRUE;
98b3700b07SGordon Ross 	(void) cond_signal(&smbd_dc_cv);
99fd9ee8b5Sjoyce mcintosh 
100fd9ee8b5Sjoyce mcintosh 	(void) mutex_unlock(&smbd_dc_mutex);
101fd9ee8b5Sjoyce mcintosh }
102fd9ee8b5Sjoyce mcintosh 
1031fdeec65Sjoyce mcintosh /*ARGSUSED*/
1041fdeec65Sjoyce mcintosh static void *
smbd_dc_monitor(void * arg)1051fdeec65Sjoyce mcintosh smbd_dc_monitor(void *arg)
1061fdeec65Sjoyce mcintosh {
107b3700b07SGordon Ross 	smb_domainex_t	di;
108b3700b07SGordon Ross 	boolean_t	ds_not_responding;
109b3700b07SGordon Ross 	boolean_t	ds_cfg_changed;
110fd9ee8b5Sjoyce mcintosh 	timestruc_t	delay;
1111fdeec65Sjoyce mcintosh 	int		i;
1121fdeec65Sjoyce mcintosh 
113b3700b07SGordon Ross 	/* Wait for smb_dclocator_init() to complete. */
1141fdeec65Sjoyce mcintosh 	smbd_online_wait("smbd_dc_monitor");
115b3700b07SGordon Ross 	smbd_dc_update();
1161fdeec65Sjoyce mcintosh 
1171fdeec65Sjoyce mcintosh 	while (smbd_online()) {
118b3700b07SGordon Ross 		ds_not_responding = B_FALSE;
119b3700b07SGordon Ross 		ds_cfg_changed = B_FALSE;
120fd9ee8b5Sjoyce mcintosh 		delay.tv_sec = SMBD_DC_MONITOR_INTERVAL;
121fd9ee8b5Sjoyce mcintosh 		delay.tv_nsec = 0;
122fd9ee8b5Sjoyce mcintosh 
123fd9ee8b5Sjoyce mcintosh 		(void) mutex_lock(&smbd_dc_mutex);
124fd9ee8b5Sjoyce mcintosh 		(void) cond_reltimedwait(&smbd_dc_cv, &smbd_dc_mutex, &delay);
125fd9ee8b5Sjoyce mcintosh 
126*dcd633c7SMatt Barden 		if (smbd.s_dc_changed) {
127*dcd633c7SMatt Barden 			smbd.s_dc_changed = B_FALSE;
128fd9ee8b5Sjoyce mcintosh 			ds_cfg_changed = B_TRUE;
129b3700b07SGordon Ross 			/* NB: smb_ddiscover_refresh was called. */
130fd9ee8b5Sjoyce mcintosh 		}
131fd9ee8b5Sjoyce mcintosh 
132fd9ee8b5Sjoyce mcintosh 		(void) mutex_unlock(&smbd_dc_mutex);
1331fdeec65Sjoyce mcintosh 
134b3700b07SGordon Ross 		if (ds_cfg_changed) {
135b3700b07SGordon Ross 			syslog(LOG_DEBUG, "smbd_dc_monitor: config changed");
136b3700b07SGordon Ross 			goto rediscover;
137b3700b07SGordon Ross 		}
138b3700b07SGordon Ross 
139b3700b07SGordon Ross 		if (!smb_domain_getinfo(&di)) {
140b3700b07SGordon Ross 			syslog(LOG_DEBUG, "smbd_dc_monitor: no domain info");
141b3700b07SGordon Ross 			goto rediscover;
142b3700b07SGordon Ross 		}
143b3700b07SGordon Ross 
144b3700b07SGordon Ross 		if (di.d_dci.dc_name[0] == '\0') {
145b3700b07SGordon Ross 			syslog(LOG_DEBUG, "smbd_dc_monitor: no DC name");
146b3700b07SGordon Ross 			goto rediscover;
147b3700b07SGordon Ross 		}
148b3700b07SGordon Ross 
1491fdeec65Sjoyce mcintosh 		for (i = 0; i < SMBD_DC_MONITOR_ATTEMPTS; ++i) {
150b3700b07SGordon Ross 			if (smbd_dc_check(&di) == 0) {
1511fdeec65Sjoyce mcintosh 				ds_not_responding = B_FALSE;
1521fdeec65Sjoyce mcintosh 				break;
1531fdeec65Sjoyce mcintosh 			}
1541fdeec65Sjoyce mcintosh 
1551fdeec65Sjoyce mcintosh 			ds_not_responding = B_TRUE;
1561fdeec65Sjoyce mcintosh 			(void) sleep(SMBD_DC_MONITOR_RETRY_INTERVAL);
1571fdeec65Sjoyce mcintosh 		}
1581fdeec65Sjoyce mcintosh 
159b3700b07SGordon Ross 		if (ds_not_responding) {
160b819cea2SGordon Ross 			syslog(LOG_NOTICE,
161b3700b07SGordon Ross 			    "smbd_dc_monitor: DC not responding: %s",
162b3700b07SGordon Ross 			    di.d_dci.dc_name);
163b3700b07SGordon Ross 			smb_ddiscover_bad_dc(di.d_dci.dc_name);
164b3700b07SGordon Ross 		}
1651fdeec65Sjoyce mcintosh 
166fd9ee8b5Sjoyce mcintosh 		if (ds_not_responding || ds_cfg_changed) {
167b3700b07SGordon Ross 		rediscover:
168b3700b07SGordon Ross 			/*
169b3700b07SGordon Ross 			 * An smb_ads_refresh will be done by the
170b3700b07SGordon Ross 			 * smb_ddiscover_service when necessary.
171b3700b07SGordon Ross 			 * Note: smbd_dc_monitor_refresh was already
172b3700b07SGordon Ross 			 * called if appropriate.
173b3700b07SGordon Ross 			 */
1741fdeec65Sjoyce mcintosh 			smbd_dc_update();
1751fdeec65Sjoyce mcintosh 		}
1761fdeec65Sjoyce mcintosh 	}
1771fdeec65Sjoyce mcintosh 
1781fdeec65Sjoyce mcintosh 	smbd.s_dc_monitor_tid = 0;
1791fdeec65Sjoyce mcintosh 	return (NULL);
1801fdeec65Sjoyce mcintosh }
1811fdeec65Sjoyce mcintosh 
182b3700b07SGordon Ross /*
183b3700b07SGordon Ross  * Simply attempt a connection to the DC.
184b3700b07SGordon Ross  */
185b3700b07SGordon Ross static int
smbd_dc_check(smb_domainex_t * di)186b3700b07SGordon Ross smbd_dc_check(smb_domainex_t *di)
187b3700b07SGordon Ross {
188d209bf0dSAndy Fiddaman 	struct sockaddr_storage sa;
189b3700b07SGordon Ross 	int salen = 0;
190b3700b07SGordon Ross 	int sock = -1;
191b3700b07SGordon Ross 	int tmo = 5 * 1000;	/* 5 sec. */
192b3700b07SGordon Ross 	int rc;
193b3700b07SGordon Ross 
194b3700b07SGordon Ross 	bzero(&sa, sizeof (sa));
195b3700b07SGordon Ross 	switch (di->d_dci.dc_addr.a_family) {
196b3700b07SGordon Ross 	case AF_INET: {
197b3700b07SGordon Ross 		struct sockaddr_in *sin = (void *)&sa;
198b3700b07SGordon Ross 		sin->sin_family = AF_INET;
199b3700b07SGordon Ross 		sin->sin_port = htons(IPPORT_SMB);
200b3700b07SGordon Ross 		sin->sin_addr.s_addr = di->d_dci.dc_addr.a_ipv4;
201b3700b07SGordon Ross 		salen = sizeof (*sin);
202b3700b07SGordon Ross 		break;
203b3700b07SGordon Ross 	}
204b3700b07SGordon Ross 	case AF_INET6: {
205b3700b07SGordon Ross 		struct sockaddr_in6 *sin6 = (void *)&sa;
206b3700b07SGordon Ross 		sin6->sin6_family = AF_INET6;
207b3700b07SGordon Ross 		sin6->sin6_port = htons(IPPORT_SMB);
208b3700b07SGordon Ross 		(void) memcpy(&sin6->sin6_addr,
209b3700b07SGordon Ross 		    &di->d_dci.dc_addr.a_ipv6,
210b3700b07SGordon Ross 		    sizeof (in6_addr_t));
211b3700b07SGordon Ross 		salen = sizeof (*sin6);
212b3700b07SGordon Ross 		break;
213b3700b07SGordon Ross 	}
214b3700b07SGordon Ross 	default:
215b3700b07SGordon Ross 		return (-1);
216b3700b07SGordon Ross 	}
217b3700b07SGordon Ross 
218b3700b07SGordon Ross 	sock = socket(di->d_dci.dc_addr.a_family, SOCK_STREAM, 0);
219b3700b07SGordon Ross 	if (sock < 0)
220b3700b07SGordon Ross 		return (errno);
221b3700b07SGordon Ross 	(void) setsockopt(sock, IPPROTO_TCP,
222b3700b07SGordon Ross 	    TCP_CONN_ABORT_THRESHOLD, &tmo, sizeof (tmo));
223b3700b07SGordon Ross 
224d209bf0dSAndy Fiddaman 	rc = connect(sock, (const struct sockaddr *)&sa, salen);
225b3700b07SGordon Ross 	if (rc < 0)
226b3700b07SGordon Ross 		rc = errno;
227b3700b07SGordon Ross 
228b3700b07SGordon Ross 	(void) close(sock);
229b3700b07SGordon Ross 	return (rc);
230b3700b07SGordon Ross }
231b3700b07SGordon Ross 
2321fdeec65Sjoyce mcintosh /*
2331fdeec65Sjoyce mcintosh  * Locate a domain controller in the current resource domain and Update
2341fdeec65Sjoyce mcintosh  * the Netlogon credential chain.
2351fdeec65Sjoyce mcintosh  *
2361fdeec65Sjoyce mcintosh  * The domain configuration will be updated upon successful DC discovery.
2371fdeec65Sjoyce mcintosh  */
2381fdeec65Sjoyce mcintosh static void
smbd_dc_update(void)2391fdeec65Sjoyce mcintosh smbd_dc_update(void)
2401fdeec65Sjoyce mcintosh {
2411fdeec65Sjoyce mcintosh 	char		domain[MAXHOSTNAMELEN];
2421fdeec65Sjoyce mcintosh 	smb_domainex_t	info;
2431ed6b69aSGordon Ross 	smb_domain_t	*di;
2441ed6b69aSGordon Ross 	DWORD		status;
2451fdeec65Sjoyce mcintosh 
246b3700b07SGordon Ross 	/*
247b3700b07SGordon Ross 	 * Don't want this active until we're a domain member.
248b3700b07SGordon Ross 	 */
249b3700b07SGordon Ross 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
250b3700b07SGordon Ross 		return;
251b3700b07SGordon Ross 
252b3700b07SGordon Ross 	if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0)
253b3700b07SGordon Ross 		return;
254b3700b07SGordon Ross 
255b3700b07SGordon Ross 	if (domain[0] == '\0') {
256b3700b07SGordon Ross 		syslog(LOG_NOTICE,
257b3700b07SGordon Ross 		    "smbd_dc_update: no domain name set");
258b3700b07SGordon Ross 		return;
2591fdeec65Sjoyce mcintosh 	}
2601fdeec65Sjoyce mcintosh 
261b3700b07SGordon Ross 	if (!smb_locate_dc(domain, &info)) {
262b819cea2SGordon Ross 		syslog(LOG_NOTICE,
2631fdeec65Sjoyce mcintosh 		    "smbd_dc_update: %s: locate failed", domain);
2641ed6b69aSGordon Ross 		return;
2651ed6b69aSGordon Ross 	}
2661fdeec65Sjoyce mcintosh 
2671ed6b69aSGordon Ross 	di = &info.d_primary;
268b819cea2SGordon Ross 	syslog(LOG_INFO,
269b3700b07SGordon Ross 	    "smbd_dc_update: %s: located %s", domain, info.d_dci.dc_name);
2701fdeec65Sjoyce mcintosh 
271b3700b07SGordon Ross 	status = mlsvc_netlogon(info.d_dci.dc_name, di->di_nbname);
2721ed6b69aSGordon Ross 	if (status != NT_STATUS_SUCCESS) {
2731ed6b69aSGordon Ross 		syslog(LOG_NOTICE,
2741ed6b69aSGordon Ross 		    "failed to establish NETLOGON credential chain");
275b3700b07SGordon Ross 		syslog(LOG_NOTICE, " with server %s for domain %s (%s)",
276b3700b07SGordon Ross 		    info.d_dci.dc_name, domain,
277b3700b07SGordon Ross 		    xlate_nt_status(status));
2781fdeec65Sjoyce mcintosh 	}
2791fdeec65Sjoyce mcintosh }
2801fdeec65Sjoyce mcintosh 
281da6c28aaSamw /*
282da6c28aaSamw  * smbd_join
283da6c28aaSamw  *
2848d7e4166Sjose borrego  * Joins the specified domain/workgroup.
2858d7e4166Sjose borrego  *
2868d7e4166Sjose borrego  * If the security mode or domain name is being changed,
2878d7e4166Sjose borrego  * the caller must restart the service.
288da6c28aaSamw  */
289b3700b07SGordon Ross void
smbd_join(smb_joininfo_t * info,smb_joinres_t * res)290b3700b07SGordon Ross smbd_join(smb_joininfo_t *info, smb_joinres_t *res)
291da6c28aaSamw {
2922c1b14e5Sjose borrego 	dssetup_clear_domain_info();
2938d7e4166Sjose borrego 	if (info->mode == SMB_SECMODE_WORKGRP)
294b3700b07SGordon Ross 		smbd_join_workgroup(info, res);
2958d7e4166Sjose borrego 	else
296b3700b07SGordon Ross 		smbd_join_domain(info, res);
297da6c28aaSamw }
298faa1795aSjb 
299b3700b07SGordon Ross static void
smbd_join_workgroup(smb_joininfo_t * info,smb_joinres_t * res)300b3700b07SGordon Ross smbd_join_workgroup(smb_joininfo_t *info, smb_joinres_t *res)
3018d7e4166Sjose borrego {
3028d7e4166Sjose borrego 	char nb_domain[SMB_PI_MAX_DOMAIN];
3038d7e4166Sjose borrego 
304b3700b07SGordon Ross 	syslog(LOG_DEBUG, "smbd: join workgroup: %s", info->domain_name);
305b3700b07SGordon Ross 
3068d7e4166Sjose borrego 	(void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain,
3078d7e4166Sjose borrego 	    sizeof (nb_domain));
3088d7e4166Sjose borrego 
3098d7e4166Sjose borrego 	smbd_set_secmode(SMB_SECMODE_WORKGRP);
31029bd2886SAlan Wright 	smb_config_setdomaininfo(info->domain_name, "", "", "", "");
311b3700b07SGordon Ross 	(void) smb_config_set_idmap_domain("");
312b3700b07SGordon Ross 	(void) smb_config_refresh_idmap();
3138d7e4166Sjose borrego 
3148d7e4166Sjose borrego 	if (strcasecmp(nb_domain, info->domain_name))
3158d7e4166Sjose borrego 		smb_browser_reconfig();
3168d7e4166Sjose borrego 
317b3700b07SGordon Ross 	res->status = NT_STATUS_SUCCESS;
3188d7e4166Sjose borrego }
3198d7e4166Sjose borrego 
320b3700b07SGordon Ross static void
smbd_join_domain(smb_joininfo_t * info,smb_joinres_t * res)321b3700b07SGordon Ross smbd_join_domain(smb_joininfo_t *info, smb_joinres_t *res)
3228d7e4166Sjose borrego {
3238d7e4166Sjose borrego 
324b3700b07SGordon Ross 	syslog(LOG_DEBUG, "smbd: join domain: %s", info->domain_name);
3258d7e4166Sjose borrego 
3268d7e4166Sjose borrego 	/* info->domain_name could either be NetBIOS domain name or FQDN */
327b3700b07SGordon Ross 	mlsvc_join(info, res);
328b3700b07SGordon Ross 	if (res->status == 0) {
329b3700b07SGordon Ross 		smbd_set_secmode(SMB_SECMODE_DOMAIN);
330b3700b07SGordon Ross 	} else {
3318d7e4166Sjose borrego 		syslog(LOG_ERR, "smbd: failed joining %s (%s)",
332b3700b07SGordon Ross 		    info->domain_name, xlate_nt_status(res->status));
3338d7e4166Sjose borrego 	}
334c8ec8eeaSjose borrego }
335