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
smbd_dc_monitor_init(void)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
smbd_dc_monitor_refresh(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 *
smbd_dc_monitor(void * arg)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
smbd_dc_check(smb_domainex_t * di)188 smbd_dc_check(smb_domainex_t *di)
189 {
190 struct sockaddr 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, &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
smbd_dc_update(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
smbd_join(smb_joininfo_t * info,smb_joinres_t * res)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
smbd_join_workgroup(smb_joininfo_t * info,smb_joinres_t * res)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
smbd_join_domain(smb_joininfo_t * info,smb_joinres_t * res)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