WHATSNEW: Update changes since 3.3.0pre1 and release date.
[Samba.git] / source / winbindd / winbindd_util.c
blob7847d1d315812c80d6021253a5f491ec03c92b9d
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000-2001
7 Copyright (C) 2001 by Martin Pool <mbp@samba.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "winbindd.h"
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_WINBIND
29 extern struct winbindd_methods cache_methods;
30 extern struct winbindd_methods builtin_passdb_methods;
31 extern struct winbindd_methods sam_passdb_methods;
34 /**
35 * @file winbindd_util.c
37 * Winbind daemon for NT domain authentication nss module.
38 **/
41 /* The list of trusted domains. Note that the list can be deleted and
42 recreated using the init_domain_list() function so pointers to
43 individual winbindd_domain structures cannot be made. Keep a copy of
44 the domain name instead. */
46 static struct winbindd_domain *_domain_list = NULL;
48 /**
49 When was the last scan of trusted domains done?
51 0 == not ever
54 static time_t last_trustdom_scan;
56 struct winbindd_domain *domain_list(void)
58 /* Initialise list */
60 if ((!_domain_list) && (!init_domain_list())) {
61 smb_panic("Init_domain_list failed");
64 return _domain_list;
67 /* Free all entries in the trusted domain list */
69 void free_domain_list(void)
71 struct winbindd_domain *domain = _domain_list;
73 while(domain) {
74 struct winbindd_domain *next = domain->next;
76 DLIST_REMOVE(_domain_list, domain);
77 SAFE_FREE(domain);
78 domain = next;
82 static bool is_internal_domain(const DOM_SID *sid)
84 if (sid == NULL)
85 return False;
87 if ( IS_DC )
88 return sid_check_is_builtin(sid);
90 return (sid_check_is_domain(sid) || sid_check_is_builtin(sid));
93 static bool is_in_internal_domain(const DOM_SID *sid)
95 if (sid == NULL)
96 return False;
98 if ( IS_DC )
99 return sid_check_is_in_builtin(sid);
101 return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid));
105 /* Add a trusted domain to our list of domains */
106 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
107 struct winbindd_methods *methods,
108 const DOM_SID *sid)
110 struct winbindd_domain *domain;
111 const char *alternative_name = NULL;
112 char *idmap_config_option;
113 const char *param;
114 const char **ignored_domains, **dom;
116 ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
117 for (dom=ignored_domains; dom && *dom; dom++) {
118 if (gen_fnmatch(*dom, domain_name) == 0) {
119 DEBUG(2,("Ignoring domain '%s'\n", domain_name));
120 return NULL;
124 /* ignore alt_name if we are not in an AD domain */
126 if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
127 alternative_name = alt_name;
130 /* We can't call domain_list() as this function is called from
131 init_domain_list() and we'll get stuck in a loop. */
132 for (domain = _domain_list; domain; domain = domain->next) {
133 if (strequal(domain_name, domain->name) ||
134 strequal(domain_name, domain->alt_name))
136 break;
139 if (alternative_name && *alternative_name)
141 if (strequal(alternative_name, domain->name) ||
142 strequal(alternative_name, domain->alt_name))
144 break;
148 if (sid)
150 if (is_null_sid(sid)) {
151 continue;
154 if (sid_equal(sid, &domain->sid)) {
155 break;
160 /* See if we found a match. Check if we need to update the
161 SID. */
163 if ( domain && sid) {
164 if ( sid_equal( &domain->sid, &global_sid_NULL ) )
165 sid_copy( &domain->sid, sid );
167 return domain;
170 /* Create new domain entry */
172 if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
173 return NULL;
175 /* Fill in fields */
177 ZERO_STRUCTP(domain);
179 fstrcpy(domain->name, domain_name);
180 if (alternative_name) {
181 fstrcpy(domain->alt_name, alternative_name);
184 domain->methods = methods;
185 domain->backend = NULL;
186 domain->internal = is_internal_domain(sid);
187 domain->sequence_number = DOM_SEQUENCE_NONE;
188 domain->last_seq_check = 0;
189 domain->initialized = False;
190 domain->online = is_internal_domain(sid);
191 domain->check_online_timeout = 0;
192 domain->dc_probe_pid = (pid_t)-1;
193 if (sid) {
194 sid_copy(&domain->sid, sid);
197 /* Link to domain list */
198 DLIST_ADD_END(_domain_list, domain, struct winbindd_domain *);
200 wcache_tdc_add_domain( domain );
202 idmap_config_option = talloc_asprintf(talloc_tos(), "idmap config %s",
203 domain->name);
204 if (idmap_config_option == NULL) {
205 DEBUG(0, ("talloc failed, not looking for idmap config\n"));
206 goto done;
209 param = lp_parm_const_string(-1, idmap_config_option, "range", NULL);
211 DEBUG(10, ("%s : range = %s\n", idmap_config_option,
212 param ? param : "not defined"));
214 if (param != NULL) {
215 unsigned low_id, high_id;
216 if (sscanf(param, "%u - %u", &low_id, &high_id) != 2) {
217 DEBUG(1, ("invalid range syntax in %s: %s\n",
218 idmap_config_option, param));
219 goto done;
221 if (low_id > high_id) {
222 DEBUG(1, ("invalid range in %s: %s\n",
223 idmap_config_option, param));
224 goto done;
226 domain->have_idmap_config = true;
227 domain->id_range_low = low_id;
228 domain->id_range_high = high_id;
231 done:
233 DEBUG(2,("Added domain %s %s %s\n",
234 domain->name, domain->alt_name,
235 &domain->sid?sid_string_dbg(&domain->sid):""));
237 return domain;
240 /********************************************************************
241 rescan our domains looking for new trusted domains
242 ********************************************************************/
244 struct trustdom_state {
245 TALLOC_CTX *mem_ctx;
246 bool primary;
247 bool forest_root;
248 struct winbindd_response *response;
251 static void trustdom_recv(void *private_data, bool success);
252 static void rescan_forest_root_trusts( void );
253 static void rescan_forest_trusts( void );
255 static void add_trusted_domains( struct winbindd_domain *domain )
257 TALLOC_CTX *mem_ctx;
258 struct winbindd_request *request;
259 struct winbindd_response *response;
260 uint32 fr_flags = (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
262 struct trustdom_state *state;
264 mem_ctx = talloc_init("add_trusted_domains");
265 if (mem_ctx == NULL) {
266 DEBUG(0, ("talloc_init failed\n"));
267 return;
270 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
271 response = TALLOC_P(mem_ctx, struct winbindd_response);
272 state = TALLOC_P(mem_ctx, struct trustdom_state);
274 if ((request == NULL) || (response == NULL) || (state == NULL)) {
275 DEBUG(0, ("talloc failed\n"));
276 talloc_destroy(mem_ctx);
277 return;
280 state->mem_ctx = mem_ctx;
281 state->response = response;
283 /* Flags used to know how to continue the forest trust search */
285 state->primary = domain->primary;
286 state->forest_root = ((domain->domain_flags & fr_flags) == fr_flags );
288 request->length = sizeof(*request);
289 request->cmd = WINBINDD_LIST_TRUSTDOM;
291 async_domain_request(mem_ctx, domain, request, response,
292 trustdom_recv, state);
295 static void trustdom_recv(void *private_data, bool success)
297 struct trustdom_state *state =
298 talloc_get_type_abort(private_data, struct trustdom_state);
299 struct winbindd_response *response = state->response;
300 char *p;
302 if ((!success) || (response->result != WINBINDD_OK)) {
303 DEBUG(1, ("Could not receive trustdoms\n"));
304 talloc_destroy(state->mem_ctx);
305 return;
308 p = (char *)response->extra_data.data;
310 while ((p != NULL) && (*p != '\0')) {
311 char *q, *sidstr, *alt_name;
312 DOM_SID sid;
313 struct winbindd_domain *domain;
314 char *alternate_name = NULL;
316 alt_name = strchr(p, '\\');
317 if (alt_name == NULL) {
318 DEBUG(0, ("Got invalid trustdom response\n"));
319 break;
322 *alt_name = '\0';
323 alt_name += 1;
325 sidstr = strchr(alt_name, '\\');
326 if (sidstr == NULL) {
327 DEBUG(0, ("Got invalid trustdom response\n"));
328 break;
331 *sidstr = '\0';
332 sidstr += 1;
334 q = strchr(sidstr, '\n');
335 if (q != NULL)
336 *q = '\0';
338 if (!string_to_sid(&sid, sidstr)) {
339 /* Allow NULL sid for sibling domains */
340 if ( strcmp(sidstr,"S-0-0") == 0) {
341 sid_copy( &sid, &global_sid_NULL);
342 } else {
343 DEBUG(0, ("Got invalid trustdom response\n"));
344 break;
348 /* use the real alt_name if we have one, else pass in NULL */
350 if ( !strequal( alt_name, "(null)" ) )
351 alternate_name = alt_name;
353 /* If we have an existing domain structure, calling
354 add_trusted_domain() will update the SID if
355 necessary. This is important because we need the
356 SID for sibling domains */
358 if ( find_domain_from_name_noinit(p) != NULL ) {
359 domain = add_trusted_domain(p, alternate_name,
360 &cache_methods,
361 &sid);
362 } else {
363 domain = add_trusted_domain(p, alternate_name,
364 &cache_methods,
365 &sid);
366 if (domain) {
367 setup_domain_child(domain,
368 &domain->child);
371 p=q;
372 if (p != NULL)
373 p += 1;
376 SAFE_FREE(response->extra_data.data);
379 Cases to consider when scanning trusts:
380 (a) we are calling from a child domain (primary && !forest_root)
381 (b) we are calling from the root of the forest (primary && forest_root)
382 (c) we are calling from a trusted forest domain (!primary
383 && !forest_root)
386 if ( state->primary ) {
387 /* If this is our primary domain and we are not in the
388 forest root, we have to scan the root trusts first */
390 if ( !state->forest_root )
391 rescan_forest_root_trusts();
392 else
393 rescan_forest_trusts();
395 } else if ( state->forest_root ) {
396 /* Once we have done root forest trust search, we can
397 go on to search the trusted forests */
399 rescan_forest_trusts();
402 talloc_destroy(state->mem_ctx);
404 return;
407 /********************************************************************
408 Scan the trusts of our forest root
409 ********************************************************************/
411 static void rescan_forest_root_trusts( void )
413 struct winbindd_tdc_domain *dom_list = NULL;
414 size_t num_trusts = 0;
415 int i;
417 /* The only transitive trusts supported by Windows 2003 AD are
418 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
419 first two are handled in forest and listed by
420 DsEnumerateDomainTrusts(). Forest trusts are not so we
421 have to do that ourselves. */
423 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
424 return;
426 for ( i=0; i<num_trusts; i++ ) {
427 struct winbindd_domain *d = NULL;
429 /* Find the forest root. Don't necessarily trust
430 the domain_list() as our primary domain may not
431 have been initialized. */
433 if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
434 continue;
437 /* Here's the forest root */
439 d = find_domain_from_name_noinit( dom_list[i].domain_name );
441 if ( !d ) {
442 d = add_trusted_domain( dom_list[i].domain_name,
443 dom_list[i].dns_name,
444 &cache_methods,
445 &dom_list[i].sid );
448 if (d == NULL) {
449 continue;
452 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
453 "for domain tree root %s (%s)\n",
454 d->name, d->alt_name ));
456 d->domain_flags = dom_list[i].trust_flags;
457 d->domain_type = dom_list[i].trust_type;
458 d->domain_trust_attribs = dom_list[i].trust_attribs;
460 add_trusted_domains( d );
462 break;
465 TALLOC_FREE( dom_list );
467 return;
470 /********************************************************************
471 scan the transitive forest trusts (not our own)
472 ********************************************************************/
475 static void rescan_forest_trusts( void )
477 struct winbindd_domain *d = NULL;
478 struct winbindd_tdc_domain *dom_list = NULL;
479 size_t num_trusts = 0;
480 int i;
482 /* The only transitive trusts supported by Windows 2003 AD are
483 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
484 first two are handled in forest and listed by
485 DsEnumerateDomainTrusts(). Forest trusts are not so we
486 have to do that ourselves. */
488 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
489 return;
491 for ( i=0; i<num_trusts; i++ ) {
492 uint32 flags = dom_list[i].trust_flags;
493 uint32 type = dom_list[i].trust_type;
494 uint32 attribs = dom_list[i].trust_attribs;
496 d = find_domain_from_name_noinit( dom_list[i].domain_name );
498 /* ignore our primary and internal domains */
500 if ( d && (d->internal || d->primary ) )
501 continue;
503 if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
504 (type == NETR_TRUST_TYPE_UPLEVEL) &&
505 (attribs == NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
507 /* add the trusted domain if we don't know
508 about it */
510 if ( !d ) {
511 d = add_trusted_domain( dom_list[i].domain_name,
512 dom_list[i].dns_name,
513 &cache_methods,
514 &dom_list[i].sid );
517 if (d == NULL) {
518 continue;
521 DEBUG(10,("Following trust path for domain %s (%s)\n",
522 d->name, d->alt_name ));
523 add_trusted_domains( d );
527 TALLOC_FREE( dom_list );
529 return;
532 /*********************************************************************
533 The process of updating the trusted domain list is a three step
534 async process:
535 (a) ask our domain
536 (b) ask the root domain in our forest
537 (c) ask the a DC in any Win2003 trusted forests
538 *********************************************************************/
540 void rescan_trusted_domains( void )
542 time_t now = time(NULL);
544 /* see if the time has come... */
546 if ((now >= last_trustdom_scan) &&
547 ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
548 return;
550 /* I use to clear the cache here and start over but that
551 caused problems in child processes that needed the
552 trust dom list early on. Removing it means we
553 could have some trusted domains listed that have been
554 removed from our primary domain's DC until a full
555 restart. This should be ok since I think this is what
556 Windows does as well. */
558 /* this will only add new domains we didn't already know about
559 in the domain_list()*/
561 add_trusted_domains( find_our_domain() );
563 last_trustdom_scan = now;
565 return;
568 struct init_child_state {
569 TALLOC_CTX *mem_ctx;
570 struct winbindd_domain *domain;
571 struct winbindd_request *request;
572 struct winbindd_response *response;
573 void (*continuation)(void *private_data, bool success);
574 void *private_data;
577 static void init_child_recv(void *private_data, bool success);
578 static void init_child_getdc_recv(void *private_data, bool success);
580 enum winbindd_result init_child_connection(struct winbindd_domain *domain,
581 void (*continuation)(void *private_data,
582 bool success),
583 void *private_data)
585 TALLOC_CTX *mem_ctx;
586 struct winbindd_request *request;
587 struct winbindd_response *response;
588 struct init_child_state *state;
589 struct winbindd_domain *request_domain;
591 mem_ctx = talloc_init("init_child_connection");
592 if (mem_ctx == NULL) {
593 DEBUG(0, ("talloc_init failed\n"));
594 return WINBINDD_ERROR;
597 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
598 response = TALLOC_P(mem_ctx, struct winbindd_response);
599 state = TALLOC_P(mem_ctx, struct init_child_state);
601 if ((request == NULL) || (response == NULL) || (state == NULL)) {
602 DEBUG(0, ("talloc failed\n"));
603 TALLOC_FREE(mem_ctx);
604 continuation(private_data, False);
605 return WINBINDD_ERROR;
608 request->length = sizeof(*request);
610 state->mem_ctx = mem_ctx;
611 state->domain = domain;
612 state->request = request;
613 state->response = response;
614 state->continuation = continuation;
615 state->private_data = private_data;
617 if (IS_DC || domain->primary || domain->internal ) {
618 /* The primary domain has to find the DC name itself */
619 request->cmd = WINBINDD_INIT_CONNECTION;
620 fstrcpy(request->domain_name, domain->name);
621 request->data.init_conn.is_primary = domain->primary ? true : false;
622 fstrcpy(request->data.init_conn.dcname, "");
623 async_request(mem_ctx, &domain->child, request, response,
624 init_child_recv, state);
625 return WINBINDD_PENDING;
628 /* This is *not* the primary domain, let's ask our DC about a DC
629 * name */
631 request->cmd = WINBINDD_GETDCNAME;
632 fstrcpy(request->domain_name, domain->name);
634 request_domain = find_our_domain();
635 async_domain_request(mem_ctx, request_domain, request, response,
636 init_child_getdc_recv, state);
637 return WINBINDD_PENDING;
640 static void init_child_getdc_recv(void *private_data, bool success)
642 struct init_child_state *state =
643 talloc_get_type_abort(private_data, struct init_child_state);
644 const char *dcname = "";
646 DEBUG(10, ("Received getdcname response\n"));
648 if (success && (state->response->result == WINBINDD_OK)) {
649 dcname = state->response->data.dc_name;
652 state->request->cmd = WINBINDD_INIT_CONNECTION;
653 fstrcpy(state->request->domain_name, state->domain->name);
654 state->request->data.init_conn.is_primary = False;
655 fstrcpy(state->request->data.init_conn.dcname, dcname);
657 async_request(state->mem_ctx, &state->domain->child,
658 state->request, state->response,
659 init_child_recv, state);
662 static void init_child_recv(void *private_data, bool success)
664 struct init_child_state *state =
665 talloc_get_type_abort(private_data, struct init_child_state);
667 DEBUG(5, ("Received child initialization response for domain %s\n",
668 state->domain->name));
670 if ((!success) || (state->response->result != WINBINDD_OK)) {
671 DEBUG(3, ("Could not init child\n"));
672 state->continuation(state->private_data, False);
673 talloc_destroy(state->mem_ctx);
674 return;
677 fstrcpy(state->domain->name,
678 state->response->data.domain_info.name);
679 fstrcpy(state->domain->alt_name,
680 state->response->data.domain_info.alt_name);
681 string_to_sid(&state->domain->sid,
682 state->response->data.domain_info.sid);
683 state->domain->native_mode =
684 state->response->data.domain_info.native_mode;
685 state->domain->active_directory =
686 state->response->data.domain_info.active_directory;
688 init_dc_connection(state->domain);
690 if (state->continuation != NULL)
691 state->continuation(state->private_data, True);
692 talloc_destroy(state->mem_ctx);
695 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
696 struct winbindd_cli_state *state)
698 /* Ensure null termination */
699 state->request.domain_name
700 [sizeof(state->request.domain_name)-1]='\0';
701 state->request.data.init_conn.dcname
702 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
704 if (strlen(state->request.data.init_conn.dcname) > 0) {
705 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
708 init_dc_connection(domain);
710 if (!domain->initialized) {
711 /* If we return error here we can't do any cached authentication,
712 but we may be in disconnected mode and can't initialize correctly.
713 Do what the previous code did and just return without initialization,
714 once we go online we'll re-initialize.
716 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
717 "online = %d\n", domain->name, (int)domain->online ));
720 fstrcpy(state->response.data.domain_info.name, domain->name);
721 fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
722 sid_to_fstring(state->response.data.domain_info.sid, &domain->sid);
724 state->response.data.domain_info.native_mode
725 = domain->native_mode;
726 state->response.data.domain_info.active_directory
727 = domain->active_directory;
728 state->response.data.domain_info.primary
729 = domain->primary;
731 return WINBINDD_OK;
734 /* Look up global info for the winbind daemon */
735 bool init_domain_list(void)
737 struct winbindd_domain *domain;
738 int role = lp_server_role();
740 /* Free existing list */
741 free_domain_list();
743 /* BUILTIN domain */
745 domain = add_trusted_domain("BUILTIN", NULL, &builtin_passdb_methods,
746 &global_sid_Builtin);
747 if (domain) {
748 setup_domain_child(domain,
749 &domain->child);
752 /* Local SAM */
754 domain = add_trusted_domain(get_global_sam_name(), NULL,
755 &sam_passdb_methods, get_global_sam_sid());
756 if (domain) {
757 if ( role != ROLE_DOMAIN_MEMBER ) {
758 domain->primary = True;
760 setup_domain_child(domain,
761 &domain->child);
764 /* Add ourselves as the first entry. */
766 if ( role == ROLE_DOMAIN_MEMBER ) {
767 DOM_SID our_sid;
769 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
770 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
771 return False;
774 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
775 &cache_methods, &our_sid);
776 if (domain) {
777 domain->primary = True;
778 setup_domain_child(domain,
779 &domain->child);
781 /* Even in the parent winbindd we'll need to
782 talk to the DC, so try and see if we can
783 contact it. Theoretically this isn't neccessary
784 as the init_dc_connection() in init_child_recv()
785 will do this, but we can start detecting the DC
786 early here. */
787 set_domain_online_request(domain);
791 return True;
794 void check_domain_trusted( const char *name, const DOM_SID *user_sid )
796 struct winbindd_domain *domain;
797 DOM_SID dom_sid;
798 uint32 rid;
800 domain = find_domain_from_name_noinit( name );
801 if ( domain )
802 return;
804 sid_copy( &dom_sid, user_sid );
805 if ( !sid_split_rid( &dom_sid, &rid ) )
806 return;
808 /* add the newly discovered trusted domain */
810 domain = add_trusted_domain( name, NULL, &cache_methods,
811 &dom_sid);
813 if ( !domain )
814 return;
816 /* assume this is a trust from a one-way transitive
817 forest trust */
819 domain->active_directory = True;
820 domain->domain_flags = NETR_TRUST_FLAG_OUTBOUND;
821 domain->domain_type = NETR_TRUST_TYPE_UPLEVEL;
822 domain->internal = False;
823 domain->online = True;
825 setup_domain_child(domain,
826 &domain->child);
828 wcache_tdc_add_domain( domain );
830 return;
833 /**
834 * Given a domain name, return the struct winbindd domain info for it
836 * @note Do *not* pass lp_workgroup() to this function. domain_list
837 * may modify it's value, and free that pointer. Instead, our local
838 * domain may be found by calling find_our_domain().
839 * directly.
842 * @return The domain structure for the named domain, if it is working.
845 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
847 struct winbindd_domain *domain;
849 /* Search through list */
851 for (domain = domain_list(); domain != NULL; domain = domain->next) {
852 if (strequal(domain_name, domain->name) ||
853 (domain->alt_name[0] &&
854 strequal(domain_name, domain->alt_name))) {
855 return domain;
859 /* Not found */
861 return NULL;
864 struct winbindd_domain *find_domain_from_name(const char *domain_name)
866 struct winbindd_domain *domain;
868 domain = find_domain_from_name_noinit(domain_name);
870 if (domain == NULL)
871 return NULL;
873 if (!domain->initialized)
874 init_dc_connection(domain);
876 return domain;
879 /* Given a domain sid, return the struct winbindd domain info for it */
881 struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid)
883 struct winbindd_domain *domain;
885 /* Search through list */
887 for (domain = domain_list(); domain != NULL; domain = domain->next) {
888 if (sid_compare_domain(sid, &domain->sid) == 0)
889 return domain;
892 /* Not found */
894 return NULL;
897 /* Given a domain sid, return the struct winbindd domain info for it */
899 struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid)
901 struct winbindd_domain *domain;
903 domain = find_domain_from_sid_noinit(sid);
905 if (domain == NULL)
906 return NULL;
908 if (!domain->initialized)
909 init_dc_connection(domain);
911 return domain;
914 struct winbindd_domain *find_our_domain(void)
916 struct winbindd_domain *domain;
918 /* Search through list */
920 for (domain = domain_list(); domain != NULL; domain = domain->next) {
921 if (domain->primary)
922 return domain;
925 smb_panic("Could not find our domain");
926 return NULL;
929 struct winbindd_domain *find_root_domain(void)
931 struct winbindd_domain *ours = find_our_domain();
933 if ( !ours )
934 return NULL;
936 if ( strlen(ours->forest_name) == 0 )
937 return NULL;
939 return find_domain_from_name( ours->forest_name );
942 struct winbindd_domain *find_builtin_domain(void)
944 DOM_SID sid;
945 struct winbindd_domain *domain;
947 string_to_sid(&sid, "S-1-5-32");
948 domain = find_domain_from_sid(&sid);
950 if (domain == NULL) {
951 smb_panic("Could not find BUILTIN domain");
954 return domain;
957 /* Find the appropriate domain to lookup a name or SID */
959 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
961 /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
963 if ( sid_check_is_in_unix_groups(sid) ||
964 sid_check_is_unix_groups(sid) ||
965 sid_check_is_in_unix_users(sid) ||
966 sid_check_is_unix_users(sid) )
968 return find_domain_from_sid(get_global_sam_sid());
971 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
972 * one to contact the external DC's. On member servers the internal
973 * domains are different: These are part of the local SAM. */
975 DEBUG(10, ("find_lookup_domain_from_sid(%s)\n", sid_string_dbg(sid)));
977 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
978 DEBUG(10, ("calling find_domain_from_sid\n"));
979 return find_domain_from_sid(sid);
982 /* On a member server a query for SID or name can always go to our
983 * primary DC. */
985 DEBUG(10, ("calling find_our_domain\n"));
986 return find_our_domain();
989 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
991 if ( strequal(domain_name, unix_users_domain_name() ) ||
992 strequal(domain_name, unix_groups_domain_name() ) )
994 return find_domain_from_name_noinit( get_global_sam_name() );
997 if (IS_DC || strequal(domain_name, "BUILTIN") ||
998 strequal(domain_name, get_global_sam_name()))
999 return find_domain_from_name_noinit(domain_name);
1001 /* The "Unix User" and "Unix Group" domain our handled by passdb */
1003 return find_our_domain();
1006 /* Lookup a sid in a domain from a name */
1008 bool winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
1009 enum winbindd_cmd orig_cmd,
1010 struct winbindd_domain *domain,
1011 const char *domain_name,
1012 const char *name, DOM_SID *sid,
1013 enum lsa_SidType *type)
1015 NTSTATUS result;
1017 /* Lookup name */
1018 result = domain->methods->name_to_sid(domain, mem_ctx, orig_cmd,
1019 domain_name, name, sid, type);
1021 /* Return sid and type if lookup successful */
1022 if (!NT_STATUS_IS_OK(result)) {
1023 *type = SID_NAME_UNKNOWN;
1026 return NT_STATUS_IS_OK(result);
1030 * @brief Lookup a name in a domain from a sid.
1032 * @param sid Security ID you want to look up.
1033 * @param name On success, set to the name corresponding to @p sid.
1034 * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
1035 * @param type On success, contains the type of name: alias, group or
1036 * user.
1037 * @retval True if the name exists, in which case @p name and @p type
1038 * are set, otherwise False.
1040 bool winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
1041 struct winbindd_domain *domain,
1042 DOM_SID *sid,
1043 char **dom_name,
1044 char **name,
1045 enum lsa_SidType *type)
1047 NTSTATUS result;
1049 *dom_name = NULL;
1050 *name = NULL;
1052 /* Lookup name */
1054 result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type);
1056 /* Return name and type if successful */
1058 if (NT_STATUS_IS_OK(result)) {
1059 return True;
1062 *type = SID_NAME_UNKNOWN;
1064 return False;
1067 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
1069 void free_getent_state(struct getent_state *state)
1071 struct getent_state *temp;
1073 /* Iterate over state list */
1075 temp = state;
1077 while(temp != NULL) {
1078 struct getent_state *next;
1080 /* Free sam entries then list entry */
1082 SAFE_FREE(state->sam_entries);
1083 DLIST_REMOVE(state, state);
1084 next = temp->next;
1086 SAFE_FREE(temp);
1087 temp = next;
1091 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1093 static bool assume_domain(const char *domain)
1095 /* never assume the domain on a standalone server */
1097 if ( lp_server_role() == ROLE_STANDALONE )
1098 return False;
1100 /* domain member servers may possibly assume for the domain name */
1102 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1103 if ( !strequal(lp_workgroup(), domain) )
1104 return False;
1106 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
1107 return True;
1110 /* only left with a domain controller */
1112 if ( strequal(get_global_sam_name(), domain) ) {
1113 return True;
1116 return False;
1119 /* Parse a string of the form DOMAIN\user into a domain and a user */
1121 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
1123 char *p = strchr(domuser,*lp_winbind_separator());
1125 if ( !p ) {
1126 fstrcpy(user, domuser);
1128 if ( assume_domain(lp_workgroup())) {
1129 fstrcpy(domain, lp_workgroup());
1130 } else if ((p = strchr(domuser, '@')) != NULL) {
1131 fstrcpy(domain, "");
1132 } else {
1133 return False;
1135 } else {
1136 fstrcpy(user, p+1);
1137 fstrcpy(domain, domuser);
1138 domain[PTR_DIFF(p, domuser)] = 0;
1141 strupper_m(domain);
1143 return True;
1146 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1147 char **domain, char **user)
1149 fstring fstr_domain, fstr_user;
1150 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1151 return False;
1153 *domain = talloc_strdup(mem_ctx, fstr_domain);
1154 *user = talloc_strdup(mem_ctx, fstr_user);
1155 return ((*domain != NULL) && (*user != NULL));
1158 /* add a domain user name to a buffer */
1159 void parse_add_domuser(void *buf, char *domuser, int *len)
1161 fstring domain;
1162 char *p, *user;
1164 user = domuser;
1165 p = strchr(domuser, *lp_winbind_separator());
1167 if (p) {
1169 fstrcpy(domain, domuser);
1170 domain[PTR_DIFF(p, domuser)] = 0;
1171 p++;
1173 if (assume_domain(domain)) {
1175 user = p;
1176 *len -= (PTR_DIFF(p, domuser));
1180 safe_strcpy(buf, user, *len);
1183 /* Ensure an incoming username from NSS is fully qualified. Replace the
1184 incoming fstring with DOMAIN <separator> user. Returns the same
1185 values as parse_domain_user() but also replaces the incoming username.
1186 Used to ensure all names are fully qualified within winbindd.
1187 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1188 The protocol definitions of auth_crap, chng_pswd_auth_crap
1189 really should be changed to use this instead of doing things
1190 by hand. JRA. */
1192 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
1194 if (!parse_domain_user(username_inout, domain, user)) {
1195 return False;
1197 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1198 domain, *lp_winbind_separator(),
1199 user);
1200 return True;
1204 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1205 'winbind separator' options.
1206 This means:
1207 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1208 lp_workgroup()
1210 If we are a PDC or BDC, and this is for our domain, do likewise.
1212 Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
1213 username is then unqualified in unix
1215 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1217 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
1219 fstring tmp_user;
1221 fstrcpy(tmp_user, user);
1222 strlower_m(tmp_user);
1224 if (can_assume && assume_domain(domain)) {
1225 strlcpy(name, tmp_user, sizeof(fstring));
1226 } else {
1227 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1228 domain, *lp_winbind_separator(),
1229 tmp_user);
1234 * Winbindd socket accessor functions
1237 const char *get_winbind_pipe_dir(void)
1239 return lp_parm_const_string(-1, "winbindd", "socket dir", WINBINDD_SOCKET_DIR);
1242 char *get_winbind_priv_pipe_dir(void)
1244 return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
1247 /* Open the winbindd socket */
1249 static int _winbindd_socket = -1;
1250 static int _winbindd_priv_socket = -1;
1252 int open_winbindd_socket(void)
1254 if (_winbindd_socket == -1) {
1255 _winbindd_socket = create_pipe_sock(
1256 get_winbind_pipe_dir(), WINBINDD_SOCKET_NAME, 0755);
1257 DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
1258 _winbindd_socket));
1261 return _winbindd_socket;
1264 int open_winbindd_priv_socket(void)
1266 if (_winbindd_priv_socket == -1) {
1267 _winbindd_priv_socket = create_pipe_sock(
1268 get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
1269 DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
1270 _winbindd_priv_socket));
1273 return _winbindd_priv_socket;
1276 /* Close the winbindd socket */
1278 void close_winbindd_socket(void)
1280 if (_winbindd_socket != -1) {
1281 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
1282 _winbindd_socket));
1283 close(_winbindd_socket);
1284 _winbindd_socket = -1;
1286 if (_winbindd_priv_socket != -1) {
1287 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
1288 _winbindd_priv_socket));
1289 close(_winbindd_priv_socket);
1290 _winbindd_priv_socket = -1;
1295 * Client list accessor functions
1298 static struct winbindd_cli_state *_client_list;
1299 static int _num_clients;
1301 /* Return list of all connected clients */
1303 struct winbindd_cli_state *winbindd_client_list(void)
1305 return _client_list;
1308 /* Add a connection to the list */
1310 void winbindd_add_client(struct winbindd_cli_state *cli)
1312 DLIST_ADD(_client_list, cli);
1313 _num_clients++;
1316 /* Remove a client from the list */
1318 void winbindd_remove_client(struct winbindd_cli_state *cli)
1320 DLIST_REMOVE(_client_list, cli);
1321 _num_clients--;
1324 /* Close all open clients */
1326 void winbindd_kill_all_clients(void)
1328 struct winbindd_cli_state *cl = winbindd_client_list();
1330 DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
1332 while (cl) {
1333 struct winbindd_cli_state *next;
1335 next = cl->next;
1336 winbindd_remove_client(cl);
1337 cl = next;
1341 /* Return number of open clients */
1343 int winbindd_num_clients(void)
1345 return _num_clients;
1348 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1349 TALLOC_CTX *mem_ctx,
1350 const DOM_SID *user_sid,
1351 uint32 *p_num_groups, DOM_SID **user_sids)
1353 struct netr_SamInfo3 *info3 = NULL;
1354 NTSTATUS status = NT_STATUS_NO_MEMORY;
1355 size_t num_groups = 0;
1357 DEBUG(3,(": lookup_usergroups_cached\n"));
1359 *user_sids = NULL;
1360 *p_num_groups = 0;
1362 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1364 if (info3 == NULL) {
1365 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1368 if (info3->base.groups.count == 0) {
1369 TALLOC_FREE(info3);
1370 return NT_STATUS_UNSUCCESSFUL;
1373 /* Skip Domain local groups outside our domain.
1374 We'll get these from the getsidaliases() RPC call. */
1375 status = sid_array_from_info3(mem_ctx, info3,
1376 user_sids,
1377 &num_groups,
1378 false, true);
1380 if (!NT_STATUS_IS_OK(status)) {
1381 TALLOC_FREE(info3);
1382 return status;
1385 TALLOC_FREE(info3);
1386 *p_num_groups = num_groups;
1387 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1389 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1391 return status;
1394 /*********************************************************************
1395 We use this to remove spaces from user and group names
1396 ********************************************************************/
1398 NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
1399 struct winbindd_domain *domain,
1400 char *name,
1401 char **normalized)
1403 NTSTATUS nt_status;
1405 if (!name || !normalized) {
1406 return NT_STATUS_INVALID_PARAMETER;
1409 if (!lp_winbind_normalize_names()) {
1410 return NT_STATUS_PROCEDURE_NOT_FOUND;
1413 /* Alias support and whitespace replacement are mutually
1414 exclusive */
1416 nt_status = resolve_username_to_alias(mem_ctx, domain,
1417 name, normalized );
1418 if (NT_STATUS_IS_OK(nt_status)) {
1419 /* special return code to let the caller know we
1420 mapped to an alias */
1421 return NT_STATUS_FILE_RENAMED;
1424 /* check for an unreachable domain */
1426 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1427 DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
1428 domain->name));
1429 set_domain_offline(domain);
1430 return nt_status;
1433 /* deal with whitespace */
1435 *normalized = talloc_strdup(mem_ctx, name);
1436 if (!(*normalized)) {
1437 return NT_STATUS_NO_MEMORY;
1440 all_string_sub( *normalized, " ", "_", 0 );
1442 return NT_STATUS_OK;
1445 /*********************************************************************
1446 We use this to do the inverse of normalize_name_map()
1447 ********************************************************************/
1449 NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
1450 char *name,
1451 char **normalized)
1453 NTSTATUS nt_status;
1454 struct winbindd_domain *domain = find_our_domain();
1456 if (!name || !normalized) {
1457 return NT_STATUS_INVALID_PARAMETER;
1460 if (!lp_winbind_normalize_names()) {
1461 return NT_STATUS_PROCEDURE_NOT_FOUND;
1464 /* Alias support and whitespace replacement are mutally
1465 exclusive */
1467 /* When mapping from an alias to a username, we don't know the
1468 domain. But we only need a domain structure to cache
1469 a successful lookup , so just our own domain structure for
1470 the seqnum. */
1472 nt_status = resolve_alias_to_username(mem_ctx, domain,
1473 name, normalized);
1474 if (NT_STATUS_IS_OK(nt_status)) {
1475 /* Special return code to let the caller know we mapped
1476 from an alias */
1477 return NT_STATUS_FILE_RENAMED;
1480 /* check for an unreachable domain */
1482 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1483 DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
1484 domain->name));
1485 set_domain_offline(domain);
1486 return nt_status;
1489 /* deal with whitespace */
1491 *normalized = talloc_strdup(mem_ctx, name);
1492 if (!(*normalized)) {
1493 return NT_STATUS_NO_MEMORY;
1496 all_string_sub(*normalized, "_", " ", 0);
1498 return NT_STATUS_OK;
1501 /*********************************************************************
1502 ********************************************************************/
1504 bool winbindd_can_contact_domain(struct winbindd_domain *domain)
1506 struct winbindd_tdc_domain *tdc = NULL;
1507 TALLOC_CTX *frame = talloc_stackframe();
1508 bool ret = false;
1510 /* We can contact the domain if it is our primary domain */
1512 if (domain->primary) {
1513 return true;
1516 /* Trust the TDC cache and not the winbindd_domain flags */
1518 if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
1519 DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
1520 domain->name));
1521 return false;
1524 /* Can always contact a domain that is in out forest */
1526 if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
1527 ret = true;
1528 goto done;
1532 * On a _member_ server, we cannot contact the domain if it
1533 * is running AD and we have no inbound trust.
1536 if (!IS_DC &&
1537 domain->active_directory &&
1538 ((tdc->trust_flags & NETR_TRUST_FLAG_INBOUND) != NETR_TRUST_FLAG_INBOUND))
1540 DEBUG(10, ("winbindd_can_contact_domain: %s is an AD domain "
1541 "and we have no inbound trust.\n", domain->name));
1542 goto done;
1545 /* Assume everything else is ok (probably not true but what
1546 can you do?) */
1548 ret = true;
1550 done:
1551 talloc_destroy(frame);
1553 return ret;
1556 /*********************************************************************
1557 ********************************************************************/
1559 bool winbindd_internal_child(struct winbindd_child *child)
1561 if ((child == idmap_child()) || (child == locator_child())) {
1562 return True;
1565 return False;
1568 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1570 /*********************************************************************
1571 ********************************************************************/
1573 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1575 char *var = NULL;
1576 char addr[INET6_ADDRSTRLEN];
1577 const char *kdc = NULL;
1578 int lvl = 11;
1580 if (!domain || !domain->alt_name || !*domain->alt_name) {
1581 return;
1584 if (domain->initialized && !domain->active_directory) {
1585 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1586 domain->alt_name));
1587 return;
1590 print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
1591 kdc = addr;
1592 if (!*kdc) {
1593 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1594 domain->alt_name));
1595 kdc = domain->dcname;
1598 if (!kdc || !*kdc) {
1599 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1600 domain->alt_name));
1601 return;
1604 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1605 domain->alt_name) == -1) {
1606 return;
1609 DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1610 var, kdc));
1612 setenv(var, kdc, 1);
1613 free(var);
1616 /*********************************************************************
1617 ********************************************************************/
1619 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1621 struct winbindd_domain *our_dom = find_our_domain();
1623 winbindd_set_locator_kdc_env(domain);
1625 if (domain != our_dom) {
1626 winbindd_set_locator_kdc_env(our_dom);
1630 /*********************************************************************
1631 ********************************************************************/
1633 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1635 char *var = NULL;
1637 if (!domain || !domain->alt_name || !*domain->alt_name) {
1638 return;
1641 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1642 domain->alt_name) == -1) {
1643 return;
1646 unsetenv(var);
1647 free(var);
1649 #else
1651 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1653 return;
1656 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1658 return;
1661 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */
1663 void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
1665 resp->data.auth.nt_status = NT_STATUS_V(result);
1666 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
1668 /* we might have given a more useful error above */
1669 if (*resp->data.auth.error_string == '\0')
1670 fstrcpy(resp->data.auth.error_string,
1671 get_friendly_nt_error_msg(result));
1672 resp->data.auth.pam_error = nt_status_to_pam(result);