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