r23054: Move the check for the lookup_domain of S-1-22-{1,2} before the
[Samba.git] / source / nsswitch / winbindd_util.c
blob13971be8b711ed5f7a3d066a65d49fffba4691b9
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 2 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, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "includes.h"
25 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 extern struct winbindd_methods cache_methods;
31 extern struct winbindd_methods passdb_methods;
33 /**
34 * @file winbindd_util.c
36 * Winbind daemon for NT domain authentication nss module.
37 **/
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;
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\n");
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 ) {
145 if ( sid_equal( &domain->sid, &global_sid_NULL ) )
146 sid_copy( &domain->sid, sid );
148 return domain;
151 /* See if we found a match. Check if we need to update the
152 SID. */
154 if ( domain ) {
155 if ( sid_equal( &domain->sid, &global_sid_NULL ) )
156 sid_copy( &domain->sid, sid );
158 return domain;
161 /* Create new domain entry */
163 if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
164 return NULL;
166 /* Fill in fields */
168 ZERO_STRUCTP(domain);
170 /* prioritise the short name */
171 if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) {
172 fstrcpy(domain->name, alternative_name);
173 fstrcpy(domain->alt_name, domain_name);
174 } else {
175 fstrcpy(domain->name, domain_name);
176 if (alternative_name) {
177 fstrcpy(domain->alt_name, alternative_name);
181 domain->methods = methods;
182 domain->backend = NULL;
183 domain->internal = is_internal_domain(sid);
184 domain->sequence_number = DOM_SEQUENCE_NONE;
185 domain->last_seq_check = 0;
186 domain->initialized = False;
187 domain->online = is_internal_domain(sid);
188 domain->check_online_timeout = 0;
189 if (sid) {
190 sid_copy(&domain->sid, sid);
193 /* Link to domain list */
194 DLIST_ADD(_domain_list, domain);
196 wcache_tdc_add_domain( domain );
198 DEBUG(2,("Added domain %s %s %s\n",
199 domain->name, domain->alt_name,
200 &domain->sid?sid_string_static(&domain->sid):""));
202 return domain;
205 /********************************************************************
206 rescan our domains looking for new trusted domains
207 ********************************************************************/
209 struct trustdom_state {
210 TALLOC_CTX *mem_ctx;
211 BOOL primary;
212 BOOL forest_root;
213 struct winbindd_response *response;
216 static void trustdom_recv(void *private_data, BOOL success);
217 static void rescan_forest_root_trusts( void );
218 static void rescan_forest_trusts( void );
220 static void add_trusted_domains( struct winbindd_domain *domain )
222 TALLOC_CTX *mem_ctx;
223 struct winbindd_request *request;
224 struct winbindd_response *response;
225 uint32 fr_flags = (DS_DOMAIN_TREE_ROOT|DS_DOMAIN_IN_FOREST);
227 struct trustdom_state *state;
229 mem_ctx = talloc_init("add_trusted_domains");
230 if (mem_ctx == NULL) {
231 DEBUG(0, ("talloc_init failed\n"));
232 return;
235 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
236 response = TALLOC_P(mem_ctx, struct winbindd_response);
237 state = TALLOC_P(mem_ctx, struct trustdom_state);
239 if ((request == NULL) || (response == NULL) || (state == NULL)) {
240 DEBUG(0, ("talloc failed\n"));
241 talloc_destroy(mem_ctx);
242 return;
245 state->mem_ctx = mem_ctx;
246 state->response = response;
248 /* Flags used to know how to continue the forest trust search */
250 state->primary = domain->primary;
251 state->forest_root = ((domain->domain_flags & fr_flags) == fr_flags );
253 request->length = sizeof(*request);
254 request->cmd = WINBINDD_LIST_TRUSTDOM;
256 async_domain_request(mem_ctx, domain, request, response,
257 trustdom_recv, state);
260 static void trustdom_recv(void *private_data, BOOL success)
262 struct trustdom_state *state =
263 talloc_get_type_abort(private_data, struct trustdom_state);
264 struct winbindd_response *response = state->response;
265 char *p;
267 if ((!success) || (response->result != WINBINDD_OK)) {
268 DEBUG(1, ("Could not receive trustdoms\n"));
269 talloc_destroy(state->mem_ctx);
270 return;
273 p = (char *)response->extra_data.data;
275 while ((p != NULL) && (*p != '\0')) {
276 char *q, *sidstr, *alt_name;
277 DOM_SID sid;
278 struct winbindd_domain *domain;
279 char *alternate_name = NULL;
281 alt_name = strchr(p, '\\');
282 if (alt_name == NULL) {
283 DEBUG(0, ("Got invalid trustdom response\n"));
284 break;
287 *alt_name = '\0';
288 alt_name += 1;
290 sidstr = strchr(alt_name, '\\');
291 if (sidstr == NULL) {
292 DEBUG(0, ("Got invalid trustdom response\n"));
293 break;
296 *sidstr = '\0';
297 sidstr += 1;
299 q = strchr(sidstr, '\n');
300 if (q != NULL)
301 *q = '\0';
303 if (!string_to_sid(&sid, sidstr)) {
304 /* Allow NULL sid for sibling domains */
305 if ( strcmp(sidstr,"S-0-0") == 0) {
306 sid_copy( &sid, &global_sid_NULL);
307 } else {
308 DEBUG(0, ("Got invalid trustdom response\n"));
309 break;
313 /* use the real alt_name if we have one, else pass in NULL */
315 if ( !strequal( alt_name, "(null)" ) )
316 alternate_name = alt_name;
318 /* If we have an existing domain structure, calling
319 add_trusted_domain() will update the SID if
320 necessary. This is important because we need the
321 SID for sibling domains */
323 if ( find_domain_from_name_noinit(p) != NULL ) {
324 domain = add_trusted_domain(p, alternate_name,
325 &cache_methods,
326 &sid);
327 } else {
328 domain = add_trusted_domain(p, alternate_name,
329 &cache_methods,
330 &sid);
331 setup_domain_child(domain, &domain->child, NULL);
333 p=q;
334 if (p != NULL)
335 p += 1;
338 SAFE_FREE(response->extra_data.data);
341 Cases to consider when scanning trusts:
342 (a) we are calling from a child domain (primary && !forest_root)
343 (b) we are calling from the root of the forest (primary && forest_root)
344 (c) we are calling from a trusted forest domain (!primary
345 && !forest_root)
348 if ( state->primary ) {
349 /* If this is our primary domain and we are not the in the
350 forest root, we have to scan the root trusts first */
352 if ( !state->forest_root )
353 rescan_forest_root_trusts();
354 else
355 rescan_forest_trusts();
357 } else if ( state->forest_root ) {
358 /* Once we have done root forest trust search, we can
359 go on to search thing trusted forests */
361 rescan_forest_trusts();
364 talloc_destroy(state->mem_ctx);
366 return;
369 /********************************************************************
370 Scan the trusts of our forest root
371 ********************************************************************/
373 static void rescan_forest_root_trusts( void )
375 struct winbindd_tdc_domain *dom_list = NULL;
376 size_t num_trusts = 0;
377 int i;
379 /* The only transitive trusts supported by Windows 2003 AD are
380 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
381 first two are handled in forest and listed by
382 DsEnumerateDomainTrusts(). Forest trusts are not so we
383 have to do that ourselves. */
385 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
386 return;
388 for ( i=0; i<num_trusts; i++ ) {
389 struct winbindd_domain *d = NULL;
391 /* Find the forest root. Don't necessarily trust
392 the domain_list() as our primary domain may not
393 have been initialized. */
395 if ( !(dom_list[i].trust_flags & DS_DOMAIN_TREE_ROOT) ) {
396 continue;
399 /* Here's the forest root */
401 d = find_domain_from_name_noinit( dom_list[i].domain_name );
403 if ( !d ) {
404 d = add_trusted_domain( dom_list[i].domain_name,
405 dom_list[i].dns_name,
406 &cache_methods,
407 &dom_list[i].sid );
410 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
411 "for domain tree root %s (%s)\n",
412 d->name, d->alt_name ));
414 d->domain_flags = dom_list[i].trust_flags;
415 d->domain_type = dom_list[i].trust_type;
416 d->domain_trust_attribs = dom_list[i].trust_attribs;
418 add_trusted_domains( d );
420 break;
423 TALLOC_FREE( dom_list );
425 return;
428 /********************************************************************
429 scan the transitive forest trists (not our own)
430 ********************************************************************/
433 static void rescan_forest_trusts( void )
435 struct winbindd_domain *d = NULL;
436 struct winbindd_tdc_domain *dom_list = NULL;
437 size_t num_trusts = 0;
438 int i;
440 /* The only transitive trusts supported by Windows 2003 AD are
441 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
442 first two are handled in forest and listed by
443 DsEnumerateDomainTrusts(). Forest trusts are not so we
444 have to do that ourselves. */
446 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
447 return;
449 for ( i=0; i<num_trusts; i++ ) {
450 uint32 flags = dom_list[i].trust_flags;
451 uint32 type = dom_list[i].trust_type;
452 uint32 attribs = dom_list[i].trust_attribs;
454 d = find_domain_from_name_noinit( dom_list[i].domain_name );
456 /* ignore our primary and internal domains */
458 if ( d && (d->internal || d->primary ) )
459 continue;
461 if ( (flags & DS_DOMAIN_DIRECT_INBOUND) &&
462 (type == DS_DOMAIN_TRUST_TYPE_UPLEVEL) &&
463 (attribs == DS_DOMAIN_TRUST_ATTRIB_FOREST_TRANSITIVE) )
465 /* add the trusted domain if we don't know
466 about it */
468 if ( !d ) {
469 d = add_trusted_domain( dom_list[i].domain_name,
470 dom_list[i].dns_name,
471 &cache_methods,
472 &dom_list[i].sid );
475 DEBUG(10,("Following trust path for domain %s (%s)\n",
476 d->name, d->alt_name ));
477 add_trusted_domains( d );
481 TALLOC_FREE( dom_list );
483 return;
486 /*********************************************************************
487 The process of updating the trusted domain list is a three step
488 async process:
489 (a) ask our domain
490 (b) ask the root domain in our forest
491 (c) ask the a DC in any Win2003 trusted forests
492 *********************************************************************/
494 void rescan_trusted_domains( void )
496 time_t now = time(NULL);
498 /* see if the time has come... */
500 if ((now >= last_trustdom_scan) &&
501 ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
502 return;
504 /* clear the TRUSTDOM cache first */
506 wcache_tdc_clear();
508 /* this will only add new domains we didn't already know about
509 in the domain_list()*/
511 add_trusted_domains( find_our_domain() );
513 last_trustdom_scan = now;
515 return;
518 struct init_child_state {
519 TALLOC_CTX *mem_ctx;
520 struct winbindd_domain *domain;
521 struct winbindd_request *request;
522 struct winbindd_response *response;
523 void (*continuation)(void *private_data, BOOL success);
524 void *private_data;
527 static void init_child_recv(void *private_data, BOOL success);
528 static void init_child_getdc_recv(void *private_data, BOOL success);
530 enum winbindd_result init_child_connection(struct winbindd_domain *domain,
531 void (*continuation)(void *private_data,
532 BOOL success),
533 void *private_data)
535 TALLOC_CTX *mem_ctx;
536 struct winbindd_request *request;
537 struct winbindd_response *response;
538 struct init_child_state *state;
539 struct winbindd_domain *request_domain;
541 mem_ctx = talloc_init("init_child_connection");
542 if (mem_ctx == NULL) {
543 DEBUG(0, ("talloc_init failed\n"));
544 return WINBINDD_ERROR;
547 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
548 response = TALLOC_P(mem_ctx, struct winbindd_response);
549 state = TALLOC_P(mem_ctx, struct init_child_state);
551 if ((request == NULL) || (response == NULL) || (state == NULL)) {
552 DEBUG(0, ("talloc failed\n"));
553 TALLOC_FREE(mem_ctx);
554 continuation(private_data, False);
555 return WINBINDD_ERROR;
558 request->length = sizeof(*request);
560 state->mem_ctx = mem_ctx;
561 state->domain = domain;
562 state->request = request;
563 state->response = response;
564 state->continuation = continuation;
565 state->private_data = private_data;
567 if (IS_DC || domain->primary || domain->internal ) {
568 /* The primary domain has to find the DC name itself */
569 request->cmd = WINBINDD_INIT_CONNECTION;
570 fstrcpy(request->domain_name, domain->name);
571 request->data.init_conn.is_primary = domain->internal ? False : True;
572 fstrcpy(request->data.init_conn.dcname, "");
573 async_request(mem_ctx, &domain->child, request, response,
574 init_child_recv, state);
575 return WINBINDD_PENDING;
578 /* This is *not* the primary domain, let's ask our DC about a DC
579 * name */
581 request->cmd = WINBINDD_GETDCNAME;
582 fstrcpy(request->domain_name, domain->name);
584 request_domain = find_our_domain();
585 async_domain_request(mem_ctx, request_domain, request, response,
586 init_child_getdc_recv, state);
587 return WINBINDD_PENDING;
590 static void init_child_getdc_recv(void *private_data, BOOL success)
592 struct init_child_state *state =
593 talloc_get_type_abort(private_data, struct init_child_state);
594 const char *dcname = "";
596 DEBUG(10, ("Received getdcname response\n"));
598 if (success && (state->response->result == WINBINDD_OK)) {
599 dcname = state->response->data.dc_name;
602 state->request->cmd = WINBINDD_INIT_CONNECTION;
603 fstrcpy(state->request->domain_name, state->domain->name);
604 state->request->data.init_conn.is_primary = False;
605 fstrcpy(state->request->data.init_conn.dcname, dcname);
607 async_request(state->mem_ctx, &state->domain->child,
608 state->request, state->response,
609 init_child_recv, state);
612 static void init_child_recv(void *private_data, BOOL success)
614 struct init_child_state *state =
615 talloc_get_type_abort(private_data, struct init_child_state);
617 DEBUG(5, ("Received child initialization response for domain %s\n",
618 state->domain->name));
620 if ((!success) || (state->response->result != WINBINDD_OK)) {
621 DEBUG(3, ("Could not init child\n"));
622 state->continuation(state->private_data, False);
623 talloc_destroy(state->mem_ctx);
624 return;
627 fstrcpy(state->domain->name,
628 state->response->data.domain_info.name);
629 fstrcpy(state->domain->alt_name,
630 state->response->data.domain_info.alt_name);
631 string_to_sid(&state->domain->sid,
632 state->response->data.domain_info.sid);
633 state->domain->native_mode =
634 state->response->data.domain_info.native_mode;
635 state->domain->active_directory =
636 state->response->data.domain_info.active_directory;
637 state->domain->sequence_number =
638 state->response->data.domain_info.sequence_number;
640 init_dc_connection(state->domain);
642 if (state->continuation != NULL)
643 state->continuation(state->private_data, True);
644 talloc_destroy(state->mem_ctx);
647 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
648 struct winbindd_cli_state *state)
650 /* Ensure null termination */
651 state->request.domain_name
652 [sizeof(state->request.domain_name)-1]='\0';
653 state->request.data.init_conn.dcname
654 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
656 if (strlen(state->request.data.init_conn.dcname) > 0) {
657 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
660 init_dc_connection(domain);
662 if (!domain->initialized) {
663 /* If we return error here we can't do any cached authentication,
664 but we may be in disconnected mode and can't initialize correctly.
665 Do what the previous code did and just return without initialization,
666 once we go online we'll re-initialize.
668 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
669 "online = %d\n", domain->name, (int)domain->online ));
672 fstrcpy(state->response.data.domain_info.name, domain->name);
673 fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
674 fstrcpy(state->response.data.domain_info.sid,
675 sid_string_static(&domain->sid));
677 state->response.data.domain_info.native_mode
678 = domain->native_mode;
679 state->response.data.domain_info.active_directory
680 = domain->active_directory;
681 state->response.data.domain_info.primary
682 = domain->primary;
683 state->response.data.domain_info.sequence_number =
684 domain->sequence_number;
686 return WINBINDD_OK;
689 /* Look up global info for the winbind daemon */
690 BOOL init_domain_list(void)
692 struct winbindd_domain *domain;
693 int role = lp_server_role();
695 /* Free existing list */
696 free_domain_list();
698 /* Add ourselves as the first entry. */
700 if ( role == ROLE_DOMAIN_MEMBER ) {
701 DOM_SID our_sid;
703 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
704 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
705 return False;
708 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
709 &cache_methods, &our_sid);
710 domain->primary = True;
711 setup_domain_child(domain, &domain->child, NULL);
713 /* Even in the parent winbindd we'll need to
714 talk to the DC, so try and see if we can
715 contact it. Theoretically this isn't neccessary
716 as the init_dc_connection() in init_child_recv()
717 will do this, but we can start detecting the DC
718 early here. */
719 set_domain_online_request(domain);
722 /* Local SAM */
724 domain = add_trusted_domain(get_global_sam_name(), NULL,
725 &passdb_methods, get_global_sam_sid());
726 if ( role != ROLE_DOMAIN_MEMBER ) {
727 domain->primary = True;
729 setup_domain_child(domain, &domain->child, NULL);
731 /* BUILTIN domain */
733 domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
734 &global_sid_Builtin);
735 setup_domain_child(domain, &domain->child, NULL);
737 return True;
740 void check_domain_trusted( const char *name, const DOM_SID *user_sid )
742 struct winbindd_domain *domain;
743 DOM_SID dom_sid;
744 uint32 rid;
746 domain = find_domain_from_name_noinit( name );
747 if ( domain )
748 return;
750 sid_copy( &dom_sid, user_sid );
751 if ( !sid_split_rid( &dom_sid, &rid ) )
752 return;
754 /* add the newly discovered trusted domain */
756 domain = add_trusted_domain( name, NULL, &cache_methods,
757 &dom_sid);
759 if ( !domain )
760 return;
762 /* assume this is a trust from a one-way transitive
763 forest trust */
765 domain->active_directory = True;
766 domain->domain_flags = DS_DOMAIN_DIRECT_OUTBOUND;
767 domain->domain_type = DS_DOMAIN_TRUST_TYPE_UPLEVEL;
768 domain->internal = False;
769 domain->online = True;
771 setup_domain_child(domain, &domain->child, NULL);
773 wcache_tdc_add_domain( domain );
775 return;
778 /**
779 * Given a domain name, return the struct winbindd domain info for it
781 * @note Do *not* pass lp_workgroup() to this function. domain_list
782 * may modify it's value, and free that pointer. Instead, our local
783 * domain may be found by calling find_our_domain().
784 * directly.
787 * @return The domain structure for the named domain, if it is working.
790 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
792 struct winbindd_domain *domain;
794 /* Search through list */
796 for (domain = domain_list(); domain != NULL; domain = domain->next) {
797 if (strequal(domain_name, domain->name) ||
798 (domain->alt_name[0] &&
799 strequal(domain_name, domain->alt_name))) {
800 return domain;
804 /* Not found */
806 return NULL;
809 struct winbindd_domain *find_domain_from_name(const char *domain_name)
811 struct winbindd_domain *domain;
813 domain = find_domain_from_name_noinit(domain_name);
815 if (domain == NULL)
816 return NULL;
818 if (!domain->initialized)
819 init_dc_connection(domain);
821 return domain;
824 /* Given a domain sid, return the struct winbindd domain info for it */
826 struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid)
828 struct winbindd_domain *domain;
830 /* Search through list */
832 for (domain = domain_list(); domain != NULL; domain = domain->next) {
833 if (sid_compare_domain(sid, &domain->sid) == 0)
834 return domain;
837 /* Not found */
839 return NULL;
842 /* Given a domain sid, return the struct winbindd domain info for it */
844 struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid)
846 struct winbindd_domain *domain;
848 domain = find_domain_from_sid_noinit(sid);
850 if (domain == NULL)
851 return NULL;
853 if (!domain->initialized)
854 init_dc_connection(domain);
856 return domain;
859 struct winbindd_domain *find_our_domain(void)
861 struct winbindd_domain *domain;
863 /* Search through list */
865 for (domain = domain_list(); domain != NULL; domain = domain->next) {
866 if (domain->primary)
867 return domain;
870 smb_panic("Could not find our domain\n");
871 return NULL;
874 struct winbindd_domain *find_root_domain(void)
876 struct winbindd_domain *ours = find_our_domain();
878 if ( !ours )
879 return NULL;
881 if ( strlen(ours->forest_name) == 0 )
882 return NULL;
884 return find_domain_from_name( ours->forest_name );
887 struct winbindd_domain *find_builtin_domain(void)
889 DOM_SID sid;
890 struct winbindd_domain *domain;
892 string_to_sid(&sid, "S-1-5-32");
893 domain = find_domain_from_sid(&sid);
895 if (domain == NULL)
896 smb_panic("Could not find BUILTIN domain\n");
898 return domain;
901 /* Find the appropriate domain to lookup a name or SID */
903 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
905 /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
907 if ( sid_check_is_in_unix_groups(sid) ||
908 sid_check_is_unix_groups(sid) ||
909 sid_check_is_in_unix_users(sid) ||
910 sid_check_is_unix_users(sid) )
912 return find_domain_from_sid(get_global_sam_sid());
915 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
916 * one to contact the external DC's. On member servers the internal
917 * domains are different: These are part of the local SAM. */
919 DEBUG(10, ("find_lookup_domain_from_sid(%s)\n",
920 sid_string_static(sid)));
922 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
923 DEBUG(10, ("calling find_domain_from_sid\n"));
924 return find_domain_from_sid(sid);
927 /* On a member server a query for SID or name can always go to our
928 * primary DC. */
930 DEBUG(10, ("calling find_our_domain\n"));
931 return find_our_domain();
934 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
936 if ( strequal(domain_name, unix_users_domain_name() ) ||
937 strequal(domain_name, unix_groups_domain_name() ) )
939 return find_domain_from_name_noinit( get_global_sam_name() );
942 if (IS_DC || strequal(domain_name, "BUILTIN") ||
943 strequal(domain_name, get_global_sam_name()))
944 return find_domain_from_name_noinit(domain_name);
946 /* The "Unix User" and "Unix Group" domain our handled by passdb */
948 return find_our_domain();
951 /* Lookup a sid in a domain from a name */
953 BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
954 struct winbindd_domain *domain,
955 const char *domain_name,
956 const char *name, DOM_SID *sid,
957 enum lsa_SidType *type)
959 NTSTATUS result;
961 /* Lookup name */
962 result = domain->methods->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
964 /* Return sid and type if lookup successful */
965 if (!NT_STATUS_IS_OK(result)) {
966 *type = SID_NAME_UNKNOWN;
969 return NT_STATUS_IS_OK(result);
973 * @brief Lookup a name in a domain from a sid.
975 * @param sid Security ID you want to look up.
976 * @param name On success, set to the name corresponding to @p sid.
977 * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
978 * @param type On success, contains the type of name: alias, group or
979 * user.
980 * @retval True if the name exists, in which case @p name and @p type
981 * are set, otherwise False.
983 BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
984 struct winbindd_domain *domain,
985 DOM_SID *sid,
986 char **dom_name,
987 char **name,
988 enum lsa_SidType *type)
990 NTSTATUS result;
992 *dom_name = NULL;
993 *name = NULL;
995 /* Lookup name */
997 result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type);
999 /* Return name and type if successful */
1001 if (NT_STATUS_IS_OK(result)) {
1002 return True;
1005 *type = SID_NAME_UNKNOWN;
1007 return False;
1010 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
1012 void free_getent_state(struct getent_state *state)
1014 struct getent_state *temp;
1016 /* Iterate over state list */
1018 temp = state;
1020 while(temp != NULL) {
1021 struct getent_state *next;
1023 /* Free sam entries then list entry */
1025 SAFE_FREE(state->sam_entries);
1026 DLIST_REMOVE(state, state);
1027 next = temp->next;
1029 SAFE_FREE(temp);
1030 temp = next;
1034 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1036 static BOOL assume_domain(const char *domain)
1038 /* never assume the domain on a standalone server */
1040 if ( lp_server_role() == ROLE_STANDALONE )
1041 return False;
1043 /* domain member servers may possibly assume for the domain name */
1045 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1046 if ( !strequal(lp_workgroup(), domain) )
1047 return False;
1049 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
1050 return True;
1053 /* only left with a domain controller */
1055 if ( strequal(get_global_sam_name(), domain) ) {
1056 return True;
1059 return False;
1062 /* Parse a string of the form DOMAIN\user into a domain and a user */
1064 BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
1066 char *p = strchr(domuser,*lp_winbind_separator());
1068 if ( !p ) {
1069 fstrcpy(user, domuser);
1071 if ( assume_domain(lp_workgroup())) {
1072 fstrcpy(domain, lp_workgroup());
1073 } else {
1074 return False;
1076 } else {
1077 fstrcpy(user, p+1);
1078 fstrcpy(domain, domuser);
1079 domain[PTR_DIFF(p, domuser)] = 0;
1082 strupper_m(domain);
1084 return True;
1087 BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1088 char **domain, char **user)
1090 fstring fstr_domain, fstr_user;
1091 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1092 return False;
1094 *domain = talloc_strdup(mem_ctx, fstr_domain);
1095 *user = talloc_strdup(mem_ctx, fstr_user);
1096 return ((*domain != NULL) && (*user != NULL));
1099 /* Ensure an incoming username from NSS is fully qualified. Replace the
1100 incoming fstring with DOMAIN <separator> user. Returns the same
1101 values as parse_domain_user() but also replaces the incoming username.
1102 Used to ensure all names are fully qualified within winbindd.
1103 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1104 The protocol definitions of auth_crap, chng_pswd_auth_crap
1105 really should be changed to use this instead of doing things
1106 by hand. JRA. */
1108 BOOL canonicalize_username(fstring username_inout, fstring domain, fstring user)
1110 if (!parse_domain_user(username_inout, domain, user)) {
1111 return False;
1113 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1114 domain, *lp_winbind_separator(),
1115 user);
1116 return True;
1120 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1121 'winbind separator' options.
1122 This means:
1123 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1124 lp_workgroup()
1126 If we are a PDC or BDC, and this is for our domain, do likewise.
1128 Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
1129 username is then unqualified in unix
1131 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1133 void fill_domain_username(fstring name, const char *domain, const char *user, BOOL can_assume)
1135 fstring tmp_user;
1137 fstrcpy(tmp_user, user);
1138 strlower_m(tmp_user);
1140 if (can_assume && assume_domain(domain)) {
1141 strlcpy(name, tmp_user, sizeof(fstring));
1142 } else {
1143 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1144 domain, *lp_winbind_separator(),
1145 tmp_user);
1150 * Winbindd socket accessor functions
1153 char *get_winbind_priv_pipe_dir(void)
1155 return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
1159 * Client list accessor functions
1162 static struct winbindd_cli_state *_client_list;
1163 static int _num_clients;
1165 /* Return list of all connected clients */
1167 struct winbindd_cli_state *winbindd_client_list(void)
1169 return _client_list;
1172 /* Add a connection to the list */
1174 void winbindd_add_client(struct winbindd_cli_state *cli)
1176 DLIST_ADD(_client_list, cli);
1177 _num_clients++;
1180 /* Remove a client from the list */
1182 void winbindd_remove_client(struct winbindd_cli_state *cli)
1184 DLIST_REMOVE(_client_list, cli);
1185 _num_clients--;
1188 /* Close all open clients */
1190 void winbindd_kill_all_clients(void)
1192 struct winbindd_cli_state *cl = winbindd_client_list();
1194 DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
1196 while (cl) {
1197 struct winbindd_cli_state *next;
1199 next = cl->next;
1200 winbindd_remove_client(cl);
1201 cl = next;
1205 /* Return number of open clients */
1207 int winbindd_num_clients(void)
1209 return _num_clients;
1212 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1213 TALLOC_CTX *mem_ctx,
1214 const DOM_SID *user_sid,
1215 uint32 *p_num_groups, DOM_SID **user_sids)
1217 NET_USER_INFO_3 *info3 = NULL;
1218 NTSTATUS status = NT_STATUS_NO_MEMORY;
1219 int i;
1220 size_t num_groups = 0;
1221 DOM_SID group_sid, primary_group;
1223 DEBUG(3,(": lookup_usergroups_cached\n"));
1225 *user_sids = NULL;
1226 num_groups = 0;
1227 *p_num_groups = 0;
1229 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1231 if (info3 == NULL) {
1232 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1235 if (info3->num_groups == 0) {
1236 TALLOC_FREE(info3);
1237 return NT_STATUS_UNSUCCESSFUL;
1240 /* always add the primary group to the sid array */
1241 sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid);
1243 if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
1244 TALLOC_FREE(info3);
1245 return NT_STATUS_NO_MEMORY;
1248 for (i=0; i<info3->num_groups; i++) {
1249 sid_copy(&group_sid, &info3->dom_sid.sid);
1250 sid_append_rid(&group_sid, info3->gids[i].g_rid);
1252 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
1253 &num_groups)) {
1254 TALLOC_FREE(info3);
1255 return NT_STATUS_NO_MEMORY;
1259 TALLOC_FREE(info3);
1260 *p_num_groups = num_groups;
1261 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1263 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1265 return status;
1268 /*********************************************************************
1269 We use this to remove spaces from user and group names
1270 ********************************************************************/
1272 void ws_name_replace( char *name, char replace )
1274 char replace_char[2] = { 0x0, 0x0 };
1276 if ( !lp_winbind_normalize_names() || (replace == '\0') )
1277 return;
1279 replace_char[0] = replace;
1280 all_string_sub( name, " ", replace_char, 0 );
1282 return;
1285 /*********************************************************************
1286 We use this to do the inverse of ws_name_replace()
1287 ********************************************************************/
1289 void ws_name_return( char *name, char replace )
1291 char replace_char[2] = { 0x0, 0x0 };
1293 if ( !lp_winbind_normalize_names() || (replace == '\0') )
1294 return;
1296 replace_char[0] = replace;
1297 all_string_sub( name, replace_char, " ", 0 );
1299 return;
1302 /*********************************************************************
1303 ********************************************************************/
1305 BOOL winbindd_can_contact_domain( struct winbindd_domain *domain )
1307 /* We can contact the domain if it is our primary domain */
1309 if ( domain->primary )
1310 return True;
1312 /* Can always contact a domain that is in out forest */
1314 if ( domain->domain_flags & DS_DOMAIN_IN_FOREST )
1315 return True;
1317 /* We cannot contact the domain if it is running AD and
1318 we have no inbound trust */
1320 if ( domain->active_directory &&
1321 ((domain->domain_flags&DS_DOMAIN_DIRECT_INBOUND) != DS_DOMAIN_DIRECT_INBOUND) )
1323 return False;
1326 /* Assume everything else is ok (probably not true but what
1327 can you do?) */
1329 return True;