Merge branch 'v3-2-test' of ssh://git.samba.org/data/git/samba into v3-2-test
[Samba/ekacnet.git] / source3 / winbindd / winbindd_util.c
blob7933ecf63e42391729e6c7176cd5a3b72bc8001f
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 passdb_methods;
32 /**
33 * @file winbindd_util.c
35 * Winbind daemon for NT domain authentication nss module.
36 **/
39 /* The list of trusted domains. Note that the list can be deleted and
40 recreated using the init_domain_list() function so pointers to
41 individual winbindd_domain structures cannot be made. Keep a copy of
42 the domain name instead. */
44 static struct winbindd_domain *_domain_list = NULL;
46 /**
47 When was the last scan of trusted domains done?
49 0 == not ever
52 static time_t last_trustdom_scan;
54 struct winbindd_domain *domain_list(void)
56 /* Initialise list */
58 if ((!_domain_list) && (!init_domain_list())) {
59 smb_panic("Init_domain_list failed");
62 return _domain_list;
65 /* Free all entries in the trusted domain list */
67 void free_domain_list(void)
69 struct winbindd_domain *domain = _domain_list;
71 while(domain) {
72 struct winbindd_domain *next = domain->next;
74 DLIST_REMOVE(_domain_list, domain);
75 SAFE_FREE(domain);
76 domain = next;
80 static bool is_internal_domain(const DOM_SID *sid)
82 if (sid == NULL)
83 return False;
85 return (sid_check_is_domain(sid) || sid_check_is_builtin(sid));
88 static bool is_in_internal_domain(const DOM_SID *sid)
90 if (sid == NULL)
91 return False;
93 return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid));
97 /* Add a trusted domain to our list of domains */
98 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
99 struct winbindd_methods *methods,
100 const DOM_SID *sid)
102 struct winbindd_domain *domain;
103 const char *alternative_name = NULL;
105 /* ignore alt_name if we are not in an AD domain */
107 if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
108 alternative_name = alt_name;
111 /* We can't call domain_list() as this function is called from
112 init_domain_list() and we'll get stuck in a loop. */
113 for (domain = _domain_list; domain; domain = domain->next) {
114 if (strequal(domain_name, domain->name) ||
115 strequal(domain_name, domain->alt_name))
117 break;
120 if (alternative_name && *alternative_name)
122 if (strequal(alternative_name, domain->name) ||
123 strequal(alternative_name, domain->alt_name))
125 break;
129 if (sid)
131 if (is_null_sid(sid)) {
132 continue;
135 if (sid_equal(sid, &domain->sid)) {
136 break;
141 /* See if we found a match. Check if we need to update the
142 SID. */
144 if ( domain && sid) {
145 if ( sid_equal( &domain->sid, &global_sid_NULL ) )
146 sid_copy( &domain->sid, sid );
148 return domain;
151 /* Create new domain entry */
153 if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
154 return NULL;
156 /* Fill in fields */
158 ZERO_STRUCTP(domain);
160 /* prioritise the short name */
161 if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) {
162 fstrcpy(domain->name, alternative_name);
163 fstrcpy(domain->alt_name, domain_name);
164 } else {
165 fstrcpy(domain->name, domain_name);
166 if (alternative_name) {
167 fstrcpy(domain->alt_name, alternative_name);
171 domain->methods = methods;
172 domain->backend = NULL;
173 domain->internal = is_internal_domain(sid);
174 domain->sequence_number = DOM_SEQUENCE_NONE;
175 domain->last_seq_check = 0;
176 domain->initialized = False;
177 domain->online = is_internal_domain(sid);
178 domain->check_online_timeout = 0;
179 if (sid) {
180 sid_copy(&domain->sid, sid);
183 /* Link to domain list */
184 DLIST_ADD_END(_domain_list, domain, struct winbindd_domain *);
186 wcache_tdc_add_domain( domain );
188 DEBUG(2,("Added domain %s %s %s\n",
189 domain->name, domain->alt_name,
190 &domain->sid?sid_string_dbg(&domain->sid):""));
192 return domain;
195 /********************************************************************
196 rescan our domains looking for new trusted domains
197 ********************************************************************/
199 struct trustdom_state {
200 TALLOC_CTX *mem_ctx;
201 bool primary;
202 bool forest_root;
203 struct winbindd_response *response;
206 static void trustdom_recv(void *private_data, bool success);
207 static void rescan_forest_root_trusts( void );
208 static void rescan_forest_trusts( void );
210 static void add_trusted_domains( struct winbindd_domain *domain )
212 TALLOC_CTX *mem_ctx;
213 struct winbindd_request *request;
214 struct winbindd_response *response;
215 uint32 fr_flags = (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
217 struct trustdom_state *state;
219 mem_ctx = talloc_init("add_trusted_domains");
220 if (mem_ctx == NULL) {
221 DEBUG(0, ("talloc_init failed\n"));
222 return;
225 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
226 response = TALLOC_P(mem_ctx, struct winbindd_response);
227 state = TALLOC_P(mem_ctx, struct trustdom_state);
229 if ((request == NULL) || (response == NULL) || (state == NULL)) {
230 DEBUG(0, ("talloc failed\n"));
231 talloc_destroy(mem_ctx);
232 return;
235 state->mem_ctx = mem_ctx;
236 state->response = response;
238 /* Flags used to know how to continue the forest trust search */
240 state->primary = domain->primary;
241 state->forest_root = ((domain->domain_flags & fr_flags) == fr_flags );
243 request->length = sizeof(*request);
244 request->cmd = WINBINDD_LIST_TRUSTDOM;
246 async_domain_request(mem_ctx, domain, request, response,
247 trustdom_recv, state);
250 static void trustdom_recv(void *private_data, bool success)
252 struct trustdom_state *state =
253 talloc_get_type_abort(private_data, struct trustdom_state);
254 struct winbindd_response *response = state->response;
255 char *p;
257 if ((!success) || (response->result != WINBINDD_OK)) {
258 DEBUG(1, ("Could not receive trustdoms\n"));
259 talloc_destroy(state->mem_ctx);
260 return;
263 p = (char *)response->extra_data.data;
265 while ((p != NULL) && (*p != '\0')) {
266 char *q, *sidstr, *alt_name;
267 DOM_SID sid;
268 struct winbindd_domain *domain;
269 char *alternate_name = NULL;
271 alt_name = strchr(p, '\\');
272 if (alt_name == NULL) {
273 DEBUG(0, ("Got invalid trustdom response\n"));
274 break;
277 *alt_name = '\0';
278 alt_name += 1;
280 sidstr = strchr(alt_name, '\\');
281 if (sidstr == NULL) {
282 DEBUG(0, ("Got invalid trustdom response\n"));
283 break;
286 *sidstr = '\0';
287 sidstr += 1;
289 q = strchr(sidstr, '\n');
290 if (q != NULL)
291 *q = '\0';
293 if (!string_to_sid(&sid, sidstr)) {
294 /* Allow NULL sid for sibling domains */
295 if ( strcmp(sidstr,"S-0-0") == 0) {
296 sid_copy( &sid, &global_sid_NULL);
297 } else {
298 DEBUG(0, ("Got invalid trustdom response\n"));
299 break;
303 /* use the real alt_name if we have one, else pass in NULL */
305 if ( !strequal( alt_name, "(null)" ) )
306 alternate_name = alt_name;
308 /* If we have an existing domain structure, calling
309 add_trusted_domain() will update the SID if
310 necessary. This is important because we need the
311 SID for sibling domains */
313 if ( find_domain_from_name_noinit(p) != NULL ) {
314 domain = add_trusted_domain(p, alternate_name,
315 &cache_methods,
316 &sid);
317 } else {
318 domain = add_trusted_domain(p, alternate_name,
319 &cache_methods,
320 &sid);
321 if (domain) {
322 setup_domain_child(domain,
323 &domain->child);
326 p=q;
327 if (p != NULL)
328 p += 1;
331 SAFE_FREE(response->extra_data.data);
334 Cases to consider when scanning trusts:
335 (a) we are calling from a child domain (primary && !forest_root)
336 (b) we are calling from the root of the forest (primary && forest_root)
337 (c) we are calling from a trusted forest domain (!primary
338 && !forest_root)
341 if ( state->primary ) {
342 /* If this is our primary domain and we are not the in the
343 forest root, we have to scan the root trusts first */
345 if ( !state->forest_root )
346 rescan_forest_root_trusts();
347 else
348 rescan_forest_trusts();
350 } else if ( state->forest_root ) {
351 /* Once we have done root forest trust search, we can
352 go on to search thing trusted forests */
354 rescan_forest_trusts();
357 talloc_destroy(state->mem_ctx);
359 return;
362 /********************************************************************
363 Scan the trusts of our forest root
364 ********************************************************************/
366 static void rescan_forest_root_trusts( void )
368 struct winbindd_tdc_domain *dom_list = NULL;
369 size_t num_trusts = 0;
370 int i;
372 /* The only transitive trusts supported by Windows 2003 AD are
373 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
374 first two are handled in forest and listed by
375 DsEnumerateDomainTrusts(). Forest trusts are not so we
376 have to do that ourselves. */
378 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
379 return;
381 for ( i=0; i<num_trusts; i++ ) {
382 struct winbindd_domain *d = NULL;
384 /* Find the forest root. Don't necessarily trust
385 the domain_list() as our primary domain may not
386 have been initialized. */
388 if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
389 continue;
392 /* Here's the forest root */
394 d = find_domain_from_name_noinit( dom_list[i].domain_name );
396 if ( !d ) {
397 d = add_trusted_domain( dom_list[i].domain_name,
398 dom_list[i].dns_name,
399 &cache_methods,
400 &dom_list[i].sid );
403 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
404 "for domain tree root %s (%s)\n",
405 d->name, d->alt_name ));
407 d->domain_flags = dom_list[i].trust_flags;
408 d->domain_type = dom_list[i].trust_type;
409 d->domain_trust_attribs = dom_list[i].trust_attribs;
411 add_trusted_domains( d );
413 break;
416 TALLOC_FREE( dom_list );
418 return;
421 /********************************************************************
422 scan the transitive forest trists (not our own)
423 ********************************************************************/
426 static void rescan_forest_trusts( void )
428 struct winbindd_domain *d = NULL;
429 struct winbindd_tdc_domain *dom_list = NULL;
430 size_t num_trusts = 0;
431 int i;
433 /* The only transitive trusts supported by Windows 2003 AD are
434 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
435 first two are handled in forest and listed by
436 DsEnumerateDomainTrusts(). Forest trusts are not so we
437 have to do that ourselves. */
439 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
440 return;
442 for ( i=0; i<num_trusts; i++ ) {
443 uint32 flags = dom_list[i].trust_flags;
444 uint32 type = dom_list[i].trust_type;
445 uint32 attribs = dom_list[i].trust_attribs;
447 d = find_domain_from_name_noinit( dom_list[i].domain_name );
449 /* ignore our primary and internal domains */
451 if ( d && (d->internal || d->primary ) )
452 continue;
454 if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
455 (type == NETR_TRUST_TYPE_UPLEVEL) &&
456 (attribs == NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
458 /* add the trusted domain if we don't know
459 about it */
461 if ( !d ) {
462 d = add_trusted_domain( dom_list[i].domain_name,
463 dom_list[i].dns_name,
464 &cache_methods,
465 &dom_list[i].sid );
468 DEBUG(10,("Following trust path for domain %s (%s)\n",
469 d->name, d->alt_name ));
470 add_trusted_domains( d );
474 TALLOC_FREE( dom_list );
476 return;
479 /*********************************************************************
480 The process of updating the trusted domain list is a three step
481 async process:
482 (a) ask our domain
483 (b) ask the root domain in our forest
484 (c) ask the a DC in any Win2003 trusted forests
485 *********************************************************************/
487 void rescan_trusted_domains( void )
489 time_t now = time(NULL);
491 /* see if the time has come... */
493 if ((now >= last_trustdom_scan) &&
494 ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
495 return;
497 /* I use to clear the cache here and start over but that
498 caused problems in child processes that needed the
499 trust dom list early on. Removing it means we
500 could have some trusted domains listed that have been
501 removed from our primary domain's DC until a full
502 restart. This should be ok since I think this is what
503 Windows does as well. */
505 /* this will only add new domains we didn't already know about
506 in the domain_list()*/
508 add_trusted_domains( find_our_domain() );
510 last_trustdom_scan = now;
512 return;
515 struct init_child_state {
516 TALLOC_CTX *mem_ctx;
517 struct winbindd_domain *domain;
518 struct winbindd_request *request;
519 struct winbindd_response *response;
520 void (*continuation)(void *private_data, bool success);
521 void *private_data;
524 static void init_child_recv(void *private_data, bool success);
525 static void init_child_getdc_recv(void *private_data, bool success);
527 enum winbindd_result init_child_connection(struct winbindd_domain *domain,
528 void (*continuation)(void *private_data,
529 bool success),
530 void *private_data)
532 TALLOC_CTX *mem_ctx;
533 struct winbindd_request *request;
534 struct winbindd_response *response;
535 struct init_child_state *state;
536 struct winbindd_domain *request_domain;
538 mem_ctx = talloc_init("init_child_connection");
539 if (mem_ctx == NULL) {
540 DEBUG(0, ("talloc_init failed\n"));
541 return WINBINDD_ERROR;
544 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
545 response = TALLOC_P(mem_ctx, struct winbindd_response);
546 state = TALLOC_P(mem_ctx, struct init_child_state);
548 if ((request == NULL) || (response == NULL) || (state == NULL)) {
549 DEBUG(0, ("talloc failed\n"));
550 TALLOC_FREE(mem_ctx);
551 continuation(private_data, False);
552 return WINBINDD_ERROR;
555 request->length = sizeof(*request);
557 state->mem_ctx = mem_ctx;
558 state->domain = domain;
559 state->request = request;
560 state->response = response;
561 state->continuation = continuation;
562 state->private_data = private_data;
564 if (IS_DC || domain->primary || domain->internal ) {
565 /* The primary domain has to find the DC name itself */
566 request->cmd = WINBINDD_INIT_CONNECTION;
567 fstrcpy(request->domain_name, domain->name);
568 request->data.init_conn.is_primary = domain->primary ? true : false;
569 fstrcpy(request->data.init_conn.dcname, "");
570 async_request(mem_ctx, &domain->child, request, response,
571 init_child_recv, state);
572 return WINBINDD_PENDING;
575 /* This is *not* the primary domain, let's ask our DC about a DC
576 * name */
578 request->cmd = WINBINDD_GETDCNAME;
579 fstrcpy(request->domain_name, domain->name);
581 request_domain = find_our_domain();
582 async_domain_request(mem_ctx, request_domain, request, response,
583 init_child_getdc_recv, state);
584 return WINBINDD_PENDING;
587 static void init_child_getdc_recv(void *private_data, bool success)
589 struct init_child_state *state =
590 talloc_get_type_abort(private_data, struct init_child_state);
591 const char *dcname = "";
593 DEBUG(10, ("Received getdcname response\n"));
595 if (success && (state->response->result == WINBINDD_OK)) {
596 dcname = state->response->data.dc_name;
599 state->request->cmd = WINBINDD_INIT_CONNECTION;
600 fstrcpy(state->request->domain_name, state->domain->name);
601 state->request->data.init_conn.is_primary = False;
602 fstrcpy(state->request->data.init_conn.dcname, dcname);
604 async_request(state->mem_ctx, &state->domain->child,
605 state->request, state->response,
606 init_child_recv, state);
609 static void init_child_recv(void *private_data, bool success)
611 struct init_child_state *state =
612 talloc_get_type_abort(private_data, struct init_child_state);
614 DEBUG(5, ("Received child initialization response for domain %s\n",
615 state->domain->name));
617 if ((!success) || (state->response->result != WINBINDD_OK)) {
618 DEBUG(3, ("Could not init child\n"));
619 state->continuation(state->private_data, False);
620 talloc_destroy(state->mem_ctx);
621 return;
624 fstrcpy(state->domain->name,
625 state->response->data.domain_info.name);
626 fstrcpy(state->domain->alt_name,
627 state->response->data.domain_info.alt_name);
628 string_to_sid(&state->domain->sid,
629 state->response->data.domain_info.sid);
630 state->domain->native_mode =
631 state->response->data.domain_info.native_mode;
632 state->domain->active_directory =
633 state->response->data.domain_info.active_directory;
635 init_dc_connection(state->domain);
637 if (state->continuation != NULL)
638 state->continuation(state->private_data, True);
639 talloc_destroy(state->mem_ctx);
642 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
643 struct winbindd_cli_state *state)
645 /* Ensure null termination */
646 state->request.domain_name
647 [sizeof(state->request.domain_name)-1]='\0';
648 state->request.data.init_conn.dcname
649 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
651 if (strlen(state->request.data.init_conn.dcname) > 0) {
652 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
655 init_dc_connection(domain);
657 if (!domain->initialized) {
658 /* If we return error here we can't do any cached authentication,
659 but we may be in disconnected mode and can't initialize correctly.
660 Do what the previous code did and just return without initialization,
661 once we go online we'll re-initialize.
663 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
664 "online = %d\n", domain->name, (int)domain->online ));
667 fstrcpy(state->response.data.domain_info.name, domain->name);
668 fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
669 sid_to_fstring(state->response.data.domain_info.sid, &domain->sid);
671 state->response.data.domain_info.native_mode
672 = domain->native_mode;
673 state->response.data.domain_info.active_directory
674 = domain->active_directory;
675 state->response.data.domain_info.primary
676 = domain->primary;
678 return WINBINDD_OK;
681 /* Look up global info for the winbind daemon */
682 bool init_domain_list(void)
684 struct winbindd_domain *domain;
685 int role = lp_server_role();
687 /* Free existing list */
688 free_domain_list();
690 /* BUILTIN domain */
692 domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
693 &global_sid_Builtin);
694 if (domain) {
695 setup_domain_child(domain,
696 &domain->child);
699 /* Local SAM */
701 domain = add_trusted_domain(get_global_sam_name(), NULL,
702 &passdb_methods, get_global_sam_sid());
703 if (domain) {
704 if ( role != ROLE_DOMAIN_MEMBER ) {
705 domain->primary = True;
707 setup_domain_child(domain,
708 &domain->child);
711 /* Add ourselves as the first entry. */
713 if ( role == ROLE_DOMAIN_MEMBER ) {
714 DOM_SID our_sid;
716 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
717 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
718 return False;
721 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
722 &cache_methods, &our_sid);
723 if (domain) {
724 domain->primary = True;
725 setup_domain_child(domain,
726 &domain->child);
728 /* Even in the parent winbindd we'll need to
729 talk to the DC, so try and see if we can
730 contact it. Theoretically this isn't neccessary
731 as the init_dc_connection() in init_child_recv()
732 will do this, but we can start detecting the DC
733 early here. */
734 set_domain_online_request(domain);
738 return True;
741 void check_domain_trusted( const char *name, const DOM_SID *user_sid )
743 struct winbindd_domain *domain;
744 DOM_SID dom_sid;
745 uint32 rid;
747 domain = find_domain_from_name_noinit( name );
748 if ( domain )
749 return;
751 sid_copy( &dom_sid, user_sid );
752 if ( !sid_split_rid( &dom_sid, &rid ) )
753 return;
755 /* add the newly discovered trusted domain */
757 domain = add_trusted_domain( name, NULL, &cache_methods,
758 &dom_sid);
760 if ( !domain )
761 return;
763 /* assume this is a trust from a one-way transitive
764 forest trust */
766 domain->active_directory = True;
767 domain->domain_flags = NETR_TRUST_FLAG_OUTBOUND;
768 domain->domain_type = NETR_TRUST_TYPE_UPLEVEL;
769 domain->internal = False;
770 domain->online = True;
772 setup_domain_child(domain,
773 &domain->child);
775 wcache_tdc_add_domain( domain );
777 return;
780 /**
781 * Given a domain name, return the struct winbindd domain info for it
783 * @note Do *not* pass lp_workgroup() to this function. domain_list
784 * may modify it's value, and free that pointer. Instead, our local
785 * domain may be found by calling find_our_domain().
786 * directly.
789 * @return The domain structure for the named domain, if it is working.
792 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
794 struct winbindd_domain *domain;
796 /* Search through list */
798 for (domain = domain_list(); domain != NULL; domain = domain->next) {
799 if (strequal(domain_name, domain->name) ||
800 (domain->alt_name[0] &&
801 strequal(domain_name, domain->alt_name))) {
802 return domain;
806 /* Not found */
808 return NULL;
811 struct winbindd_domain *find_domain_from_name(const char *domain_name)
813 struct winbindd_domain *domain;
815 domain = find_domain_from_name_noinit(domain_name);
817 if (domain == NULL)
818 return NULL;
820 if (!domain->initialized)
821 init_dc_connection(domain);
823 return domain;
826 /* Given a domain sid, return the struct winbindd domain info for it */
828 struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid)
830 struct winbindd_domain *domain;
832 /* Search through list */
834 for (domain = domain_list(); domain != NULL; domain = domain->next) {
835 if (sid_compare_domain(sid, &domain->sid) == 0)
836 return domain;
839 /* Not found */
841 return NULL;
844 /* Given a domain sid, return the struct winbindd domain info for it */
846 struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid)
848 struct winbindd_domain *domain;
850 domain = find_domain_from_sid_noinit(sid);
852 if (domain == NULL)
853 return NULL;
855 if (!domain->initialized)
856 init_dc_connection(domain);
858 return domain;
861 struct winbindd_domain *find_our_domain(void)
863 struct winbindd_domain *domain;
865 /* Search through list */
867 for (domain = domain_list(); domain != NULL; domain = domain->next) {
868 if (domain->primary)
869 return domain;
872 smb_panic("Could not find our domain");
873 return NULL;
876 struct winbindd_domain *find_root_domain(void)
878 struct winbindd_domain *ours = find_our_domain();
880 if ( !ours )
881 return NULL;
883 if ( strlen(ours->forest_name) == 0 )
884 return NULL;
886 return find_domain_from_name( ours->forest_name );
889 struct winbindd_domain *find_builtin_domain(void)
891 DOM_SID sid;
892 struct winbindd_domain *domain;
894 string_to_sid(&sid, "S-1-5-32");
895 domain = find_domain_from_sid(&sid);
897 if (domain == NULL) {
898 smb_panic("Could not find BUILTIN domain");
901 return domain;
904 /* Find the appropriate domain to lookup a name or SID */
906 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
908 /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
910 if ( sid_check_is_in_unix_groups(sid) ||
911 sid_check_is_unix_groups(sid) ||
912 sid_check_is_in_unix_users(sid) ||
913 sid_check_is_unix_users(sid) )
915 return find_domain_from_sid(get_global_sam_sid());
918 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
919 * one to contact the external DC's. On member servers the internal
920 * domains are different: These are part of the local SAM. */
922 DEBUG(10, ("find_lookup_domain_from_sid(%s)\n", sid_string_dbg(sid)));
924 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
925 DEBUG(10, ("calling find_domain_from_sid\n"));
926 return find_domain_from_sid(sid);
929 /* On a member server a query for SID or name can always go to our
930 * primary DC. */
932 DEBUG(10, ("calling find_our_domain\n"));
933 return find_our_domain();
936 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
938 if ( strequal(domain_name, unix_users_domain_name() ) ||
939 strequal(domain_name, unix_groups_domain_name() ) )
941 return find_domain_from_name_noinit( get_global_sam_name() );
944 if (IS_DC || strequal(domain_name, "BUILTIN") ||
945 strequal(domain_name, get_global_sam_name()))
946 return find_domain_from_name_noinit(domain_name);
948 /* The "Unix User" and "Unix Group" domain our handled by passdb */
950 return find_our_domain();
953 /* Lookup a sid in a domain from a name */
955 bool winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
956 enum winbindd_cmd orig_cmd,
957 struct winbindd_domain *domain,
958 const char *domain_name,
959 const char *name, DOM_SID *sid,
960 enum lsa_SidType *type)
962 NTSTATUS result;
964 /* Lookup name */
965 result = domain->methods->name_to_sid(domain, mem_ctx, orig_cmd,
966 domain_name, name, sid, type);
968 /* Return sid and type if lookup successful */
969 if (!NT_STATUS_IS_OK(result)) {
970 *type = SID_NAME_UNKNOWN;
973 return NT_STATUS_IS_OK(result);
977 * @brief Lookup a name in a domain from a sid.
979 * @param sid Security ID you want to look up.
980 * @param name On success, set to the name corresponding to @p sid.
981 * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
982 * @param type On success, contains the type of name: alias, group or
983 * user.
984 * @retval True if the name exists, in which case @p name and @p type
985 * are set, otherwise False.
987 bool winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
988 struct winbindd_domain *domain,
989 DOM_SID *sid,
990 char **dom_name,
991 char **name,
992 enum lsa_SidType *type)
994 NTSTATUS result;
996 *dom_name = NULL;
997 *name = NULL;
999 /* Lookup name */
1001 result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type);
1003 /* Return name and type if successful */
1005 if (NT_STATUS_IS_OK(result)) {
1006 return True;
1009 *type = SID_NAME_UNKNOWN;
1011 return False;
1014 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
1016 void free_getent_state(struct getent_state *state)
1018 struct getent_state *temp;
1020 /* Iterate over state list */
1022 temp = state;
1024 while(temp != NULL) {
1025 struct getent_state *next;
1027 /* Free sam entries then list entry */
1029 SAFE_FREE(state->sam_entries);
1030 DLIST_REMOVE(state, state);
1031 next = temp->next;
1033 SAFE_FREE(temp);
1034 temp = next;
1038 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1040 static bool assume_domain(const char *domain)
1042 /* never assume the domain on a standalone server */
1044 if ( lp_server_role() == ROLE_STANDALONE )
1045 return False;
1047 /* domain member servers may possibly assume for the domain name */
1049 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1050 if ( !strequal(lp_workgroup(), domain) )
1051 return False;
1053 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
1054 return True;
1057 /* only left with a domain controller */
1059 if ( strequal(get_global_sam_name(), domain) ) {
1060 return True;
1063 return False;
1066 /* Parse a string of the form DOMAIN\user into a domain and a user */
1068 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
1070 char *p = strchr(domuser,*lp_winbind_separator());
1072 if ( !p ) {
1073 fstrcpy(user, domuser);
1075 if ( assume_domain(lp_workgroup())) {
1076 fstrcpy(domain, lp_workgroup());
1077 } else if ((p = strchr(domuser, '@')) != NULL) {
1078 fstrcpy(domain, "");
1079 } else {
1080 return False;
1082 } else {
1083 fstrcpy(user, p+1);
1084 fstrcpy(domain, domuser);
1085 domain[PTR_DIFF(p, domuser)] = 0;
1088 strupper_m(domain);
1090 return True;
1093 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1094 char **domain, char **user)
1096 fstring fstr_domain, fstr_user;
1097 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1098 return False;
1100 *domain = talloc_strdup(mem_ctx, fstr_domain);
1101 *user = talloc_strdup(mem_ctx, fstr_user);
1102 return ((*domain != NULL) && (*user != NULL));
1105 /* Ensure an incoming username from NSS is fully qualified. Replace the
1106 incoming fstring with DOMAIN <separator> user. Returns the same
1107 values as parse_domain_user() but also replaces the incoming username.
1108 Used to ensure all names are fully qualified within winbindd.
1109 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1110 The protocol definitions of auth_crap, chng_pswd_auth_crap
1111 really should be changed to use this instead of doing things
1112 by hand. JRA. */
1114 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
1116 if (!parse_domain_user(username_inout, domain, user)) {
1117 return False;
1119 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1120 domain, *lp_winbind_separator(),
1121 user);
1122 return True;
1126 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1127 'winbind separator' options.
1128 This means:
1129 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1130 lp_workgroup()
1132 If we are a PDC or BDC, and this is for our domain, do likewise.
1134 Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
1135 username is then unqualified in unix
1137 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1139 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
1141 fstring tmp_user;
1143 fstrcpy(tmp_user, user);
1144 strlower_m(tmp_user);
1146 if (can_assume && assume_domain(domain)) {
1147 strlcpy(name, tmp_user, sizeof(fstring));
1148 } else {
1149 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1150 domain, *lp_winbind_separator(),
1151 tmp_user);
1156 * Winbindd socket accessor functions
1159 const char *get_winbind_pipe_dir(void)
1161 return lp_parm_const_string(-1, "winbindd", "socket dir", WINBINDD_SOCKET_DIR);
1164 char *get_winbind_priv_pipe_dir(void)
1166 return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
1169 /* Open the winbindd socket */
1171 static int _winbindd_socket = -1;
1172 static int _winbindd_priv_socket = -1;
1174 int open_winbindd_socket(void)
1176 if (_winbindd_socket == -1) {
1177 _winbindd_socket = create_pipe_sock(
1178 get_winbind_pipe_dir(), WINBINDD_SOCKET_NAME, 0755);
1179 DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
1180 _winbindd_socket));
1183 return _winbindd_socket;
1186 int open_winbindd_priv_socket(void)
1188 if (_winbindd_priv_socket == -1) {
1189 _winbindd_priv_socket = create_pipe_sock(
1190 get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
1191 DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
1192 _winbindd_priv_socket));
1195 return _winbindd_priv_socket;
1198 /* Close the winbindd socket */
1200 void close_winbindd_socket(void)
1202 if (_winbindd_socket != -1) {
1203 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
1204 _winbindd_socket));
1205 close(_winbindd_socket);
1206 _winbindd_socket = -1;
1208 if (_winbindd_priv_socket != -1) {
1209 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
1210 _winbindd_priv_socket));
1211 close(_winbindd_priv_socket);
1212 _winbindd_priv_socket = -1;
1217 * Client list accessor functions
1220 static struct winbindd_cli_state *_client_list;
1221 static int _num_clients;
1223 /* Return list of all connected clients */
1225 struct winbindd_cli_state *winbindd_client_list(void)
1227 return _client_list;
1230 /* Add a connection to the list */
1232 void winbindd_add_client(struct winbindd_cli_state *cli)
1234 DLIST_ADD(_client_list, cli);
1235 _num_clients++;
1238 /* Remove a client from the list */
1240 void winbindd_remove_client(struct winbindd_cli_state *cli)
1242 DLIST_REMOVE(_client_list, cli);
1243 _num_clients--;
1246 /* Close all open clients */
1248 void winbindd_kill_all_clients(void)
1250 struct winbindd_cli_state *cl = winbindd_client_list();
1252 DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
1254 while (cl) {
1255 struct winbindd_cli_state *next;
1257 next = cl->next;
1258 winbindd_remove_client(cl);
1259 cl = next;
1263 /* Return number of open clients */
1265 int winbindd_num_clients(void)
1267 return _num_clients;
1270 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1271 TALLOC_CTX *mem_ctx,
1272 const DOM_SID *user_sid,
1273 uint32 *p_num_groups, DOM_SID **user_sids)
1275 NET_USER_INFO_3 *info3 = NULL;
1276 NTSTATUS status = NT_STATUS_NO_MEMORY;
1277 int i;
1278 size_t num_groups = 0;
1279 DOM_SID group_sid, primary_group;
1281 DEBUG(3,(": lookup_usergroups_cached\n"));
1283 *user_sids = NULL;
1284 num_groups = 0;
1285 *p_num_groups = 0;
1287 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1289 if (info3 == NULL) {
1290 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1293 if (info3->num_groups == 0) {
1294 TALLOC_FREE(info3);
1295 return NT_STATUS_UNSUCCESSFUL;
1298 /* always add the primary group to the sid array */
1299 sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid);
1301 status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
1302 &num_groups);
1303 if (!NT_STATUS_IS_OK(status)) {
1304 TALLOC_FREE(info3);
1305 return status;
1308 for (i=0; i<info3->num_groups; i++) {
1309 sid_copy(&group_sid, &info3->dom_sid.sid);
1310 sid_append_rid(&group_sid, info3->gids[i].g_rid);
1312 status = add_sid_to_array(mem_ctx, &group_sid, user_sids,
1313 &num_groups);
1314 if (!NT_STATUS_IS_OK(status)) {
1315 TALLOC_FREE(info3);
1316 return status;
1320 /* Add any Universal groups in the other_sids list */
1322 for (i=0; i<info3->num_other_sids; i++) {
1323 /* Skip Domain local groups outside our domain.
1324 We'll get these from the getsidaliases() RPC call. */
1325 if (info3->other_sids_attrib[i] & SE_GROUP_RESOURCE)
1326 continue;
1328 status = add_sid_to_array(mem_ctx, &info3->other_sids[i].sid,
1329 user_sids, &num_groups);
1330 if (!NT_STATUS_IS_OK(status)) {
1331 TALLOC_FREE(info3);
1332 return status;
1337 TALLOC_FREE(info3);
1338 *p_num_groups = num_groups;
1339 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1341 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1343 return status;
1346 /*********************************************************************
1347 We use this to remove spaces from user and group names
1348 ********************************************************************/
1350 void ws_name_replace( char *name, char replace )
1352 char replace_char[2] = { 0x0, 0x0 };
1354 if ( !lp_winbind_normalize_names() || (replace == '\0') )
1355 return;
1357 replace_char[0] = replace;
1358 all_string_sub( name, " ", replace_char, 0 );
1360 return;
1363 /*********************************************************************
1364 We use this to do the inverse of ws_name_replace()
1365 ********************************************************************/
1367 void ws_name_return( char *name, char replace )
1369 char replace_char[2] = { 0x0, 0x0 };
1371 if ( !lp_winbind_normalize_names() || (replace == '\0') )
1372 return;
1374 replace_char[0] = replace;
1375 all_string_sub( name, replace_char, " ", 0 );
1377 return;
1380 /*********************************************************************
1381 ********************************************************************/
1383 bool winbindd_can_contact_domain(struct winbindd_domain *domain)
1385 struct winbindd_tdc_domain *tdc = NULL;
1386 TALLOC_CTX *frame = talloc_stackframe();
1387 bool ret = false;
1389 /* We can contact the domain if it is our primary domain */
1391 if (domain->primary) {
1392 return true;
1395 /* Trust the TDC cache and not the winbindd_domain flags */
1397 if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
1398 DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
1399 domain->name));
1400 return false;
1403 /* Can always contact a domain that is in out forest */
1405 if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
1406 ret = true;
1407 goto done;
1411 * On a _member_ server, we cannot contact the domain if it
1412 * is running AD and we have no inbound trust.
1415 if (!IS_DC &&
1416 domain->active_directory &&
1417 ((tdc->trust_flags & NETR_TRUST_FLAG_INBOUND) != NETR_TRUST_FLAG_INBOUND))
1419 DEBUG(10, ("winbindd_can_contact_domain: %s is an AD domain "
1420 "and we have no inbound trust.\n", domain->name));
1421 goto done;
1424 /* Assume everything else is ok (probably not true but what
1425 can you do?) */
1427 ret = true;
1429 done:
1430 talloc_destroy(frame);
1432 return ret;
1435 /*********************************************************************
1436 ********************************************************************/
1438 bool winbindd_internal_child(struct winbindd_child *child)
1440 if ((child == idmap_child()) || (child == locator_child())) {
1441 return True;
1444 return False;
1447 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1449 /*********************************************************************
1450 ********************************************************************/
1452 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1454 char *var = NULL;
1455 char addr[INET6_ADDRSTRLEN];
1456 const char *kdc = NULL;
1457 int lvl = 11;
1459 if (!domain || !domain->alt_name || !*domain->alt_name) {
1460 return;
1463 if (domain->initialized && !domain->active_directory) {
1464 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1465 domain->alt_name));
1466 return;
1469 print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
1470 kdc = addr;
1471 if (!*kdc) {
1472 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1473 domain->alt_name));
1474 kdc = domain->dcname;
1477 if (!kdc || !*kdc) {
1478 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1479 domain->alt_name));
1480 return;
1483 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1484 domain->alt_name) == -1) {
1485 return;
1488 DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1489 var, kdc));
1491 setenv(var, kdc, 1);
1492 free(var);
1495 /*********************************************************************
1496 ********************************************************************/
1498 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1500 struct winbindd_domain *our_dom = find_our_domain();
1502 winbindd_set_locator_kdc_env(domain);
1504 if (domain != our_dom) {
1505 winbindd_set_locator_kdc_env(our_dom);
1509 /*********************************************************************
1510 ********************************************************************/
1512 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1514 char *var = NULL;
1516 if (!domain || !domain->alt_name || !*domain->alt_name) {
1517 return;
1520 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1521 domain->alt_name) == -1) {
1522 return;
1525 unsetenv(var);
1526 free(var);
1528 #else
1530 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1532 return;
1535 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1537 return;
1540 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */