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