[GLUE] Rsync SAMBA_3_2_0 SVN r25598 in order to create the v3-2-test branch.
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_util.c
blobc2fe09eead3a3177d4a2c46a334ccb11c2ba0fd0
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;
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 if ( IS_DC )
86 return sid_check_is_builtin(sid);
88 return (sid_check_is_domain(sid) || sid_check_is_builtin(sid));
91 static BOOL is_in_internal_domain(const DOM_SID *sid)
93 if (sid == NULL)
94 return False;
96 if ( IS_DC )
97 return sid_check_is_in_builtin(sid);
99 return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid));
103 /* Add a trusted domain to our list of domains */
104 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
105 struct winbindd_methods *methods,
106 const DOM_SID *sid)
108 struct winbindd_domain *domain;
109 const char *alternative_name = NULL;
111 /* ignore alt_name if we are not in an AD domain */
113 if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
114 alternative_name = alt_name;
117 /* We can't call domain_list() as this function is called from
118 init_domain_list() and we'll get stuck in a loop. */
119 for (domain = _domain_list; domain; domain = domain->next) {
120 if (strequal(domain_name, domain->name) ||
121 strequal(domain_name, domain->alt_name))
123 break;
126 if (alternative_name && *alternative_name)
128 if (strequal(alternative_name, domain->name) ||
129 strequal(alternative_name, domain->alt_name))
131 break;
135 if (sid)
137 if (is_null_sid(sid)) {
138 continue;
141 if (sid_equal(sid, &domain->sid)) {
142 break;
147 /* See if we found a match. Check if we need to update the
148 SID. */
150 if ( domain && sid) {
151 if ( sid_equal( &domain->sid, &global_sid_NULL ) )
152 sid_copy( &domain->sid, sid );
154 return domain;
157 /* Create new domain entry */
159 if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
160 return NULL;
162 /* Fill in fields */
164 ZERO_STRUCTP(domain);
166 /* prioritise the short name */
167 if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) {
168 fstrcpy(domain->name, alternative_name);
169 fstrcpy(domain->alt_name, domain_name);
170 } else {
171 fstrcpy(domain->name, domain_name);
172 if (alternative_name) {
173 fstrcpy(domain->alt_name, alternative_name);
177 domain->methods = methods;
178 domain->backend = NULL;
179 domain->internal = is_internal_domain(sid);
180 domain->sequence_number = DOM_SEQUENCE_NONE;
181 domain->last_seq_check = 0;
182 domain->initialized = False;
183 domain->online = is_internal_domain(sid);
184 domain->check_online_timeout = 0;
185 if (sid) {
186 sid_copy(&domain->sid, sid);
189 /* Link to domain list */
190 DLIST_ADD_END(_domain_list, domain, struct winbindd_domain *);
192 wcache_tdc_add_domain( domain );
194 DEBUG(2,("Added domain %s %s %s\n",
195 domain->name, domain->alt_name,
196 &domain->sid?sid_string_static(&domain->sid):""));
198 return domain;
201 /********************************************************************
202 rescan our domains looking for new trusted domains
203 ********************************************************************/
205 struct trustdom_state {
206 TALLOC_CTX *mem_ctx;
207 BOOL primary;
208 BOOL forest_root;
209 struct winbindd_response *response;
212 static void trustdom_recv(void *private_data, BOOL success);
213 static void rescan_forest_root_trusts( void );
214 static void rescan_forest_trusts( void );
216 static void add_trusted_domains( struct winbindd_domain *domain )
218 TALLOC_CTX *mem_ctx;
219 struct winbindd_request *request;
220 struct winbindd_response *response;
221 uint32 fr_flags = (DS_DOMAIN_TREE_ROOT|DS_DOMAIN_IN_FOREST);
223 struct trustdom_state *state;
225 mem_ctx = talloc_init("add_trusted_domains");
226 if (mem_ctx == NULL) {
227 DEBUG(0, ("talloc_init failed\n"));
228 return;
231 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
232 response = TALLOC_P(mem_ctx, struct winbindd_response);
233 state = TALLOC_P(mem_ctx, struct trustdom_state);
235 if ((request == NULL) || (response == NULL) || (state == NULL)) {
236 DEBUG(0, ("talloc failed\n"));
237 talloc_destroy(mem_ctx);
238 return;
241 state->mem_ctx = mem_ctx;
242 state->response = response;
244 /* Flags used to know how to continue the forest trust search */
246 state->primary = domain->primary;
247 state->forest_root = ((domain->domain_flags & fr_flags) == fr_flags );
249 request->length = sizeof(*request);
250 request->cmd = WINBINDD_LIST_TRUSTDOM;
252 async_domain_request(mem_ctx, domain, request, response,
253 trustdom_recv, state);
256 static void trustdom_recv(void *private_data, BOOL success)
258 struct trustdom_state *state =
259 talloc_get_type_abort(private_data, struct trustdom_state);
260 struct winbindd_response *response = state->response;
261 char *p;
263 if ((!success) || (response->result != WINBINDD_OK)) {
264 DEBUG(1, ("Could not receive trustdoms\n"));
265 talloc_destroy(state->mem_ctx);
266 return;
269 p = (char *)response->extra_data.data;
271 while ((p != NULL) && (*p != '\0')) {
272 char *q, *sidstr, *alt_name;
273 DOM_SID sid;
274 struct winbindd_domain *domain;
275 char *alternate_name = NULL;
277 alt_name = strchr(p, '\\');
278 if (alt_name == NULL) {
279 DEBUG(0, ("Got invalid trustdom response\n"));
280 break;
283 *alt_name = '\0';
284 alt_name += 1;
286 sidstr = strchr(alt_name, '\\');
287 if (sidstr == NULL) {
288 DEBUG(0, ("Got invalid trustdom response\n"));
289 break;
292 *sidstr = '\0';
293 sidstr += 1;
295 q = strchr(sidstr, '\n');
296 if (q != NULL)
297 *q = '\0';
299 if (!string_to_sid(&sid, sidstr)) {
300 /* Allow NULL sid for sibling domains */
301 if ( strcmp(sidstr,"S-0-0") == 0) {
302 sid_copy( &sid, &global_sid_NULL);
303 } else {
304 DEBUG(0, ("Got invalid trustdom response\n"));
305 break;
309 /* use the real alt_name if we have one, else pass in NULL */
311 if ( !strequal( alt_name, "(null)" ) )
312 alternate_name = alt_name;
314 /* If we have an existing domain structure, calling
315 add_trusted_domain() will update the SID if
316 necessary. This is important because we need the
317 SID for sibling domains */
319 if ( find_domain_from_name_noinit(p) != NULL ) {
320 domain = add_trusted_domain(p, alternate_name,
321 &cache_methods,
322 &sid);
323 } else {
324 domain = add_trusted_domain(p, alternate_name,
325 &cache_methods,
326 &sid);
327 if (domain) {
328 setup_domain_child(domain, &domain->child, NULL);
331 p=q;
332 if (p != NULL)
333 p += 1;
336 SAFE_FREE(response->extra_data.data);
339 Cases to consider when scanning trusts:
340 (a) we are calling from a child domain (primary && !forest_root)
341 (b) we are calling from the root of the forest (primary && forest_root)
342 (c) we are calling from a trusted forest domain (!primary
343 && !forest_root)
346 if ( state->primary ) {
347 /* If this is our primary domain and we are not the in the
348 forest root, we have to scan the root trusts first */
350 if ( !state->forest_root )
351 rescan_forest_root_trusts();
352 else
353 rescan_forest_trusts();
355 } else if ( state->forest_root ) {
356 /* Once we have done root forest trust search, we can
357 go on to search thing trusted forests */
359 rescan_forest_trusts();
362 talloc_destroy(state->mem_ctx);
364 return;
367 /********************************************************************
368 Scan the trusts of our forest root
369 ********************************************************************/
371 static void rescan_forest_root_trusts( void )
373 struct winbindd_tdc_domain *dom_list = NULL;
374 size_t num_trusts = 0;
375 int i;
377 /* The only transitive trusts supported by Windows 2003 AD are
378 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
379 first two are handled in forest and listed by
380 DsEnumerateDomainTrusts(). Forest trusts are not so we
381 have to do that ourselves. */
383 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
384 return;
386 for ( i=0; i<num_trusts; i++ ) {
387 struct winbindd_domain *d = NULL;
389 /* Find the forest root. Don't necessarily trust
390 the domain_list() as our primary domain may not
391 have been initialized. */
393 if ( !(dom_list[i].trust_flags & DS_DOMAIN_TREE_ROOT) ) {
394 continue;
397 /* Here's the forest root */
399 d = find_domain_from_name_noinit( dom_list[i].domain_name );
401 if ( !d ) {
402 d = add_trusted_domain( dom_list[i].domain_name,
403 dom_list[i].dns_name,
404 &cache_methods,
405 &dom_list[i].sid );
408 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
409 "for domain tree root %s (%s)\n",
410 d->name, d->alt_name ));
412 d->domain_flags = dom_list[i].trust_flags;
413 d->domain_type = dom_list[i].trust_type;
414 d->domain_trust_attribs = dom_list[i].trust_attribs;
416 add_trusted_domains( d );
418 break;
421 TALLOC_FREE( dom_list );
423 return;
426 /********************************************************************
427 scan the transitive forest trists (not our own)
428 ********************************************************************/
431 static void rescan_forest_trusts( void )
433 struct winbindd_domain *d = NULL;
434 struct winbindd_tdc_domain *dom_list = NULL;
435 size_t num_trusts = 0;
436 int i;
438 /* The only transitive trusts supported by Windows 2003 AD are
439 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
440 first two are handled in forest and listed by
441 DsEnumerateDomainTrusts(). Forest trusts are not so we
442 have to do that ourselves. */
444 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
445 return;
447 for ( i=0; i<num_trusts; i++ ) {
448 uint32 flags = dom_list[i].trust_flags;
449 uint32 type = dom_list[i].trust_type;
450 uint32 attribs = dom_list[i].trust_attribs;
452 d = find_domain_from_name_noinit( dom_list[i].domain_name );
454 /* ignore our primary and internal domains */
456 if ( d && (d->internal || d->primary ) )
457 continue;
459 if ( (flags & DS_DOMAIN_DIRECT_INBOUND) &&
460 (type == DS_DOMAIN_TRUST_TYPE_UPLEVEL) &&
461 (attribs == DS_DOMAIN_TRUST_ATTRIB_FOREST_TRANSITIVE) )
463 /* add the trusted domain if we don't know
464 about it */
466 if ( !d ) {
467 d = add_trusted_domain( dom_list[i].domain_name,
468 dom_list[i].dns_name,
469 &cache_methods,
470 &dom_list[i].sid );
473 DEBUG(10,("Following trust path for domain %s (%s)\n",
474 d->name, d->alt_name ));
475 add_trusted_domains( d );
479 TALLOC_FREE( dom_list );
481 return;
484 /*********************************************************************
485 The process of updating the trusted domain list is a three step
486 async process:
487 (a) ask our domain
488 (b) ask the root domain in our forest
489 (c) ask the a DC in any Win2003 trusted forests
490 *********************************************************************/
492 void rescan_trusted_domains( void )
494 time_t now = time(NULL);
496 /* see if the time has come... */
498 if ((now >= last_trustdom_scan) &&
499 ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
500 return;
502 /* clear the TRUSTDOM cache first */
504 wcache_tdc_clear();
506 /* this will only add new domains we didn't already know about
507 in the domain_list()*/
509 add_trusted_domains( find_our_domain() );
511 last_trustdom_scan = now;
513 return;
516 struct init_child_state {
517 TALLOC_CTX *mem_ctx;
518 struct winbindd_domain *domain;
519 struct winbindd_request *request;
520 struct winbindd_response *response;
521 void (*continuation)(void *private_data, BOOL success);
522 void *private_data;
525 static void init_child_recv(void *private_data, BOOL success);
526 static void init_child_getdc_recv(void *private_data, BOOL success);
528 enum winbindd_result init_child_connection(struct winbindd_domain *domain,
529 void (*continuation)(void *private_data,
530 BOOL success),
531 void *private_data)
533 TALLOC_CTX *mem_ctx;
534 struct winbindd_request *request;
535 struct winbindd_response *response;
536 struct init_child_state *state;
537 struct winbindd_domain *request_domain;
539 mem_ctx = talloc_init("init_child_connection");
540 if (mem_ctx == NULL) {
541 DEBUG(0, ("talloc_init failed\n"));
542 return WINBINDD_ERROR;
545 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
546 response = TALLOC_P(mem_ctx, struct winbindd_response);
547 state = TALLOC_P(mem_ctx, struct init_child_state);
549 if ((request == NULL) || (response == NULL) || (state == NULL)) {
550 DEBUG(0, ("talloc failed\n"));
551 TALLOC_FREE(mem_ctx);
552 continuation(private_data, False);
553 return WINBINDD_ERROR;
556 request->length = sizeof(*request);
558 state->mem_ctx = mem_ctx;
559 state->domain = domain;
560 state->request = request;
561 state->response = response;
562 state->continuation = continuation;
563 state->private_data = private_data;
565 if (IS_DC || domain->primary || domain->internal ) {
566 /* The primary domain has to find the DC name itself */
567 request->cmd = WINBINDD_INIT_CONNECTION;
568 fstrcpy(request->domain_name, domain->name);
569 request->data.init_conn.is_primary = domain->internal ? False : True;
570 fstrcpy(request->data.init_conn.dcname, "");
571 async_request(mem_ctx, &domain->child, request, response,
572 init_child_recv, state);
573 return WINBINDD_PENDING;
576 /* This is *not* the primary domain, let's ask our DC about a DC
577 * name */
579 request->cmd = WINBINDD_GETDCNAME;
580 fstrcpy(request->domain_name, domain->name);
582 request_domain = find_our_domain();
583 async_domain_request(mem_ctx, request_domain, request, response,
584 init_child_getdc_recv, state);
585 return WINBINDD_PENDING;
588 static void init_child_getdc_recv(void *private_data, BOOL success)
590 struct init_child_state *state =
591 talloc_get_type_abort(private_data, struct init_child_state);
592 const char *dcname = "";
594 DEBUG(10, ("Received getdcname response\n"));
596 if (success && (state->response->result == WINBINDD_OK)) {
597 dcname = state->response->data.dc_name;
600 state->request->cmd = WINBINDD_INIT_CONNECTION;
601 fstrcpy(state->request->domain_name, state->domain->name);
602 state->request->data.init_conn.is_primary = False;
603 fstrcpy(state->request->data.init_conn.dcname, dcname);
605 async_request(state->mem_ctx, &state->domain->child,
606 state->request, state->response,
607 init_child_recv, state);
610 static void init_child_recv(void *private_data, BOOL success)
612 struct init_child_state *state =
613 talloc_get_type_abort(private_data, struct init_child_state);
615 DEBUG(5, ("Received child initialization response for domain %s\n",
616 state->domain->name));
618 if ((!success) || (state->response->result != WINBINDD_OK)) {
619 DEBUG(3, ("Could not init child\n"));
620 state->continuation(state->private_data, False);
621 talloc_destroy(state->mem_ctx);
622 return;
625 fstrcpy(state->domain->name,
626 state->response->data.domain_info.name);
627 fstrcpy(state->domain->alt_name,
628 state->response->data.domain_info.alt_name);
629 string_to_sid(&state->domain->sid,
630 state->response->data.domain_info.sid);
631 state->domain->native_mode =
632 state->response->data.domain_info.native_mode;
633 state->domain->active_directory =
634 state->response->data.domain_info.active_directory;
636 init_dc_connection(state->domain);
638 if (state->continuation != NULL)
639 state->continuation(state->private_data, True);
640 talloc_destroy(state->mem_ctx);
643 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
644 struct winbindd_cli_state *state)
646 /* Ensure null termination */
647 state->request.domain_name
648 [sizeof(state->request.domain_name)-1]='\0';
649 state->request.data.init_conn.dcname
650 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
652 if (strlen(state->request.data.init_conn.dcname) > 0) {
653 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
656 init_dc_connection(domain);
658 if (!domain->initialized) {
659 /* If we return error here we can't do any cached authentication,
660 but we may be in disconnected mode and can't initialize correctly.
661 Do what the previous code did and just return without initialization,
662 once we go online we'll re-initialize.
664 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
665 "online = %d\n", domain->name, (int)domain->online ));
668 fstrcpy(state->response.data.domain_info.name, domain->name);
669 fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
670 fstrcpy(state->response.data.domain_info.sid,
671 sid_string_static(&domain->sid));
673 state->response.data.domain_info.native_mode
674 = domain->native_mode;
675 state->response.data.domain_info.active_directory
676 = domain->active_directory;
677 state->response.data.domain_info.primary
678 = domain->primary;
680 return WINBINDD_OK;
683 /* Look up global info for the winbind daemon */
684 BOOL init_domain_list(void)
686 struct winbindd_domain *domain;
687 int role = lp_server_role();
689 /* Free existing list */
690 free_domain_list();
692 /* BUILTIN domain */
694 domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
695 &global_sid_Builtin);
696 if (domain) {
697 setup_domain_child(domain, &domain->child, NULL);
700 /* Local SAM */
702 domain = add_trusted_domain(get_global_sam_name(), NULL,
703 &passdb_methods, get_global_sam_sid());
704 if (domain) {
705 if ( role != ROLE_DOMAIN_MEMBER ) {
706 domain->primary = True;
708 setup_domain_child(domain, &domain->child, NULL);
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, &domain->child, NULL);
727 /* Even in the parent winbindd we'll need to
728 talk to the DC, so try and see if we can
729 contact it. Theoretically this isn't neccessary
730 as the init_dc_connection() in init_child_recv()
731 will do this, but we can start detecting the DC
732 early here. */
733 set_domain_online_request(domain);
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");
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");
899 return domain;
902 /* Find the appropriate domain to lookup a name or SID */
904 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
906 /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
908 if ( sid_check_is_in_unix_groups(sid) ||
909 sid_check_is_unix_groups(sid) ||
910 sid_check_is_in_unix_users(sid) ||
911 sid_check_is_unix_users(sid) )
913 return find_domain_from_sid(get_global_sam_sid());
916 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
917 * one to contact the external DC's. On member servers the internal
918 * domains are different: These are part of the local SAM. */
920 DEBUG(10, ("find_lookup_domain_from_sid(%s)\n",
921 sid_string_static(sid)));
923 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
924 DEBUG(10, ("calling find_domain_from_sid\n"));
925 return find_domain_from_sid(sid);
928 /* On a member server a query for SID or name can always go to our
929 * primary DC. */
931 DEBUG(10, ("calling find_our_domain\n"));
932 return find_our_domain();
935 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
937 if ( strequal(domain_name, unix_users_domain_name() ) ||
938 strequal(domain_name, unix_groups_domain_name() ) )
940 return find_domain_from_name_noinit( get_global_sam_name() );
943 if (IS_DC || strequal(domain_name, "BUILTIN") ||
944 strequal(domain_name, get_global_sam_name()))
945 return find_domain_from_name_noinit(domain_name);
947 /* The "Unix User" and "Unix Group" domain our handled by passdb */
949 return find_our_domain();
952 /* Lookup a sid in a domain from a name */
954 BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
955 enum winbindd_cmd orig_cmd,
956 struct winbindd_domain *domain,
957 const char *domain_name,
958 const char *name, DOM_SID *sid,
959 enum lsa_SidType *type)
961 NTSTATUS result;
963 /* Lookup name */
964 result = domain->methods->name_to_sid(domain, mem_ctx, orig_cmd,
965 domain_name, name, sid, type);
967 /* Return sid and type if lookup successful */
968 if (!NT_STATUS_IS_OK(result)) {
969 *type = SID_NAME_UNKNOWN;
972 return NT_STATUS_IS_OK(result);
976 * @brief Lookup a name in a domain from a sid.
978 * @param sid Security ID you want to look up.
979 * @param name On success, set to the name corresponding to @p sid.
980 * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
981 * @param type On success, contains the type of name: alias, group or
982 * user.
983 * @retval True if the name exists, in which case @p name and @p type
984 * are set, otherwise False.
986 BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
987 struct winbindd_domain *domain,
988 DOM_SID *sid,
989 char **dom_name,
990 char **name,
991 enum lsa_SidType *type)
993 NTSTATUS result;
995 *dom_name = NULL;
996 *name = NULL;
998 /* Lookup name */
1000 result = domain->methods->sid_to_name(domain, mem_ctx, sid, dom_name, name, type);
1002 /* Return name and type if successful */
1004 if (NT_STATUS_IS_OK(result)) {
1005 return True;
1008 *type = SID_NAME_UNKNOWN;
1010 return False;
1013 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
1015 void free_getent_state(struct getent_state *state)
1017 struct getent_state *temp;
1019 /* Iterate over state list */
1021 temp = state;
1023 while(temp != NULL) {
1024 struct getent_state *next;
1026 /* Free sam entries then list entry */
1028 SAFE_FREE(state->sam_entries);
1029 DLIST_REMOVE(state, state);
1030 next = temp->next;
1032 SAFE_FREE(temp);
1033 temp = next;
1037 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1039 static BOOL assume_domain(const char *domain)
1041 /* never assume the domain on a standalone server */
1043 if ( lp_server_role() == ROLE_STANDALONE )
1044 return False;
1046 /* domain member servers may possibly assume for the domain name */
1048 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1049 if ( !strequal(lp_workgroup(), domain) )
1050 return False;
1052 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
1053 return True;
1056 /* only left with a domain controller */
1058 if ( strequal(get_global_sam_name(), domain) ) {
1059 return True;
1062 return False;
1065 /* Parse a string of the form DOMAIN\user into a domain and a user */
1067 BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
1069 char *p = strchr(domuser,*lp_winbind_separator());
1071 if ( !p ) {
1072 fstrcpy(user, domuser);
1074 if ( assume_domain(lp_workgroup())) {
1075 fstrcpy(domain, lp_workgroup());
1076 } else if ((p = strchr(domuser, '@')) != NULL) {
1077 fstrcpy(domain, "");
1078 } else {
1079 return False;
1081 } else {
1082 fstrcpy(user, p+1);
1083 fstrcpy(domain, domuser);
1084 domain[PTR_DIFF(p, domuser)] = 0;
1087 strupper_m(domain);
1089 return True;
1092 BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1093 char **domain, char **user)
1095 fstring fstr_domain, fstr_user;
1096 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1097 return False;
1099 *domain = talloc_strdup(mem_ctx, fstr_domain);
1100 *user = talloc_strdup(mem_ctx, fstr_user);
1101 return ((*domain != NULL) && (*user != NULL));
1104 /* Ensure an incoming username from NSS is fully qualified. Replace the
1105 incoming fstring with DOMAIN <separator> user. Returns the same
1106 values as parse_domain_user() but also replaces the incoming username.
1107 Used to ensure all names are fully qualified within winbindd.
1108 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1109 The protocol definitions of auth_crap, chng_pswd_auth_crap
1110 really should be changed to use this instead of doing things
1111 by hand. JRA. */
1113 BOOL canonicalize_username(fstring username_inout, fstring domain, fstring user)
1115 if (!parse_domain_user(username_inout, domain, user)) {
1116 return False;
1118 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1119 domain, *lp_winbind_separator(),
1120 user);
1121 return True;
1125 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1126 'winbind separator' options.
1127 This means:
1128 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1129 lp_workgroup()
1131 If we are a PDC or BDC, and this is for our domain, do likewise.
1133 Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
1134 username is then unqualified in unix
1136 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1138 void fill_domain_username(fstring name, const char *domain, const char *user, BOOL can_assume)
1140 fstring tmp_user;
1142 fstrcpy(tmp_user, user);
1143 strlower_m(tmp_user);
1145 if (can_assume && assume_domain(domain)) {
1146 strlcpy(name, tmp_user, sizeof(fstring));
1147 } else {
1148 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1149 domain, *lp_winbind_separator(),
1150 tmp_user);
1155 * Winbindd socket accessor functions
1158 const char *get_winbind_pipe_dir(void)
1160 return lp_parm_const_string(-1, "winbindd", "socket dir", WINBINDD_SOCKET_DIR);
1163 char *get_winbind_priv_pipe_dir(void)
1165 return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
1168 /* Open the winbindd socket */
1170 static int _winbindd_socket = -1;
1171 static int _winbindd_priv_socket = -1;
1173 int open_winbindd_socket(void)
1175 if (_winbindd_socket == -1) {
1176 _winbindd_socket = create_pipe_sock(
1177 get_winbind_pipe_dir(), WINBINDD_SOCKET_NAME, 0755);
1178 DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
1179 _winbindd_socket));
1182 return _winbindd_socket;
1185 int open_winbindd_priv_socket(void)
1187 if (_winbindd_priv_socket == -1) {
1188 _winbindd_priv_socket = create_pipe_sock(
1189 get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
1190 DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
1191 _winbindd_priv_socket));
1194 return _winbindd_priv_socket;
1197 /* Close the winbindd socket */
1199 void close_winbindd_socket(void)
1201 if (_winbindd_socket != -1) {
1202 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
1203 _winbindd_socket));
1204 close(_winbindd_socket);
1205 _winbindd_socket = -1;
1207 if (_winbindd_priv_socket != -1) {
1208 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
1209 _winbindd_priv_socket));
1210 close(_winbindd_priv_socket);
1211 _winbindd_priv_socket = -1;
1216 * Client list accessor functions
1219 static struct winbindd_cli_state *_client_list;
1220 static int _num_clients;
1222 /* Return list of all connected clients */
1224 struct winbindd_cli_state *winbindd_client_list(void)
1226 return _client_list;
1229 /* Add a connection to the list */
1231 void winbindd_add_client(struct winbindd_cli_state *cli)
1233 DLIST_ADD(_client_list, cli);
1234 _num_clients++;
1237 /* Remove a client from the list */
1239 void winbindd_remove_client(struct winbindd_cli_state *cli)
1241 DLIST_REMOVE(_client_list, cli);
1242 _num_clients--;
1245 /* Close all open clients */
1247 void winbindd_kill_all_clients(void)
1249 struct winbindd_cli_state *cl = winbindd_client_list();
1251 DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
1253 while (cl) {
1254 struct winbindd_cli_state *next;
1256 next = cl->next;
1257 winbindd_remove_client(cl);
1258 cl = next;
1262 /* Return number of open clients */
1264 int winbindd_num_clients(void)
1266 return _num_clients;
1269 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1270 TALLOC_CTX *mem_ctx,
1271 const DOM_SID *user_sid,
1272 uint32 *p_num_groups, DOM_SID **user_sids)
1274 NET_USER_INFO_3 *info3 = NULL;
1275 NTSTATUS status = NT_STATUS_NO_MEMORY;
1276 int i;
1277 size_t num_groups = 0;
1278 DOM_SID group_sid, primary_group;
1280 DEBUG(3,(": lookup_usergroups_cached\n"));
1282 *user_sids = NULL;
1283 num_groups = 0;
1284 *p_num_groups = 0;
1286 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1288 if (info3 == NULL) {
1289 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1292 if (info3->num_groups == 0) {
1293 TALLOC_FREE(info3);
1294 return NT_STATUS_UNSUCCESSFUL;
1297 /* always add the primary group to the sid array */
1298 sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid);
1300 if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
1301 TALLOC_FREE(info3);
1302 return NT_STATUS_NO_MEMORY;
1305 for (i=0; i<info3->num_groups; i++) {
1306 sid_copy(&group_sid, &info3->dom_sid.sid);
1307 sid_append_rid(&group_sid, info3->gids[i].g_rid);
1309 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
1310 &num_groups)) {
1311 TALLOC_FREE(info3);
1312 return NT_STATUS_NO_MEMORY;
1316 /* Add any Universal groups in the other_sids list */
1318 for (i=0; i<info3->num_other_sids; i++) {
1319 /* Skip Domain local groups outside our domain.
1320 We'll get these from the getsidaliases() RPC call. */
1321 if (info3->other_sids_attrib[i] & SE_GROUP_RESOURCE)
1322 continue;
1324 if (!add_sid_to_array(mem_ctx, &info3->other_sids[i].sid,
1325 user_sids, &num_groups))
1327 TALLOC_FREE(info3);
1328 return NT_STATUS_NO_MEMORY;
1333 TALLOC_FREE(info3);
1334 *p_num_groups = num_groups;
1335 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1337 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1339 return status;
1342 /*********************************************************************
1343 We use this to remove spaces from user and group names
1344 ********************************************************************/
1346 void ws_name_replace( char *name, char replace )
1348 char replace_char[2] = { 0x0, 0x0 };
1350 if ( !lp_winbind_normalize_names() || (replace == '\0') )
1351 return;
1353 replace_char[0] = replace;
1354 all_string_sub( name, " ", replace_char, 0 );
1356 return;
1359 /*********************************************************************
1360 We use this to do the inverse of ws_name_replace()
1361 ********************************************************************/
1363 void ws_name_return( char *name, char replace )
1365 char replace_char[2] = { 0x0, 0x0 };
1367 if ( !lp_winbind_normalize_names() || (replace == '\0') )
1368 return;
1370 replace_char[0] = replace;
1371 all_string_sub( name, replace_char, " ", 0 );
1373 return;
1376 /*********************************************************************
1377 ********************************************************************/
1379 BOOL winbindd_can_contact_domain( struct winbindd_domain *domain )
1381 /* We can contact the domain if it is our primary domain */
1383 if ( domain->primary )
1384 return True;
1386 /* Can always contact a domain that is in out forest */
1388 if ( domain->domain_flags & DS_DOMAIN_IN_FOREST )
1389 return True;
1391 /* We cannot contact the domain if it is running AD and
1392 we have no inbound trust */
1394 if ( domain->active_directory &&
1395 ((domain->domain_flags&DS_DOMAIN_DIRECT_INBOUND) != DS_DOMAIN_DIRECT_INBOUND) )
1397 return False;
1400 /* Assume everything else is ok (probably not true but what
1401 can you do?) */
1403 return True;
1406 /*********************************************************************
1407 ********************************************************************/
1409 BOOL winbindd_internal_child(struct winbindd_child *child)
1411 if ((child == idmap_child()) || (child == locator_child())) {
1412 return True;
1415 return False;
1418 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1420 /*********************************************************************
1421 ********************************************************************/
1423 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1425 char *var = NULL;
1426 const char *kdc = NULL;
1427 int lvl = 11;
1429 if (!domain || !domain->alt_name || !*domain->alt_name) {
1430 return;
1433 if (domain->initialized && !domain->active_directory) {
1434 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1435 domain->alt_name));
1436 return;
1439 kdc = inet_ntoa(domain->dcaddr.sin_addr);
1440 if (!kdc) {
1441 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1442 domain->alt_name));
1443 kdc = domain->dcname;
1446 if (!kdc || !*kdc) {
1447 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1448 domain->alt_name));
1449 return;
1452 if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1453 strupper_static(domain->alt_name)) == -1) {
1454 return;
1457 DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1458 var, kdc));
1460 setenv(var, kdc, 1);
1461 free(var);
1464 /*********************************************************************
1465 ********************************************************************/
1467 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1469 struct winbindd_domain *our_dom = find_our_domain();
1471 winbindd_set_locator_kdc_env(domain);
1473 if (domain != our_dom) {
1474 winbindd_set_locator_kdc_env(our_dom);
1478 /*********************************************************************
1479 ********************************************************************/
1481 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1483 char *var = NULL;
1485 if (!domain || !domain->alt_name || !*domain->alt_name) {
1486 return;
1489 if (asprintf(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1490 strupper_static(domain->alt_name)) == -1) {
1491 return;
1494 unsetenv(var);
1495 free(var);
1497 #else
1499 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1501 return;
1504 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1506 return;
1509 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */