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