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 2014 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 <sys/errno.h>
34
35 #include <smbsrv/libsmb.h>
36 #include <smbsrv/libsmbns.h>
37 #include <smbsrv/libmlsvc.h>
38 #include <smbsrv/smbinfo.h>
39 #include "smbd.h"
40
41 #define SMBD_DC_MONITOR_ATTEMPTS 3
42 #define SMBD_DC_MONITOR_RETRY_INTERVAL 3 /* seconds */
43 #define SMBD_DC_MONITOR_INTERVAL 60 /* seconds */
44
45 extern smbd_t smbd;
46
47 static mutex_t smbd_dc_mutex;
48 static cond_t smbd_dc_cv;
49
50 static void *smbd_dc_monitor(void *);
51 static void smbd_dc_update(void);
52 /* Todo: static boolean_t smbd_set_netlogon_cred(void); */
53 static uint32_t smbd_join_workgroup(smb_joininfo_t *);
54 static uint32_t smbd_join_domain(smb_joininfo_t *);
55
56 /*
57 * Launch the DC discovery and monitor thread.
58 */
59 int
smbd_dc_monitor_init(void)60 smbd_dc_monitor_init(void)
61 {
62 pthread_attr_t attr;
63 int rc;
64
65 (void) smb_config_getstr(SMB_CI_ADS_SITE, smbd.s_site,
66 MAXHOSTNAMELEN);
67 (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &smbd.s_pdc);
68 smb_ads_init();
69
70 if (smbd.s_secmode != SMB_SECMODE_DOMAIN)
71 return (0);
72
73 (void) pthread_attr_init(&attr);
74 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
75 rc = pthread_create(&smbd.s_dc_monitor_tid, &attr, smbd_dc_monitor,
76 NULL);
77 (void) pthread_attr_destroy(&attr);
78 return (rc);
79 }
80
81 void
smbd_dc_monitor_refresh(void)82 smbd_dc_monitor_refresh(void)
83 {
84 char site[MAXHOSTNAMELEN];
85 smb_inaddr_t pdc;
86
87 site[0] = '\0';
88 bzero(&pdc, sizeof (smb_inaddr_t));
89 (void) smb_config_getstr(SMB_CI_ADS_SITE, site, MAXHOSTNAMELEN);
90 (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &pdc);
91
92 (void) mutex_lock(&smbd_dc_mutex);
93
94 if ((bcmp(&smbd.s_pdc, &pdc, sizeof (smb_inaddr_t)) != 0) ||
95 (smb_strcasecmp(smbd.s_site, site, 0) != 0)) {
96 bcopy(&pdc, &smbd.s_pdc, sizeof (smb_inaddr_t));
97 (void) strlcpy(smbd.s_site, site, MAXHOSTNAMELEN);
98 smbd.s_pdc_changed = B_TRUE;
99 (void) cond_signal(&smbd_dc_cv);
100 }
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 boolean_t ds_not_responding = B_FALSE;
110 boolean_t ds_cfg_changed = B_FALSE;
111 timestruc_t delay;
112 int i;
113
114 smbd_dc_update();
115 smbd_online_wait("smbd_dc_monitor");
116
117 while (smbd_online()) {
118 delay.tv_sec = SMBD_DC_MONITOR_INTERVAL;
119 delay.tv_nsec = 0;
120
121 (void) mutex_lock(&smbd_dc_mutex);
122 (void) cond_reltimedwait(&smbd_dc_cv, &smbd_dc_mutex, &delay);
123
124 if (smbd.s_pdc_changed) {
125 smbd.s_pdc_changed = B_FALSE;
126 ds_cfg_changed = B_TRUE;
127 }
128
129 (void) mutex_unlock(&smbd_dc_mutex);
130
131 for (i = 0; i < SMBD_DC_MONITOR_ATTEMPTS; ++i) {
132 if (dssetup_check_service() == 0) {
133 ds_not_responding = B_FALSE;
134 break;
135 }
136
137 ds_not_responding = B_TRUE;
138 (void) sleep(SMBD_DC_MONITOR_RETRY_INTERVAL);
139 }
140
141 if (ds_not_responding)
142 syslog(LOG_NOTICE,
143 "smbd_dc_monitor: domain service not responding");
144
145 if (ds_not_responding || ds_cfg_changed) {
146 ds_cfg_changed = B_FALSE;
147 smb_ads_refresh();
148 smbd_dc_update();
149 }
150 }
151
152 smbd.s_dc_monitor_tid = 0;
153 return (NULL);
154 }
155
156 /*
157 * Locate a domain controller in the current resource domain and Update
158 * the Netlogon credential chain.
159 *
160 * The domain configuration will be updated upon successful DC discovery.
161 */
162 static void
smbd_dc_update(void)163 smbd_dc_update(void)
164 {
165 char domain[MAXHOSTNAMELEN];
166 smb_domainex_t info;
167 smb_domain_t *di;
168 DWORD status;
169
170 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) {
171 (void) smb_getdomainname(domain, MAXHOSTNAMELEN);
172 (void) smb_strupr(domain);
173 }
174
175 if (!smb_locate_dc(domain, "", &info)) {
176 syslog(LOG_NOTICE,
177 "smbd_dc_update: %s: locate failed", domain);
178 return;
179 }
180
181 di = &info.d_primary;
182 syslog(LOG_INFO,
183 "smbd_dc_update: %s: located %s", domain, info.d_dc);
184
185 status = mlsvc_netlogon(info.d_dc, di->di_nbname);
186 if (status != NT_STATUS_SUCCESS) {
187 syslog(LOG_NOTICE,
188 "failed to establish NETLOGON credential chain");
189
190 /*
191 * Restart required because the domain changed
192 * or the credential chain setup failed.
193 */
194 syslog(LOG_NOTICE,
195 "smbd_dc_update: smb/server restart required");
196
197 if (smb_smf_restart_service() != 0)
198 syslog(LOG_ERR,
199 "restart failed: run 'svcs -xv smb/server'"
200 " for more information");
201 }
202 }
203
204 /*
205 * smbd_join
206 *
207 * Joins the specified domain/workgroup.
208 *
209 * If the security mode or domain name is being changed,
210 * the caller must restart the service.
211 */
212 uint32_t
smbd_join(smb_joininfo_t * info)213 smbd_join(smb_joininfo_t *info)
214 {
215 uint32_t status;
216
217 dssetup_clear_domain_info();
218 if (info->mode == SMB_SECMODE_WORKGRP)
219 status = smbd_join_workgroup(info);
220 else
221 status = smbd_join_domain(info);
222
223 return (status);
224 }
225
226 static uint32_t
smbd_join_workgroup(smb_joininfo_t * info)227 smbd_join_workgroup(smb_joininfo_t *info)
228 {
229 char nb_domain[SMB_PI_MAX_DOMAIN];
230
231 (void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain,
232 sizeof (nb_domain));
233
234 smbd_set_secmode(SMB_SECMODE_WORKGRP);
235 smb_config_setdomaininfo(info->domain_name, "", "", "", "");
236
237 if (strcasecmp(nb_domain, info->domain_name))
238 smb_browser_reconfig();
239
240 return (NT_STATUS_SUCCESS);
241 }
242
243 static uint32_t
smbd_join_domain(smb_joininfo_t * info)244 smbd_join_domain(smb_joininfo_t *info)
245 {
246 static unsigned char zero_hash[SMBAUTH_HASH_SZ];
247 smb_domainex_t dxi;
248 smb_domain_t *di;
249 uint32_t status;
250
251 /*
252 * Ensure that any previous membership of this domain has
253 * been cleared from the environment before we start. This
254 * will ensure that we don't attempt a NETLOGON_SAMLOGON
255 * when attempting to find the PDC.
256 */
257 (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE);
258
259 /* Clear DNS local (ADS) lookup cache too. */
260 smb_ads_refresh();
261
262 /*
263 * Use a NULL session while searching for a DC, and
264 * while getting information about the domain.
265 */
266 smb_ipc_set(MLSVC_ANON_USER, zero_hash);
267
268 if (!smb_locate_dc(info->domain_name, "", &dxi)) {
269 syslog(LOG_ERR, "smbd: failed locating "
270 "domain controller for %s",
271 info->domain_name);
272 status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
273 goto errout;
274 }
275
276 /* info->domain_name could either be NetBIOS domain name or FQDN */
277 status = mlsvc_join(&dxi, info->domain_username, info->domain_passwd);
278 if (status != NT_STATUS_SUCCESS) {
279 syslog(LOG_ERR, "smbd: failed joining %s (%s)",
280 info->domain_name, xlate_nt_status(status));
281 goto errout;
282 }
283
284 /*
285 * Success!
286 *
287 * Strange, mlsvc_join does some of the work to
288 * save the config, then the rest happens here.
289 * Todo: Do the config update all in one place.
290 */
291 di = &dxi.d_primary;
292 smbd_set_secmode(SMB_SECMODE_DOMAIN);
293 smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
294 di->di_sid,
295 di->di_u.di_dns.ddi_forest,
296 di->di_u.di_dns.ddi_guid);
297 smb_ipc_commit();
298 return (status);
299
300 errout:
301 smb_ipc_rollback();
302 return (status);
303 }
304