Print this page
NEX-15558 SMB logon fails during 1st second after service start
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15558 SMB logon fails during 1st second after service start
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-2667 Wrong error when join domain with wrong password
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
NEX-2225 Unable to join NexentaStor to 2008 AD
NEX-1638 Updated DC Locator
Includes work by: matt.barden@nexenta.com, kevin.crowe@nexenta.com
re #12435 rb3958 r10 is added 2 times to panic info
re #12393 rb3935 Kerberos and smbd disagree about who is our AD server
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c
+++ new/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_domain.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
|
↓ open down ↓ |
13 lines elided |
↑ open up ↑ |
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 - * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
24 + * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
25 25 */
26 26
27 27 #include <syslog.h>
28 28 #include <synch.h>
29 29 #include <pthread.h>
30 30 #include <unistd.h>
31 31 #include <string.h>
32 32 #include <strings.h>
33 33 #include <sys/errno.h>
34 34 #include <sys/types.h>
35 35 #include <netinet/in.h>
36 36 #include <arpa/nameser.h>
37 37 #include <resolv.h>
38 38 #include <netdb.h>
39 39 #include <assert.h>
40 40
41 41 #include <smbsrv/libsmb.h>
42 42 #include <smbsrv/libsmbns.h>
43 43 #include <smbsrv/libmlsvc.h>
44 44
45 45 #include <smbsrv/smbinfo.h>
46 46 #include <lsalib.h>
47 47 #include <mlsvc.h>
48 48
49 49 /*
50 50 * DC Locator
51 51 */
52 52 #define SMB_DCLOCATOR_TIMEOUT 45 /* seconds */
53 53 #define SMB_IS_FQDN(domain) (strchr(domain, '.') != NULL)
54 54
55 55 typedef struct smb_dclocator {
56 56 smb_dcinfo_t sdl_dci; /* .dc_name .dc_addr */
57 57 char sdl_domain[SMB_PI_MAX_DOMAIN];
58 58 boolean_t sdl_locate;
59 59 boolean_t sdl_bad_dc;
60 60 boolean_t sdl_cfg_chg;
61 61 mutex_t sdl_mtx;
62 62 cond_t sdl_cv;
63 63 uint32_t sdl_status;
64 64 } smb_dclocator_t;
65 65
66 66 static smb_dclocator_t smb_dclocator;
67 67 static pthread_t smb_dclocator_thr;
68 68
69 69 static void *smb_ddiscover_service(void *);
70 70 static uint32_t smb_ddiscover_qinfo(char *, char *, smb_domainex_t *);
71 71 static void smb_ddiscover_enum_trusted(char *, char *, smb_domainex_t *);
72 72 static uint32_t smb_ddiscover_use_config(char *, smb_domainex_t *);
73 73 static void smb_domainex_free(smb_domainex_t *);
74 74 static void smb_set_krb5_realm(char *);
75 75
76 76 /*
77 77 * ===================================================================
78 78 * API to initialize DC locator thread, trigger DC discovery, and
79 79 * get the discovered DC and/or domain information.
80 80 * ===================================================================
81 81 */
82 82
|
↓ open down ↓ |
48 lines elided |
↑ open up ↑ |
83 83 /*
84 84 * Initialization of the DC locator thread.
85 85 * Returns 0 on success, an error number if thread creation fails.
86 86 */
87 87 int
88 88 smb_dclocator_init(void)
89 89 {
90 90 pthread_attr_t tattr;
91 91 int rc;
92 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 +
93 101 (void) pthread_attr_init(&tattr);
94 102 (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
95 103 rc = pthread_create(&smb_dclocator_thr, &tattr,
96 104 smb_ddiscover_service, &smb_dclocator);
97 105 (void) pthread_attr_destroy(&tattr);
98 106 return (rc);
99 107 }
100 108
101 109 /*
102 110 * This is the entry point for discovering a domain controller for the
103 111 * specified domain. Called during join domain, and then periodically
104 112 * by smbd_dc_update (the "DC monitor" thread).
105 113 *
106 114 * The actual work of discovering a DC is handled by DC locator thread.
107 115 * All we do here is signal the request and wait for a DC or a timeout.
108 116 *
109 117 * Input parameters:
110 118 * domain - domain to be discovered (can either be NetBIOS or DNS domain)
111 119 *
112 120 * Output parameter:
113 121 * dp - on success, dp will be filled with the discovered DC and domain
114 122 * information.
115 123 *
116 124 * Returns B_TRUE if the DC/domain info is available.
117 125 */
118 126 boolean_t
119 127 smb_locate_dc(char *domain, smb_domainex_t *dp)
120 128 {
121 129 int rc;
122 130 boolean_t rv;
123 131 timestruc_t to;
124 132 smb_domainex_t domain_info;
125 133
126 134 if (domain == NULL || *domain == '\0') {
127 135 syslog(LOG_DEBUG, "smb_locate_dc NULL dom");
128 136 smb_set_krb5_realm(NULL);
129 137 return (B_FALSE);
130 138 }
131 139
132 140 (void) mutex_lock(&smb_dclocator.sdl_mtx);
133 141
134 142 if (strcmp(smb_dclocator.sdl_domain, domain)) {
135 143 (void) strlcpy(smb_dclocator.sdl_domain, domain,
136 144 sizeof (smb_dclocator.sdl_domain));
137 145 smb_dclocator.sdl_cfg_chg = B_TRUE;
138 146 syslog(LOG_DEBUG, "smb_locate_dc new dom=%s", domain);
139 147 smb_set_krb5_realm(domain);
140 148 }
141 149
142 150 if (!smb_dclocator.sdl_locate) {
143 151 smb_dclocator.sdl_locate = B_TRUE;
144 152 (void) cond_broadcast(&smb_dclocator.sdl_cv);
145 153 }
146 154
147 155 while (smb_dclocator.sdl_locate) {
148 156 to.tv_sec = SMB_DCLOCATOR_TIMEOUT;
149 157 to.tv_nsec = 0;
150 158 rc = cond_reltimedwait(&smb_dclocator.sdl_cv,
151 159 &smb_dclocator.sdl_mtx, &to);
152 160
153 161 if (rc == ETIME) {
154 162 syslog(LOG_NOTICE, "smb_locate_dc timeout");
155 163 rv = B_FALSE;
156 164 goto out;
157 165 }
158 166 }
159 167 if (smb_dclocator.sdl_status != 0) {
160 168 syslog(LOG_NOTICE, "smb_locate_dc status 0x%x",
161 169 smb_dclocator.sdl_status);
162 170 rv = B_FALSE;
163 171 goto out;
164 172 }
165 173
166 174 if (dp == NULL)
167 175 dp = &domain_info;
168 176 rv = smb_domain_getinfo(dp);
169 177
170 178 out:
171 179 (void) mutex_unlock(&smb_dclocator.sdl_mtx);
172 180
173 181 return (rv);
174 182 }
175 183
176 184 /*
177 185 * Tell the domain discovery service to run again now,
178 186 * and assume changed configuration (i.e. a new DC).
179 187 * Like the first part of smb_locate_dc().
180 188 *
181 189 * Note: This is called from the service refresh handler
182 190 * and the door handler to tell the ddiscover thread to
183 191 * request the new DC from idmap. Therefore, we must not
184 192 * trigger a new idmap discovery run from here, or that
185 193 * would start a ping-pong match.
186 194 */
187 195 /* ARGSUSED */
188 196 void
189 197 smb_ddiscover_refresh()
190 198 {
191 199
192 200 (void) mutex_lock(&smb_dclocator.sdl_mtx);
193 201
194 202 if (smb_dclocator.sdl_cfg_chg == B_FALSE) {
195 203 smb_dclocator.sdl_cfg_chg = B_TRUE;
196 204 syslog(LOG_DEBUG, "smb_ddiscover_refresh set cfg changed");
197 205 }
198 206 if (!smb_dclocator.sdl_locate) {
199 207 smb_dclocator.sdl_locate = B_TRUE;
200 208 (void) cond_broadcast(&smb_dclocator.sdl_cv);
201 209 }
202 210
203 211 (void) mutex_unlock(&smb_dclocator.sdl_mtx);
204 212 }
205 213
206 214 /*
207 215 * Called by our client-side threads after they fail to connect to
208 216 * the DC given to them by smb_locate_dc(). This is often called
209 217 * after some delay, because the connection timeout delays these
210 218 * threads for a while, so it's quite common that the DC locator
211 219 * service has already started looking for a new DC. These late
212 220 * notifications should not continually restart the DC locator.
213 221 */
214 222 void
215 223 smb_ddiscover_bad_dc(char *bad_dc)
216 224 {
217 225
218 226 assert(bad_dc[0] != '\0');
219 227
220 228 (void) mutex_lock(&smb_dclocator.sdl_mtx);
221 229
222 230 syslog(LOG_DEBUG, "smb_ddiscover_bad_dc, cur=%s, bad=%s",
223 231 smb_dclocator.sdl_dci.dc_name, bad_dc);
224 232
225 233 if (strcmp(smb_dclocator.sdl_dci.dc_name, bad_dc)) {
226 234 /*
227 235 * The "bad" DC is no longer the current one.
228 236 * Probably a late "bad DC" report.
229 237 */
230 238 goto out;
231 239 }
232 240 if (smb_dclocator.sdl_bad_dc) {
233 241 /* Someone already marked the current DC as "bad". */
|
↓ open down ↓ |
131 lines elided |
↑ open up ↑ |
234 242 syslog(LOG_DEBUG, "smb_ddiscover_bad_dc repeat");
235 243 goto out;
236 244 }
237 245
238 246 /*
239 247 * Mark the current DC as "bad" and let the DC Locator
240 248 * run again if it's not already.
241 249 */
242 250 syslog(LOG_INFO, "smb_ddiscover, bad DC: %s", bad_dc);
243 251 smb_dclocator.sdl_bad_dc = B_TRUE;
252 + smb_domain_bad_dc();
244 253
245 254 /* In-line smb_ddiscover_kick */
246 255 if (!smb_dclocator.sdl_locate) {
247 256 smb_dclocator.sdl_locate = B_TRUE;
248 257 (void) cond_broadcast(&smb_dclocator.sdl_cv);
249 258 }
250 259
251 260 out:
252 261 (void) mutex_unlock(&smb_dclocator.sdl_mtx);
253 262 }
254 263
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
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 265 /*
280 266 * ==========================================================
281 267 * DC discovery functions
282 268 * ==========================================================
283 269 */
284 270
285 271 /*
286 272 * This is the domain and DC discovery service: it gets woken up whenever
287 273 * there is need to locate a domain controller.
288 274 *
289 275 * Upon success, the SMB domain cache will be populated with the discovered
290 276 * DC and domain info.
291 277 */
292 278 /*ARGSUSED*/
293 279 static void *
294 280 smb_ddiscover_service(void *arg)
295 281 {
296 282 smb_domainex_t dxi;
297 283 smb_dclocator_t *sdl = arg;
298 284 uint32_t status;
299 285 boolean_t bad_dc;
300 286 boolean_t cfg_chg;
301 287
302 288 for (;;) {
303 289 /*
304 290 * Wait to be signaled for work by one of:
305 291 * smb_locate_dc(), smb_ddiscover_refresh(),
306 292 * smb_ddiscover_bad_dc()
307 293 */
308 294 syslog(LOG_DEBUG, "smb_ddiscover_service waiting");
309 295
310 296 (void) mutex_lock(&sdl->sdl_mtx);
311 297 while (!sdl->sdl_locate)
312 298 (void) cond_wait(&sdl->sdl_cv,
313 299 &sdl->sdl_mtx);
314 300
315 301 if (!smb_config_getbool(SMB_CI_DOMAIN_MEMB)) {
316 302 sdl->sdl_status = NT_STATUS_INVALID_SERVER_STATE;
317 303 syslog(LOG_DEBUG, "smb_ddiscover_service: "
318 304 "not a domain member");
319 305 goto wait_again;
320 306 }
321 307
322 308 /*
323 309 * Want to know if these change below.
324 310 * Note: mutex held here
325 311 */
326 312 find_again:
327 313 bad_dc = sdl->sdl_bad_dc;
328 314 sdl->sdl_bad_dc = B_FALSE;
329 315 if (bad_dc) {
330 316 /*
331 317 * Need to clear the current DC name or
332 318 * ddiscover_bad_dc will keep setting bad_dc
333 319 */
334 320 sdl->sdl_dci.dc_name[0] = '\0';
335 321 }
336 322 cfg_chg = sdl->sdl_cfg_chg;
337 323 sdl->sdl_cfg_chg = B_FALSE;
338 324
339 325 (void) mutex_unlock(&sdl->sdl_mtx);
340 326
341 327 syslog(LOG_DEBUG, "smb_ddiscover_service running "
342 328 "cfg_chg=%d bad_dc=%d", (int)cfg_chg, (int)bad_dc);
343 329
344 330 /*
345 331 * Clear the cached DC now so that we'll ask idmap again.
346 332 * If our current DC gave us errors, force rediscovery.
|
↓ open down ↓ |
58 lines elided |
↑ open up ↑ |
347 333 */
348 334 smb_ads_refresh(bad_dc);
349 335
350 336 /*
351 337 * Search for the DC, save the result.
352 338 */
353 339 bzero(&dxi, sizeof (dxi));
354 340 status = smb_ddiscover_main(sdl->sdl_domain, &dxi);
355 341 if (status == 0)
356 342 smb_domain_save();
343 +
357 344 (void) mutex_lock(&sdl->sdl_mtx);
345 +
358 346 sdl->sdl_status = status;
359 - if (status == 0)
347 + if (status == 0) {
360 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 + }
361 356
362 357 /*
363 358 * Run again if either of cfg_chg or bad_dc
364 359 * was turned on during smb_ddiscover_main().
365 360 * Note: mutex held here.
366 361 */
367 362 if (sdl->sdl_bad_dc) {
368 363 syslog(LOG_DEBUG, "smb_ddiscover_service "
369 364 "restart because bad_dc was set");
370 365 goto find_again;
371 366 }
372 367 if (sdl->sdl_cfg_chg) {
373 368 syslog(LOG_DEBUG, "smb_ddiscover_service "
374 369 "restart because cfg_chg was set");
375 370 goto find_again;
376 371 }
377 372
378 373 wait_again:
379 374 sdl->sdl_locate = B_FALSE;
380 375 sdl->sdl_bad_dc = B_FALSE;
381 376 sdl->sdl_cfg_chg = B_FALSE;
382 377 (void) cond_broadcast(&sdl->sdl_cv);
383 378 (void) mutex_unlock(&sdl->sdl_mtx);
384 379 }
385 380
386 381 /*NOTREACHED*/
387 382 return (NULL);
388 383 }
389 384
390 385 /*
391 386 * Discovers a domain controller for the specified domain via DNS.
392 387 * After the domain controller is discovered successfully primary and
393 388 * trusted domain infromation will be queried using RPC queries.
394 389 *
395 390 * Caller should zero out *dxi before calling, and after a
396 391 * successful return should call: smb_domain_save()
397 392 */
|
↓ open down ↓ |
27 lines elided |
↑ open up ↑ |
398 393 uint32_t
399 394 smb_ddiscover_main(char *domain, smb_domainex_t *dxi)
400 395 {
401 396 uint32_t status;
402 397
403 398 if (domain[0] == '\0') {
404 399 syslog(LOG_DEBUG, "smb_ddiscover_main NULL domain");
405 400 return (NT_STATUS_INTERNAL_ERROR);
406 401 }
407 402
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 403 status = smb_ads_lookup_msdcs(domain, &dxi->d_dci);
414 404 if (status != 0) {
415 405 syslog(LOG_DEBUG, "smb_ddiscover_main can't find DC (%s)",
416 406 xlate_nt_status(status));
417 407 goto out;
418 408 }
419 409
420 410 status = smb_ddiscover_qinfo(domain, dxi->d_dci.dc_name, dxi);
421 411 if (status != 0) {
422 412 syslog(LOG_DEBUG,
423 413 "smb_ddiscover_main can't get domain info (%s)",
424 414 xlate_nt_status(status));
425 415 goto out;
426 416 }
427 417
428 - smb_domain_update(dxi);
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 + }
429 425
430 426 out:
431 - smb_domain_end_update();
432 -
433 427 /* Don't need the trusted domain list anymore. */
434 428 smb_domainex_free(dxi);
435 429
436 430 return (status);
437 431 }
438 432
439 433 /*
440 434 * Obtain primary and trusted domain information using LSA queries.
441 435 *
442 436 * domain - either NetBIOS or fully-qualified domain name
443 437 */
444 438 static uint32_t
445 439 smb_ddiscover_qinfo(char *domain, char *server, smb_domainex_t *dxi)
446 440 {
447 441 uint32_t ret, tmp;
448 442
449 443 /* If we must return failure, use this first one. */
450 444 ret = lsa_query_dns_domain_info(server, domain, &dxi->d_primary);
451 445 if (ret == NT_STATUS_SUCCESS)
452 446 goto success;
453 447 tmp = smb_ddiscover_use_config(domain, dxi);
454 448 if (tmp == NT_STATUS_SUCCESS)
455 449 goto success;
456 450 tmp = lsa_query_primary_domain_info(server, domain, &dxi->d_primary);
457 451 if (tmp == NT_STATUS_SUCCESS)
458 452 goto success;
459 453
460 454 /* All of the above failed. */
461 455 return (ret);
462 456
463 457 success:
464 458 smb_ddiscover_enum_trusted(domain, server, dxi);
465 459 return (NT_STATUS_SUCCESS);
466 460 }
467 461
468 462 /*
469 463 * Obtain trusted domains information using LSA queries.
470 464 *
471 465 * domain - either NetBIOS or fully-qualified domain name.
472 466 */
473 467 static void
474 468 smb_ddiscover_enum_trusted(char *domain, char *server, smb_domainex_t *dxi)
475 469 {
476 470 smb_trusted_domains_t *list;
477 471 uint32_t status;
478 472
479 473 list = &dxi->d_trusted;
480 474 status = lsa_enum_trusted_domains_ex(server, domain, list);
481 475 if (status != NT_STATUS_SUCCESS)
482 476 (void) lsa_enum_trusted_domains(server, domain, list);
483 477 }
484 478
485 479 /*
486 480 * If the domain to be discovered matches the current domain (i.e the
487 481 * value of either domain or fqdn configuration), then get the primary
488 482 * domain information from SMF.
489 483 */
490 484 static uint32_t
491 485 smb_ddiscover_use_config(char *domain, smb_domainex_t *dxi)
492 486 {
493 487 boolean_t use;
494 488 smb_domain_t *dinfo;
495 489
496 490 dinfo = &dxi->d_primary;
497 491 bzero(dinfo, sizeof (smb_domain_t));
498 492
499 493 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
500 494 return (NT_STATUS_UNSUCCESSFUL);
501 495
502 496 smb_config_getdomaininfo(dinfo->di_nbname, dinfo->di_fqname,
503 497 NULL, NULL, NULL);
504 498
505 499 if (SMB_IS_FQDN(domain))
506 500 use = (smb_strcasecmp(dinfo->di_fqname, domain, 0) == 0);
507 501 else
508 502 use = (smb_strcasecmp(dinfo->di_nbname, domain, 0) == 0);
509 503
510 504 if (use)
511 505 smb_config_getdomaininfo(NULL, NULL, dinfo->di_sid,
512 506 dinfo->di_u.di_dns.ddi_forest,
513 507 dinfo->di_u.di_dns.ddi_guid);
514 508
515 509 return ((use) ? NT_STATUS_SUCCESS : NT_STATUS_UNSUCCESSFUL);
516 510 }
517 511
518 512 static void
519 513 smb_domainex_free(smb_domainex_t *dxi)
520 514 {
521 515 free(dxi->d_trusted.td_domains);
522 516 dxi->d_trusted.td_domains = NULL;
523 517 }
524 518
525 519 static void
526 520 smb_set_krb5_realm(char *domain)
527 521 {
528 522 static char realm[MAXHOSTNAMELEN];
529 523
530 524 if (domain == NULL || domain[0] == '\0') {
531 525 (void) unsetenv("KRB5_DEFAULT_REALM");
532 526 return;
533 527 }
534 528
535 529 /* In case krb5.conf is not configured, set the default realm. */
536 530 (void) strlcpy(realm, domain, sizeof (realm));
537 531 (void) smb_strupr(realm);
538 532
539 533 (void) setenv("KRB5_DEFAULT_REALM", realm, 1);
540 534 }
|
↓ open down ↓ |
98 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX