xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_join.c (revision 6dde88b51419b99fe0aab8e56184c693945826b8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
25  * Copyright 2023 RackTop Systems, Inc.
26  */
27 
28 #include <syslog.h>
29 #include <synch.h>
30 #include <pthread.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <errno.h>
35 #include <netinet/in.h>
36 #include <netinet/tcp.h>
37 
38 #include <smbsrv/libsmb.h>
39 #include <smbsrv/libsmbns.h>
40 #include <smbsrv/libmlsvc.h>
41 #include <smbsrv/smbinfo.h>
42 #include "smbd.h"
43 
44 #define	SMBD_DC_MONITOR_ATTEMPTS		3
45 #define	SMBD_DC_MONITOR_RETRY_INTERVAL		3	/* seconds */
46 #define	SMBD_DC_MONITOR_INTERVAL		60	/* seconds */
47 
48 extern smbd_t smbd;
49 
50 static mutex_t smbd_dc_mutex;
51 static cond_t smbd_dc_cv;
52 
53 static void *smbd_dc_monitor(void *);
54 static void smbd_dc_update(void);
55 static int smbd_dc_check(smb_domainex_t *);
56 /* Todo: static boolean_t smbd_set_netlogon_cred(void); */
57 static void smbd_join_workgroup(smb_joininfo_t *, smb_joinres_t *);
58 static void smbd_join_domain(smb_joininfo_t *, smb_joinres_t *);
59 
60 /*
61  * Launch the DC discovery and monitor thread.
62  */
63 int
64 smbd_dc_monitor_init(void)
65 {
66 	pthread_attr_t	attr;
67 	int		rc;
68 
69 	smb_ads_init();
70 
71 	if (smbd.s_secmode != SMB_SECMODE_DOMAIN)
72 		return (0);
73 
74 	(void) pthread_attr_init(&attr);
75 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
76 	rc = pthread_create(&smbd.s_dc_monitor_tid, &attr, smbd_dc_monitor,
77 	    NULL);
78 	(void) pthread_attr_destroy(&attr);
79 	return (rc);
80 }
81 
82 /*
83  * Refresh the DC monitor.  Called from SMF refresh and when idmap
84  * finds a different DC from what we were using previously.
85  * Update our domain (and current DC) information.
86  */
87 void
88 smbd_dc_monitor_refresh(void)
89 {
90 
91 	syslog(LOG_INFO, "smbd_dc_monitor_refresh");
92 
93 	smb_ddiscover_refresh();
94 
95 	(void) mutex_lock(&smbd_dc_mutex);
96 
97 	smbd.s_dc_changed = B_TRUE;
98 	(void) cond_signal(&smbd_dc_cv);
99 
100 	(void) mutex_unlock(&smbd_dc_mutex);
101 }
102 
103 /*ARGSUSED*/
104 static void *
105 smbd_dc_monitor(void *arg)
106 {
107 	smb_domainex_t	di;
108 	boolean_t	ds_not_responding;
109 	boolean_t	ds_cfg_changed;
110 	timestruc_t	delay;
111 	int		i;
112 
113 	/* Wait for smb_dclocator_init() to complete. */
114 	smbd_online_wait("smbd_dc_monitor");
115 	smbd_dc_update();
116 
117 	while (smbd_online()) {
118 		ds_not_responding = B_FALSE;
119 		ds_cfg_changed = B_FALSE;
120 		delay.tv_sec = SMBD_DC_MONITOR_INTERVAL;
121 		delay.tv_nsec = 0;
122 
123 		(void) mutex_lock(&smbd_dc_mutex);
124 		(void) cond_reltimedwait(&smbd_dc_cv, &smbd_dc_mutex, &delay);
125 
126 		if (smbd.s_dc_changed) {
127 			smbd.s_dc_changed = B_FALSE;
128 			ds_cfg_changed = B_TRUE;
129 			/* NB: smb_ddiscover_refresh was called. */
130 		}
131 
132 		(void) mutex_unlock(&smbd_dc_mutex);
133 
134 		if (ds_cfg_changed) {
135 			syslog(LOG_DEBUG, "smbd_dc_monitor: config changed");
136 			goto rediscover;
137 		}
138 
139 		if (!smb_domain_getinfo(&di)) {
140 			syslog(LOG_DEBUG, "smbd_dc_monitor: no domain info");
141 			goto rediscover;
142 		}
143 
144 		if (di.d_dci.dc_name[0] == '\0') {
145 			syslog(LOG_DEBUG, "smbd_dc_monitor: no DC name");
146 			goto rediscover;
147 		}
148 
149 		for (i = 0; i < SMBD_DC_MONITOR_ATTEMPTS; ++i) {
150 			if (smbd_dc_check(&di) == 0) {
151 				ds_not_responding = B_FALSE;
152 				break;
153 			}
154 
155 			ds_not_responding = B_TRUE;
156 			(void) sleep(SMBD_DC_MONITOR_RETRY_INTERVAL);
157 		}
158 
159 		if (ds_not_responding) {
160 			syslog(LOG_NOTICE,
161 			    "smbd_dc_monitor: DC not responding: %s",
162 			    di.d_dci.dc_name);
163 			smb_ddiscover_bad_dc(di.d_dci.dc_name);
164 		}
165 
166 		if (ds_not_responding || ds_cfg_changed) {
167 		rediscover:
168 			/*
169 			 * An smb_ads_refresh will be done by the
170 			 * smb_ddiscover_service when necessary.
171 			 * Note: smbd_dc_monitor_refresh was already
172 			 * called if appropriate.
173 			 */
174 			smbd_dc_update();
175 		}
176 	}
177 
178 	smbd.s_dc_monitor_tid = 0;
179 	return (NULL);
180 }
181 
182 /*
183  * Simply attempt a connection to the DC.
184  */
185 static int
186 smbd_dc_check(smb_domainex_t *di)
187 {
188 	struct sockaddr_storage sa;
189 	int salen = 0;
190 	int sock = -1;
191 	int tmo = 5 * 1000;	/* 5 sec. */
192 	int rc;
193 
194 	bzero(&sa, sizeof (sa));
195 	switch (di->d_dci.dc_addr.a_family) {
196 	case AF_INET: {
197 		struct sockaddr_in *sin = (void *)&sa;
198 		sin->sin_family = AF_INET;
199 		sin->sin_port = htons(IPPORT_SMB);
200 		sin->sin_addr.s_addr = di->d_dci.dc_addr.a_ipv4;
201 		salen = sizeof (*sin);
202 		break;
203 	}
204 	case AF_INET6: {
205 		struct sockaddr_in6 *sin6 = (void *)&sa;
206 		sin6->sin6_family = AF_INET6;
207 		sin6->sin6_port = htons(IPPORT_SMB);
208 		(void) memcpy(&sin6->sin6_addr,
209 		    &di->d_dci.dc_addr.a_ipv6,
210 		    sizeof (in6_addr_t));
211 		salen = sizeof (*sin6);
212 		break;
213 	}
214 	default:
215 		return (-1);
216 	}
217 
218 	sock = socket(di->d_dci.dc_addr.a_family, SOCK_STREAM, 0);
219 	if (sock < 0)
220 		return (errno);
221 	(void) setsockopt(sock, IPPROTO_TCP,
222 	    TCP_CONN_ABORT_THRESHOLD, &tmo, sizeof (tmo));
223 
224 	rc = connect(sock, (const struct sockaddr *)&sa, salen);
225 	if (rc < 0)
226 		rc = errno;
227 
228 	(void) close(sock);
229 	return (rc);
230 }
231 
232 /*
233  * Locate a domain controller in the current resource domain and Update
234  * the Netlogon credential chain.
235  *
236  * The domain configuration will be updated upon successful DC discovery.
237  */
238 static void
239 smbd_dc_update(void)
240 {
241 	char		domain[MAXHOSTNAMELEN];
242 	smb_domainex_t	info;
243 	smb_domain_t	*di;
244 	DWORD		status;
245 
246 	/*
247 	 * Don't want this active until we're a domain member.
248 	 */
249 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
250 		return;
251 
252 	if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0)
253 		return;
254 
255 	if (domain[0] == '\0') {
256 		syslog(LOG_NOTICE,
257 		    "smbd_dc_update: no domain name set");
258 		return;
259 	}
260 
261 	if (!smb_locate_dc(domain, &info)) {
262 		syslog(LOG_NOTICE,
263 		    "smbd_dc_update: %s: locate failed", domain);
264 		return;
265 	}
266 
267 	di = &info.d_primary;
268 	syslog(LOG_INFO,
269 	    "smbd_dc_update: %s: located %s", domain, info.d_dci.dc_name);
270 
271 	status = mlsvc_netlogon(info.d_dci.dc_name, di->di_nbname);
272 	if (status != NT_STATUS_SUCCESS) {
273 		syslog(LOG_NOTICE,
274 		    "failed to establish NETLOGON credential chain");
275 		syslog(LOG_NOTICE, " with server %s for domain %s (%s)",
276 		    info.d_dci.dc_name, domain,
277 		    xlate_nt_status(status));
278 	}
279 }
280 
281 /*
282  * smbd_join
283  *
284  * Joins the specified domain/workgroup.
285  *
286  * If the security mode or domain name is being changed,
287  * the caller must restart the service.
288  */
289 void
290 smbd_join(smb_joininfo_t *info, smb_joinres_t *res)
291 {
292 	dssetup_clear_domain_info();
293 	if (info->mode == SMB_SECMODE_WORKGRP)
294 		smbd_join_workgroup(info, res);
295 	else
296 		smbd_join_domain(info, res);
297 }
298 
299 static void
300 smbd_join_workgroup(smb_joininfo_t *info, smb_joinres_t *res)
301 {
302 	char nb_domain[SMB_PI_MAX_DOMAIN];
303 
304 	syslog(LOG_DEBUG, "smbd: join workgroup: %s", info->domain_name);
305 
306 	(void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain,
307 	    sizeof (nb_domain));
308 
309 	smbd_set_secmode(SMB_SECMODE_WORKGRP);
310 	smb_config_setdomaininfo(info->domain_name, "", "", "", "");
311 	(void) smb_config_set_idmap_domain("");
312 	(void) smb_config_refresh_idmap();
313 
314 	if (strcasecmp(nb_domain, info->domain_name))
315 		smb_browser_reconfig();
316 
317 	res->status = NT_STATUS_SUCCESS;
318 }
319 
320 static void
321 smbd_join_domain(smb_joininfo_t *info, smb_joinres_t *res)
322 {
323 
324 	syslog(LOG_DEBUG, "smbd: join domain: %s", info->domain_name);
325 
326 	/* info->domain_name could either be NetBIOS domain name or FQDN */
327 	mlsvc_join(info, res);
328 	if (res->status == 0) {
329 		smbd_set_secmode(SMB_SECMODE_DOMAIN);
330 	} else {
331 		syslog(LOG_ERR, "smbd: failed joining %s (%s)",
332 		    info->domain_name, xlate_nt_status(res->status));
333 	}
334 }
335