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) 2008, 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 <sys/errno.h>
34 #include <sys/types.h>
35 #include <netinet/in.h>
36 #include <arpa/nameser.h>
37 #include <resolv.h>
38 #include <netdb.h>
39 #include <assert.h>
40
41 #include <smbsrv/libsmb.h>
42 #include <smbsrv/libsmbns.h>
43 #include <smbsrv/libmlsvc.h>
44
73 static void smb_domainex_free(smb_domainex_t *);
74 static void smb_set_krb5_realm(char *);
75
76 /*
77 * ===================================================================
78 * API to initialize DC locator thread, trigger DC discovery, and
79 * get the discovered DC and/or domain information.
80 * ===================================================================
81 */
82
83 /*
84 * Initialization of the DC locator thread.
85 * Returns 0 on success, an error number if thread creation fails.
86 */
87 int
88 smb_dclocator_init(void)
89 {
90 pthread_attr_t tattr;
91 int rc;
92
93 (void) pthread_attr_init(&tattr);
94 (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
95 rc = pthread_create(&smb_dclocator_thr, &tattr,
96 smb_ddiscover_service, &smb_dclocator);
97 (void) pthread_attr_destroy(&tattr);
98 return (rc);
99 }
100
101 /*
102 * This is the entry point for discovering a domain controller for the
103 * specified domain. Called during join domain, and then periodically
104 * by smbd_dc_update (the "DC monitor" thread).
105 *
106 * The actual work of discovering a DC is handled by DC locator thread.
107 * All we do here is signal the request and wait for a DC or a timeout.
108 *
109 * Input parameters:
110 * domain - domain to be discovered (can either be NetBIOS or DNS domain)
111 *
112 * Output parameter:
224
225 if (strcmp(smb_dclocator.sdl_dci.dc_name, bad_dc)) {
226 /*
227 * The "bad" DC is no longer the current one.
228 * Probably a late "bad DC" report.
229 */
230 goto out;
231 }
232 if (smb_dclocator.sdl_bad_dc) {
233 /* Someone already marked the current DC as "bad". */
234 syslog(LOG_DEBUG, "smb_ddiscover_bad_dc repeat");
235 goto out;
236 }
237
238 /*
239 * Mark the current DC as "bad" and let the DC Locator
240 * run again if it's not already.
241 */
242 syslog(LOG_INFO, "smb_ddiscover, bad DC: %s", bad_dc);
243 smb_dclocator.sdl_bad_dc = B_TRUE;
244
245 /* In-line smb_ddiscover_kick */
246 if (!smb_dclocator.sdl_locate) {
247 smb_dclocator.sdl_locate = B_TRUE;
248 (void) cond_broadcast(&smb_dclocator.sdl_cv);
249 }
250
251 out:
252 (void) mutex_unlock(&smb_dclocator.sdl_mtx);
253 }
254
255 /*
256 * If domain discovery is running, wait for it to finish.
257 */
258 int
259 smb_ddiscover_wait(void)
260 {
261 timestruc_t to;
262 int rc = 0;
263
264 (void) mutex_lock(&smb_dclocator.sdl_mtx);
265
266 if (smb_dclocator.sdl_locate) {
267 to.tv_sec = SMB_DCLOCATOR_TIMEOUT;
268 to.tv_nsec = 0;
269 rc = cond_reltimedwait(&smb_dclocator.sdl_cv,
270 &smb_dclocator.sdl_mtx, &to);
271 }
272
273 (void) mutex_unlock(&smb_dclocator.sdl_mtx);
274
275 return (rc);
276 }
277
278
279 /*
280 * ==========================================================
281 * DC discovery functions
282 * ==========================================================
283 */
284
285 /*
286 * This is the domain and DC discovery service: it gets woken up whenever
287 * there is need to locate a domain controller.
288 *
289 * Upon success, the SMB domain cache will be populated with the discovered
290 * DC and domain info.
291 */
292 /*ARGSUSED*/
293 static void *
294 smb_ddiscover_service(void *arg)
295 {
296 smb_domainex_t dxi;
297 smb_dclocator_t *sdl = arg;
298 uint32_t status;
337 sdl->sdl_cfg_chg = B_FALSE;
338
339 (void) mutex_unlock(&sdl->sdl_mtx);
340
341 syslog(LOG_DEBUG, "smb_ddiscover_service running "
342 "cfg_chg=%d bad_dc=%d", (int)cfg_chg, (int)bad_dc);
343
344 /*
345 * Clear the cached DC now so that we'll ask idmap again.
346 * If our current DC gave us errors, force rediscovery.
347 */
348 smb_ads_refresh(bad_dc);
349
350 /*
351 * Search for the DC, save the result.
352 */
353 bzero(&dxi, sizeof (dxi));
354 status = smb_ddiscover_main(sdl->sdl_domain, &dxi);
355 if (status == 0)
356 smb_domain_save();
357 (void) mutex_lock(&sdl->sdl_mtx);
358 sdl->sdl_status = status;
359 if (status == 0)
360 sdl->sdl_dci = dxi.d_dci;
361
362 /*
363 * Run again if either of cfg_chg or bad_dc
364 * was turned on during smb_ddiscover_main().
365 * Note: mutex held here.
366 */
367 if (sdl->sdl_bad_dc) {
368 syslog(LOG_DEBUG, "smb_ddiscover_service "
369 "restart because bad_dc was set");
370 goto find_again;
371 }
372 if (sdl->sdl_cfg_chg) {
373 syslog(LOG_DEBUG, "smb_ddiscover_service "
374 "restart because cfg_chg was set");
375 goto find_again;
376 }
377
378 wait_again:
379 sdl->sdl_locate = B_FALSE;
380 sdl->sdl_bad_dc = B_FALSE;
388 }
389
390 /*
391 * Discovers a domain controller for the specified domain via DNS.
392 * After the domain controller is discovered successfully primary and
393 * trusted domain infromation will be queried using RPC queries.
394 *
395 * Caller should zero out *dxi before calling, and after a
396 * successful return should call: smb_domain_save()
397 */
398 uint32_t
399 smb_ddiscover_main(char *domain, smb_domainex_t *dxi)
400 {
401 uint32_t status;
402
403 if (domain[0] == '\0') {
404 syslog(LOG_DEBUG, "smb_ddiscover_main NULL domain");
405 return (NT_STATUS_INTERNAL_ERROR);
406 }
407
408 if (smb_domain_start_update() != SMB_DOMAIN_SUCCESS) {
409 syslog(LOG_DEBUG, "smb_ddiscover_main can't get lock");
410 return (NT_STATUS_INTERNAL_ERROR);
411 }
412
413 status = smb_ads_lookup_msdcs(domain, &dxi->d_dci);
414 if (status != 0) {
415 syslog(LOG_DEBUG, "smb_ddiscover_main can't find DC (%s)",
416 xlate_nt_status(status));
417 goto out;
418 }
419
420 status = smb_ddiscover_qinfo(domain, dxi->d_dci.dc_name, dxi);
421 if (status != 0) {
422 syslog(LOG_DEBUG,
423 "smb_ddiscover_main can't get domain info (%s)",
424 xlate_nt_status(status));
425 goto out;
426 }
427
428 smb_domain_update(dxi);
429
430 out:
431 smb_domain_end_update();
432
433 /* Don't need the trusted domain list anymore. */
434 smb_domainex_free(dxi);
435
436 return (status);
437 }
438
439 /*
440 * Obtain primary and trusted domain information using LSA queries.
441 *
442 * domain - either NetBIOS or fully-qualified domain name
443 */
444 static uint32_t
445 smb_ddiscover_qinfo(char *domain, char *server, smb_domainex_t *dxi)
446 {
447 uint32_t ret, tmp;
448
449 /* If we must return failure, use this first one. */
450 ret = lsa_query_dns_domain_info(server, domain, &dxi->d_primary);
451 if (ret == NT_STATUS_SUCCESS)
452 goto success;
|
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) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2017 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 #include <sys/types.h>
35 #include <netinet/in.h>
36 #include <arpa/nameser.h>
37 #include <resolv.h>
38 #include <netdb.h>
39 #include <assert.h>
40
41 #include <smbsrv/libsmb.h>
42 #include <smbsrv/libsmbns.h>
43 #include <smbsrv/libmlsvc.h>
44
73 static void smb_domainex_free(smb_domainex_t *);
74 static void smb_set_krb5_realm(char *);
75
76 /*
77 * ===================================================================
78 * API to initialize DC locator thread, trigger DC discovery, and
79 * get the discovered DC and/or domain information.
80 * ===================================================================
81 */
82
83 /*
84 * Initialization of the DC locator thread.
85 * Returns 0 on success, an error number if thread creation fails.
86 */
87 int
88 smb_dclocator_init(void)
89 {
90 pthread_attr_t tattr;
91 int rc;
92
93 /*
94 * We need the smb_ddiscover_service to run on startup,
95 * so it will enter smb_ddiscover_main() and put the
96 * SMB "domain cache" into "updating" state so clients
97 * trying to logon will wait while we're finding a DC.
98 */
99 smb_dclocator.sdl_locate = B_TRUE;
100
101 (void) pthread_attr_init(&tattr);
102 (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
103 rc = pthread_create(&smb_dclocator_thr, &tattr,
104 smb_ddiscover_service, &smb_dclocator);
105 (void) pthread_attr_destroy(&tattr);
106 return (rc);
107 }
108
109 /*
110 * This is the entry point for discovering a domain controller for the
111 * specified domain. Called during join domain, and then periodically
112 * by smbd_dc_update (the "DC monitor" thread).
113 *
114 * The actual work of discovering a DC is handled by DC locator thread.
115 * All we do here is signal the request and wait for a DC or a timeout.
116 *
117 * Input parameters:
118 * domain - domain to be discovered (can either be NetBIOS or DNS domain)
119 *
120 * Output parameter:
232
233 if (strcmp(smb_dclocator.sdl_dci.dc_name, bad_dc)) {
234 /*
235 * The "bad" DC is no longer the current one.
236 * Probably a late "bad DC" report.
237 */
238 goto out;
239 }
240 if (smb_dclocator.sdl_bad_dc) {
241 /* Someone already marked the current DC as "bad". */
242 syslog(LOG_DEBUG, "smb_ddiscover_bad_dc repeat");
243 goto out;
244 }
245
246 /*
247 * Mark the current DC as "bad" and let the DC Locator
248 * run again if it's not already.
249 */
250 syslog(LOG_INFO, "smb_ddiscover, bad DC: %s", bad_dc);
251 smb_dclocator.sdl_bad_dc = B_TRUE;
252 smb_domain_bad_dc();
253
254 /* In-line smb_ddiscover_kick */
255 if (!smb_dclocator.sdl_locate) {
256 smb_dclocator.sdl_locate = B_TRUE;
257 (void) cond_broadcast(&smb_dclocator.sdl_cv);
258 }
259
260 out:
261 (void) mutex_unlock(&smb_dclocator.sdl_mtx);
262 }
263
264
265 /*
266 * ==========================================================
267 * DC discovery functions
268 * ==========================================================
269 */
270
271 /*
272 * This is the domain and DC discovery service: it gets woken up whenever
273 * there is need to locate a domain controller.
274 *
275 * Upon success, the SMB domain cache will be populated with the discovered
276 * DC and domain info.
277 */
278 /*ARGSUSED*/
279 static void *
280 smb_ddiscover_service(void *arg)
281 {
282 smb_domainex_t dxi;
283 smb_dclocator_t *sdl = arg;
284 uint32_t status;
323 sdl->sdl_cfg_chg = B_FALSE;
324
325 (void) mutex_unlock(&sdl->sdl_mtx);
326
327 syslog(LOG_DEBUG, "smb_ddiscover_service running "
328 "cfg_chg=%d bad_dc=%d", (int)cfg_chg, (int)bad_dc);
329
330 /*
331 * Clear the cached DC now so that we'll ask idmap again.
332 * If our current DC gave us errors, force rediscovery.
333 */
334 smb_ads_refresh(bad_dc);
335
336 /*
337 * Search for the DC, save the result.
338 */
339 bzero(&dxi, sizeof (dxi));
340 status = smb_ddiscover_main(sdl->sdl_domain, &dxi);
341 if (status == 0)
342 smb_domain_save();
343
344 (void) mutex_lock(&sdl->sdl_mtx);
345
346 sdl->sdl_status = status;
347 if (status == 0) {
348 sdl->sdl_dci = dxi.d_dci;
349 } else {
350 syslog(LOG_DEBUG, "smb_ddiscover_service "
351 "retry after STATUS_%s",
352 xlate_nt_status(status));
353 (void) sleep(5);
354 goto find_again;
355 }
356
357 /*
358 * Run again if either of cfg_chg or bad_dc
359 * was turned on during smb_ddiscover_main().
360 * Note: mutex held here.
361 */
362 if (sdl->sdl_bad_dc) {
363 syslog(LOG_DEBUG, "smb_ddiscover_service "
364 "restart because bad_dc was set");
365 goto find_again;
366 }
367 if (sdl->sdl_cfg_chg) {
368 syslog(LOG_DEBUG, "smb_ddiscover_service "
369 "restart because cfg_chg was set");
370 goto find_again;
371 }
372
373 wait_again:
374 sdl->sdl_locate = B_FALSE;
375 sdl->sdl_bad_dc = B_FALSE;
383 }
384
385 /*
386 * Discovers a domain controller for the specified domain via DNS.
387 * After the domain controller is discovered successfully primary and
388 * trusted domain infromation will be queried using RPC queries.
389 *
390 * Caller should zero out *dxi before calling, and after a
391 * successful return should call: smb_domain_save()
392 */
393 uint32_t
394 smb_ddiscover_main(char *domain, smb_domainex_t *dxi)
395 {
396 uint32_t status;
397
398 if (domain[0] == '\0') {
399 syslog(LOG_DEBUG, "smb_ddiscover_main NULL domain");
400 return (NT_STATUS_INTERNAL_ERROR);
401 }
402
403 status = smb_ads_lookup_msdcs(domain, &dxi->d_dci);
404 if (status != 0) {
405 syslog(LOG_DEBUG, "smb_ddiscover_main can't find DC (%s)",
406 xlate_nt_status(status));
407 goto out;
408 }
409
410 status = smb_ddiscover_qinfo(domain, dxi->d_dci.dc_name, dxi);
411 if (status != 0) {
412 syslog(LOG_DEBUG,
413 "smb_ddiscover_main can't get domain info (%s)",
414 xlate_nt_status(status));
415 goto out;
416 }
417
418 if (smb_domain_start_update() != SMB_DOMAIN_SUCCESS) {
419 syslog(LOG_DEBUG, "smb_ddiscover_main can't get lock");
420 status = NT_STATUS_INTERNAL_ERROR;
421 } else {
422 smb_domain_update(dxi);
423 smb_domain_end_update();
424 }
425
426 out:
427 /* Don't need the trusted domain list anymore. */
428 smb_domainex_free(dxi);
429
430 return (status);
431 }
432
433 /*
434 * Obtain primary and trusted domain information using LSA queries.
435 *
436 * domain - either NetBIOS or fully-qualified domain name
437 */
438 static uint32_t
439 smb_ddiscover_qinfo(char *domain, char *server, smb_domainex_t *dxi)
440 {
441 uint32_t ret, tmp;
442
443 /* If we must return failure, use this first one. */
444 ret = lsa_query_dns_domain_info(server, domain, &dxi->d_primary);
445 if (ret == NT_STATUS_SUCCESS)
446 goto success;
|