s3: winbindd: Move the logic of whether to set 'domain->primary' into add_trusted_dom...
[Samba/wip.git] / source3 / winbindd / winbindd_util.c
blob10ca13222b4e8cdb22d6a577de03f29d68b0842c
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"
25 #include "secrets.h"
26 #include "../libcli/security/security.h"
27 #include "../libcli/auth/pam_errors.h"
28 #include "passdb/machine_sid.h"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 extern struct winbindd_methods cache_methods;
35 /**
36 * @file winbindd_util.cq
38 * Winbind daemon for NT domain authentication nss module.
39 **/
42 /* The list of trusted domains. Note that the list can be deleted and
43 recreated using the init_domain_list() function so pointers to
44 individual winbindd_domain structures cannot be made. Keep a copy of
45 the domain name instead. */
47 static struct winbindd_domain *_domain_list = NULL;
49 struct winbindd_domain *domain_list(void)
51 /* Initialise list */
53 if ((!_domain_list) && (!init_domain_list())) {
54 smb_panic("Init_domain_list failed");
57 return _domain_list;
60 /* Free all entries in the trusted domain list */
62 static void free_domain_list(void)
64 struct winbindd_domain *domain = _domain_list;
66 while(domain) {
67 struct winbindd_domain *next = domain->next;
69 DLIST_REMOVE(_domain_list, domain);
70 TALLOC_FREE(domain);
71 domain = next;
75 static bool is_internal_domain(const struct dom_sid *sid)
77 if (sid == NULL)
78 return False;
80 return (sid_check_is_our_sam(sid) || sid_check_is_builtin(sid));
83 static bool is_in_internal_domain(const struct dom_sid *sid)
85 if (sid == NULL)
86 return False;
88 return (sid_check_is_in_our_sam(sid) || sid_check_is_in_builtin(sid));
92 /* Add a trusted domain to our list of domains */
93 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
94 struct winbindd_methods *methods,
95 const struct dom_sid *sid)
97 struct winbindd_domain *domain;
98 const char *alternative_name = NULL;
99 char *idmap_config_option;
100 const char *param;
101 const char **ignored_domains, **dom;
102 int role = lp_server_role();
104 ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
105 for (dom=ignored_domains; dom && *dom; dom++) {
106 if (gen_fnmatch(*dom, domain_name) == 0) {
107 DEBUG(2,("Ignoring domain '%s'\n", domain_name));
108 return NULL;
112 /* use alt_name if available to allow DNS lookups */
114 if (alt_name && *alt_name) {
115 alternative_name = alt_name;
118 /* We can't call domain_list() as this function is called from
119 init_domain_list() and we'll get stuck in a loop. */
120 for (domain = _domain_list; domain; domain = domain->next) {
121 if (strequal(domain_name, domain->name) ||
122 strequal(domain_name, domain->alt_name))
124 break;
127 if (alternative_name && *alternative_name)
129 if (strequal(alternative_name, domain->name) ||
130 strequal(alternative_name, domain->alt_name))
132 break;
136 if (sid)
138 if (is_null_sid(sid)) {
139 continue;
142 if (dom_sid_equal(sid, &domain->sid)) {
143 break;
148 if (domain != NULL) {
150 * We found a match. Possibly update the SID
152 if ((sid != NULL)
153 && dom_sid_equal(&domain->sid, &global_sid_NULL)) {
154 sid_copy( &domain->sid, sid );
156 return domain;
159 /* Create new domain entry */
160 domain = talloc_zero(NULL, struct winbindd_domain);
161 if (domain == NULL) {
162 return NULL;
165 domain->children = talloc_zero_array(domain,
166 struct winbindd_child,
167 lp_winbind_max_domain_connections());
168 if (domain->children == NULL) {
169 TALLOC_FREE(domain);
170 return NULL;
173 domain->name = talloc_strdup(domain, domain_name);
174 if (domain->name == NULL) {
175 TALLOC_FREE(domain);
176 return NULL;
179 if (alternative_name) {
180 domain->alt_name = talloc_strdup(domain, alternative_name);
181 if (domain->alt_name == NULL) {
182 TALLOC_FREE(domain);
183 return NULL;
187 domain->methods = methods;
188 domain->backend = NULL;
189 domain->internal = is_internal_domain(sid);
190 domain->sequence_number = DOM_SEQUENCE_NONE;
191 domain->last_seq_check = 0;
192 domain->initialized = False;
193 domain->online = is_internal_domain(sid);
194 domain->check_online_timeout = 0;
195 domain->dc_probe_pid = (pid_t)-1;
196 if (sid) {
197 sid_copy(&domain->sid, sid);
200 /* Is this our primary domain ? */
201 if (strequal(domain_name, get_global_sam_name()) &&
202 (role != ROLE_DOMAIN_MEMBER)) {
203 domain->primary = true;
204 } else if (strequal(domain_name, lp_workgroup()) &&
205 (role == ROLE_DOMAIN_MEMBER)) {
206 domain->primary = true;
209 /* Link to domain list */
210 DLIST_ADD_END(_domain_list, domain, struct winbindd_domain *);
212 wcache_tdc_add_domain( domain );
214 idmap_config_option = talloc_asprintf(talloc_tos(), "idmap config %s",
215 domain->name);
216 if (idmap_config_option == NULL) {
217 DEBUG(0, ("talloc failed, not looking for idmap config\n"));
218 goto done;
221 param = lp_parm_const_string(-1, idmap_config_option, "range", NULL);
223 DEBUG(10, ("%s : range = %s\n", idmap_config_option,
224 param ? param : "not defined"));
226 if (param != NULL) {
227 unsigned low_id, high_id;
228 if (sscanf(param, "%u - %u", &low_id, &high_id) != 2) {
229 DEBUG(1, ("invalid range syntax in %s: %s\n",
230 idmap_config_option, param));
231 goto done;
233 if (low_id > high_id) {
234 DEBUG(1, ("invalid range in %s: %s\n",
235 idmap_config_option, param));
236 goto done;
238 domain->have_idmap_config = true;
239 domain->id_range_low = low_id;
240 domain->id_range_high = high_id;
243 done:
245 DEBUG(2,("Added domain %s %s %s\n",
246 domain->name, domain->alt_name,
247 &domain->sid?sid_string_dbg(&domain->sid):""));
249 return domain;
252 bool domain_is_forest_root(const struct winbindd_domain *domain)
254 const uint32_t fr_flags =
255 (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
257 return ((domain->domain_flags & fr_flags) == fr_flags);
260 /********************************************************************
261 rescan our domains looking for new trusted domains
262 ********************************************************************/
264 struct trustdom_state {
265 struct winbindd_domain *domain;
266 struct winbindd_request request;
269 static void trustdom_list_done(struct tevent_req *req);
270 static void rescan_forest_root_trusts( void );
271 static void rescan_forest_trusts( void );
273 static void add_trusted_domains( struct winbindd_domain *domain )
275 struct trustdom_state *state;
276 struct tevent_req *req;
278 state = talloc_zero(NULL, struct trustdom_state);
279 if (state == NULL) {
280 DEBUG(0, ("talloc failed\n"));
281 return;
283 state->domain = domain;
285 state->request.length = sizeof(state->request);
286 state->request.cmd = WINBINDD_LIST_TRUSTDOM;
288 req = wb_domain_request_send(state, winbind_event_context(),
289 domain, &state->request);
290 if (req == NULL) {
291 DEBUG(1, ("wb_domain_request_send failed\n"));
292 TALLOC_FREE(state);
293 return;
295 tevent_req_set_callback(req, trustdom_list_done, state);
298 static void trustdom_list_done(struct tevent_req *req)
300 struct trustdom_state *state = tevent_req_callback_data(
301 req, struct trustdom_state);
302 struct winbindd_response *response;
303 int res, err;
304 char *p;
306 res = wb_domain_request_recv(req, state, &response, &err);
307 if ((res == -1) || (response->result != WINBINDD_OK)) {
308 DEBUG(1, ("Could not receive trustdoms\n"));
309 TALLOC_FREE(state);
310 return;
313 p = (char *)response->extra_data.data;
315 while ((p != NULL) && (*p != '\0')) {
316 char *q, *sidstr, *alt_name;
317 struct dom_sid sid;
318 struct winbindd_domain *domain;
319 char *alternate_name = NULL;
320 bool domain_exists;
322 alt_name = strchr(p, '\\');
323 if (alt_name == NULL) {
324 DEBUG(0, ("Got invalid trustdom response\n"));
325 break;
328 *alt_name = '\0';
329 alt_name += 1;
331 sidstr = strchr(alt_name, '\\');
332 if (sidstr == NULL) {
333 DEBUG(0, ("Got invalid trustdom response\n"));
334 break;
337 *sidstr = '\0';
338 sidstr += 1;
340 q = strchr(sidstr, '\n');
341 if (q != NULL)
342 *q = '\0';
344 if (!string_to_sid(&sid, sidstr)) {
345 DEBUG(0, ("Got invalid trustdom response\n"));
346 break;
349 /* use the real alt_name if we have one, else pass in NULL */
351 if ( !strequal( alt_name, "(null)" ) )
352 alternate_name = alt_name;
354 /* Check if we already have a child for the domain */
355 domain_exists = (find_domain_from_name_noinit(p) != NULL);
358 * We always call add_trusted_domain() cause on an existing
359 * domain structure, it will update the SID if necessary.
360 * This is important because we need the SID for sibling
361 * domains.
363 domain = add_trusted_domain(p, alternate_name,
364 &cache_methods,
365 &sid);
368 * If the domain doesn't exist yet and got correctly added,
369 * setup a new domain child.
371 if (!domain_exists && domain != NULL) {
372 setup_domain_child(domain);
374 p=q;
375 if (p != NULL)
376 p += 1;
380 Cases to consider when scanning trusts:
381 (a) we are calling from a child domain (primary && !forest_root)
382 (b) we are calling from the root of the forest (primary && forest_root)
383 (c) we are calling from a trusted forest domain (!primary
384 && !forest_root)
387 if (state->domain->primary) {
388 /* If this is our primary domain and we are not in the
389 forest root, we have to scan the root trusts first */
391 if (!domain_is_forest_root(state->domain))
392 rescan_forest_root_trusts();
393 else
394 rescan_forest_trusts();
396 } else if (domain_is_forest_root(state->domain)) {
397 /* Once we have done root forest trust search, we can
398 go on to search the trusted forests */
400 rescan_forest_trusts();
403 TALLOC_FREE(state);
405 return;
408 /********************************************************************
409 Scan the trusts of our forest root
410 ********************************************************************/
412 static void rescan_forest_root_trusts( void )
414 struct winbindd_tdc_domain *dom_list = NULL;
415 size_t num_trusts = 0;
416 int i;
418 /* The only transitive trusts supported by Windows 2003 AD are
419 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
420 first two are handled in forest and listed by
421 DsEnumerateDomainTrusts(). Forest trusts are not so we
422 have to do that ourselves. */
424 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
425 return;
427 for ( i=0; i<num_trusts; i++ ) {
428 struct winbindd_domain *d = NULL;
430 /* Find the forest root. Don't necessarily trust
431 the domain_list() as our primary domain may not
432 have been initialized. */
434 if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
435 continue;
438 /* Here's the forest root */
440 d = find_domain_from_name_noinit( dom_list[i].domain_name );
442 if ( !d ) {
443 d = add_trusted_domain( dom_list[i].domain_name,
444 dom_list[i].dns_name,
445 &cache_methods,
446 &dom_list[i].sid );
447 if (d != NULL) {
448 setup_domain_child(d);
452 if (d == NULL) {
453 continue;
456 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
457 "for domain tree root %s (%s)\n",
458 d->name, d->alt_name ));
460 d->domain_flags = dom_list[i].trust_flags;
461 d->domain_type = dom_list[i].trust_type;
462 d->domain_trust_attribs = dom_list[i].trust_attribs;
464 add_trusted_domains( d );
466 break;
469 TALLOC_FREE( dom_list );
471 return;
474 /********************************************************************
475 scan the transitive forest trusts (not our own)
476 ********************************************************************/
479 static void rescan_forest_trusts( void )
481 struct winbindd_domain *d = NULL;
482 struct winbindd_tdc_domain *dom_list = NULL;
483 size_t num_trusts = 0;
484 int i;
486 /* The only transitive trusts supported by Windows 2003 AD are
487 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
488 first two are handled in forest and listed by
489 DsEnumerateDomainTrusts(). Forest trusts are not so we
490 have to do that ourselves. */
492 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
493 return;
495 for ( i=0; i<num_trusts; i++ ) {
496 uint32 flags = dom_list[i].trust_flags;
497 uint32 type = dom_list[i].trust_type;
498 uint32 attribs = dom_list[i].trust_attribs;
500 d = find_domain_from_name_noinit( dom_list[i].domain_name );
502 /* ignore our primary and internal domains */
504 if ( d && (d->internal || d->primary ) )
505 continue;
507 if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
508 (type == NETR_TRUST_TYPE_UPLEVEL) &&
509 (attribs == NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
511 /* add the trusted domain if we don't know
512 about it */
514 if ( !d ) {
515 d = add_trusted_domain( dom_list[i].domain_name,
516 dom_list[i].dns_name,
517 &cache_methods,
518 &dom_list[i].sid );
519 if (d != NULL) {
520 setup_domain_child(d);
524 if (d == NULL) {
525 continue;
528 DEBUG(10,("Following trust path for domain %s (%s)\n",
529 d->name, d->alt_name ));
530 add_trusted_domains( d );
534 TALLOC_FREE( dom_list );
536 return;
539 /*********************************************************************
540 The process of updating the trusted domain list is a three step
541 async process:
542 (a) ask our domain
543 (b) ask the root domain in our forest
544 (c) ask the a DC in any Win2003 trusted forests
545 *********************************************************************/
547 void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
548 struct timeval now, void *private_data)
550 TALLOC_FREE(te);
552 /* I use to clear the cache here and start over but that
553 caused problems in child processes that needed the
554 trust dom list early on. Removing it means we
555 could have some trusted domains listed that have been
556 removed from our primary domain's DC until a full
557 restart. This should be ok since I think this is what
558 Windows does as well. */
560 /* this will only add new domains we didn't already know about
561 in the domain_list()*/
563 add_trusted_domains( find_our_domain() );
565 te = tevent_add_timer(
566 ev, NULL, timeval_current_ofs(WINBINDD_RESCAN_FREQ, 0),
567 rescan_trusted_domains, NULL);
569 * If te == NULL, there's not much we can do here. Don't fail, the
570 * only thing we miss is new trusted domains.
573 return;
576 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
577 struct winbindd_cli_state *state)
579 /* Ensure null termination */
580 state->request->domain_name
581 [sizeof(state->request->domain_name)-1]='\0';
582 state->request->data.init_conn.dcname
583 [sizeof(state->request->data.init_conn.dcname)-1]='\0';
585 if (strlen(state->request->data.init_conn.dcname) > 0) {
586 fstrcpy(domain->dcname, state->request->data.init_conn.dcname);
589 if (domain->internal) {
590 domain->initialized = true;
591 } else {
592 init_dc_connection(domain);
595 if (!domain->initialized) {
596 /* If we return error here we can't do any cached authentication,
597 but we may be in disconnected mode and can't initialize correctly.
598 Do what the previous code did and just return without initialization,
599 once we go online we'll re-initialize.
601 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
602 "online = %d\n", domain->name, (int)domain->online ));
605 fstrcpy(state->response->data.domain_info.name, domain->name);
606 fstrcpy(state->response->data.domain_info.alt_name, domain->alt_name);
607 sid_to_fstring(state->response->data.domain_info.sid, &domain->sid);
609 state->response->data.domain_info.native_mode
610 = domain->native_mode;
611 state->response->data.domain_info.active_directory
612 = domain->active_directory;
613 state->response->data.domain_info.primary
614 = domain->primary;
616 return WINBINDD_OK;
619 /* Look up global info for the winbind daemon */
620 bool init_domain_list(void)
622 struct winbindd_domain *domain;
623 int role = lp_server_role();
625 /* Free existing list */
626 free_domain_list();
628 /* BUILTIN domain */
630 domain = add_trusted_domain("BUILTIN", NULL, &cache_methods,
631 &global_sid_Builtin);
632 if (domain) {
633 setup_domain_child(domain);
636 /* Local SAM */
638 domain = add_trusted_domain(get_global_sam_name(), NULL,
639 &cache_methods, get_global_sam_sid());
640 if (domain) {
641 setup_domain_child(domain);
644 /* Add ourselves as the first entry. */
646 if ( role == ROLE_DOMAIN_MEMBER ) {
647 struct dom_sid our_sid;
649 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
650 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
651 return False;
654 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
655 &cache_methods, &our_sid);
656 if (domain) {
657 setup_domain_child(domain);
659 /* Even in the parent winbindd we'll need to
660 talk to the DC, so try and see if we can
661 contact it. Theoretically this isn't neccessary
662 as the init_dc_connection() in init_child_recv()
663 will do this, but we can start detecting the DC
664 early here. */
665 set_domain_online_request(domain);
669 return True;
673 * Given a domain name, return the struct winbindd domain info for it
675 * @note Do *not* pass lp_workgroup() to this function. domain_list
676 * may modify it's value, and free that pointer. Instead, our local
677 * domain may be found by calling find_our_domain().
678 * directly.
681 * @return The domain structure for the named domain, if it is working.
684 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
686 struct winbindd_domain *domain;
688 /* Search through list */
690 for (domain = domain_list(); domain != NULL; domain = domain->next) {
691 if (strequal(domain_name, domain->name) ||
692 (domain->alt_name != NULL &&
693 strequal(domain_name, domain->alt_name))) {
694 return domain;
698 /* Not found */
700 return NULL;
703 struct winbindd_domain *find_domain_from_name(const char *domain_name)
705 struct winbindd_domain *domain;
707 domain = find_domain_from_name_noinit(domain_name);
709 if (domain == NULL)
710 return NULL;
712 if (!domain->initialized)
713 init_dc_connection(domain);
715 return domain;
718 /* Given a domain sid, return the struct winbindd domain info for it */
720 struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid)
722 struct winbindd_domain *domain;
724 /* Search through list */
726 for (domain = domain_list(); domain != NULL; domain = domain->next) {
727 if (dom_sid_compare_domain(sid, &domain->sid) == 0)
728 return domain;
731 /* Not found */
733 return NULL;
736 /* Given a domain sid, return the struct winbindd domain info for it */
738 struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid)
740 struct winbindd_domain *domain;
742 domain = find_domain_from_sid_noinit(sid);
744 if (domain == NULL)
745 return NULL;
747 if (!domain->initialized)
748 init_dc_connection(domain);
750 return domain;
753 struct winbindd_domain *find_our_domain(void)
755 struct winbindd_domain *domain;
757 /* Search through list */
759 for (domain = domain_list(); domain != NULL; domain = domain->next) {
760 if (domain->primary)
761 return domain;
764 smb_panic("Could not find our domain");
765 return NULL;
768 struct winbindd_domain *find_root_domain(void)
770 struct winbindd_domain *ours = find_our_domain();
772 if (ours->forest_name == NULL) {
773 return NULL;
776 return find_domain_from_name( ours->forest_name );
779 struct winbindd_domain *find_builtin_domain(void)
781 struct winbindd_domain *domain;
783 domain = find_domain_from_sid(&global_sid_Builtin);
784 if (domain == NULL) {
785 smb_panic("Could not find BUILTIN domain");
788 return domain;
791 /* Find the appropriate domain to lookup a name or SID */
793 struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid)
795 /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
797 if ( sid_check_is_in_unix_groups(sid) ||
798 sid_check_is_unix_groups(sid) ||
799 sid_check_is_in_unix_users(sid) ||
800 sid_check_is_unix_users(sid) )
802 return find_domain_from_sid(get_global_sam_sid());
805 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
806 * one to contact the external DC's. On member servers the internal
807 * domains are different: These are part of the local SAM. */
809 DEBUG(10, ("find_lookup_domain_from_sid(%s)\n", sid_string_dbg(sid)));
811 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
812 DEBUG(10, ("calling find_domain_from_sid\n"));
813 return find_domain_from_sid(sid);
816 /* On a member server a query for SID or name can always go to our
817 * primary DC. */
819 DEBUG(10, ("calling find_our_domain\n"));
820 return find_our_domain();
823 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
825 if ( strequal(domain_name, unix_users_domain_name() ) ||
826 strequal(domain_name, unix_groups_domain_name() ) )
829 * The "Unix User" and "Unix Group" domain our handled by
830 * passdb
832 return find_domain_from_name_noinit( get_global_sam_name() );
835 if (IS_DC || strequal(domain_name, "BUILTIN") ||
836 strequal(domain_name, get_global_sam_name()))
837 return find_domain_from_name_noinit(domain_name);
840 return find_our_domain();
843 /* Is this a domain which we may assume no DOMAIN\ prefix? */
845 static bool assume_domain(const char *domain)
847 /* never assume the domain on a standalone server */
849 if ( lp_server_role() == ROLE_STANDALONE )
850 return False;
852 /* domain member servers may possibly assume for the domain name */
854 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
855 if ( !strequal(lp_workgroup(), domain) )
856 return False;
858 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
859 return True;
862 /* only left with a domain controller */
864 if ( strequal(get_global_sam_name(), domain) ) {
865 return True;
868 return False;
871 /* Parse a string of the form DOMAIN\user into a domain and a user */
873 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
875 char *p = strchr(domuser,*lp_winbind_separator());
877 if ( !p ) {
878 fstrcpy(user, domuser);
880 if ( assume_domain(lp_workgroup())) {
881 fstrcpy(domain, lp_workgroup());
882 } else if ((p = strchr(domuser, '@')) != NULL) {
883 fstrcpy(domain, p + 1);
884 user[PTR_DIFF(p, domuser)] = 0;
885 } else {
886 return False;
888 } else {
889 fstrcpy(user, p+1);
890 fstrcpy(domain, domuser);
891 domain[PTR_DIFF(p, domuser)] = 0;
894 return strupper_m(domain);
897 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
898 char **domain, char **user)
900 fstring fstr_domain, fstr_user;
901 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
902 return False;
904 *domain = talloc_strdup(mem_ctx, fstr_domain);
905 *user = talloc_strdup(mem_ctx, fstr_user);
906 return ((*domain != NULL) && (*user != NULL));
909 /* Ensure an incoming username from NSS is fully qualified. Replace the
910 incoming fstring with DOMAIN <separator> user. Returns the same
911 values as parse_domain_user() but also replaces the incoming username.
912 Used to ensure all names are fully qualified within winbindd.
913 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
914 The protocol definitions of auth_crap, chng_pswd_auth_crap
915 really should be changed to use this instead of doing things
916 by hand. JRA. */
918 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
920 if (!parse_domain_user(username_inout, domain, user)) {
921 return False;
923 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
924 domain, *lp_winbind_separator(),
925 user);
926 return True;
930 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
931 'winbind separator' options.
932 This means:
933 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
934 lp_workgroup()
936 If we are a PDC or BDC, and this is for our domain, do likewise.
938 Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
939 username is then unqualified in unix
941 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
943 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
945 fstring tmp_user;
947 fstrcpy(tmp_user, user);
948 (void)strlower_m(tmp_user);
950 if (can_assume && assume_domain(domain)) {
951 strlcpy(name, tmp_user, sizeof(fstring));
952 } else {
953 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
954 domain, *lp_winbind_separator(),
955 tmp_user);
960 * talloc version of fill_domain_username()
961 * return NULL on talloc failure.
963 char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
964 const char *domain,
965 const char *user,
966 bool can_assume)
968 char *tmp_user, *name;
970 tmp_user = talloc_strdup(mem_ctx, user);
971 if (!strlower_m(tmp_user)) {
972 TALLOC_FREE(tmp_user);
973 return NULL;
976 if (can_assume && assume_domain(domain)) {
977 name = tmp_user;
978 } else {
979 name = talloc_asprintf(mem_ctx, "%s%c%s",
980 domain,
981 *lp_winbind_separator(),
982 tmp_user);
983 TALLOC_FREE(tmp_user);
986 return name;
990 * Client list accessor functions
993 static struct winbindd_cli_state *_client_list;
994 static int _num_clients;
996 /* Return list of all connected clients */
998 struct winbindd_cli_state *winbindd_client_list(void)
1000 return _client_list;
1003 /* Add a connection to the list */
1005 void winbindd_add_client(struct winbindd_cli_state *cli)
1007 DLIST_ADD(_client_list, cli);
1008 _num_clients++;
1011 /* Remove a client from the list */
1013 void winbindd_remove_client(struct winbindd_cli_state *cli)
1015 DLIST_REMOVE(_client_list, cli);
1016 _num_clients--;
1019 /* Return number of open clients */
1021 int winbindd_num_clients(void)
1023 return _num_clients;
1026 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1027 TALLOC_CTX *mem_ctx,
1028 const struct dom_sid *user_sid,
1029 uint32_t *p_num_groups, struct dom_sid **user_sids)
1031 struct netr_SamInfo3 *info3 = NULL;
1032 NTSTATUS status = NT_STATUS_NO_MEMORY;
1033 uint32_t num_groups = 0;
1035 DEBUG(3,(": lookup_usergroups_cached\n"));
1037 *user_sids = NULL;
1038 *p_num_groups = 0;
1040 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1042 if (info3 == NULL) {
1043 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1046 if (info3->base.groups.count == 0) {
1047 TALLOC_FREE(info3);
1048 return NT_STATUS_UNSUCCESSFUL;
1052 * Before bug #7843 the "Domain Local" groups were added with a
1053 * lookupuseraliases call, but this isn't done anymore for our domain
1054 * so we need to resolve resource groups here.
1056 * When to use Resource Groups:
1057 * http://technet.microsoft.com/en-us/library/cc753670%28v=WS.10%29.aspx
1059 status = sid_array_from_info3(mem_ctx, info3,
1060 user_sids,
1061 &num_groups,
1062 false);
1064 if (!NT_STATUS_IS_OK(status)) {
1065 TALLOC_FREE(info3);
1066 return status;
1069 TALLOC_FREE(info3);
1070 *p_num_groups = num_groups;
1071 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1073 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1075 return status;
1078 /*********************************************************************
1079 We use this to remove spaces from user and group names
1080 ********************************************************************/
1082 NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
1083 struct winbindd_domain *domain,
1084 const char *name,
1085 char **normalized)
1087 NTSTATUS nt_status;
1089 if (!name || !normalized) {
1090 return NT_STATUS_INVALID_PARAMETER;
1093 if (!lp_winbind_normalize_names()) {
1094 return NT_STATUS_PROCEDURE_NOT_FOUND;
1097 /* Alias support and whitespace replacement are mutually
1098 exclusive */
1100 nt_status = resolve_username_to_alias(mem_ctx, domain,
1101 name, normalized );
1102 if (NT_STATUS_IS_OK(nt_status)) {
1103 /* special return code to let the caller know we
1104 mapped to an alias */
1105 return NT_STATUS_FILE_RENAMED;
1108 /* check for an unreachable domain */
1110 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1111 DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
1112 domain->name));
1113 set_domain_offline(domain);
1114 return nt_status;
1117 /* deal with whitespace */
1119 *normalized = talloc_strdup(mem_ctx, name);
1120 if (!(*normalized)) {
1121 return NT_STATUS_NO_MEMORY;
1124 all_string_sub( *normalized, " ", "_", 0 );
1126 return NT_STATUS_OK;
1129 /*********************************************************************
1130 We use this to do the inverse of normalize_name_map()
1131 ********************************************************************/
1133 NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
1134 char *name,
1135 char **normalized)
1137 NTSTATUS nt_status;
1138 struct winbindd_domain *domain = find_our_domain();
1140 if (!name || !normalized) {
1141 return NT_STATUS_INVALID_PARAMETER;
1144 if (!lp_winbind_normalize_names()) {
1145 return NT_STATUS_PROCEDURE_NOT_FOUND;
1148 /* Alias support and whitespace replacement are mutally
1149 exclusive */
1151 /* When mapping from an alias to a username, we don't know the
1152 domain. But we only need a domain structure to cache
1153 a successful lookup , so just our own domain structure for
1154 the seqnum. */
1156 nt_status = resolve_alias_to_username(mem_ctx, domain,
1157 name, normalized);
1158 if (NT_STATUS_IS_OK(nt_status)) {
1159 /* Special return code to let the caller know we mapped
1160 from an alias */
1161 return NT_STATUS_FILE_RENAMED;
1164 /* check for an unreachable domain */
1166 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1167 DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
1168 domain->name));
1169 set_domain_offline(domain);
1170 return nt_status;
1173 /* deal with whitespace */
1175 *normalized = talloc_strdup(mem_ctx, name);
1176 if (!(*normalized)) {
1177 return NT_STATUS_NO_MEMORY;
1180 all_string_sub(*normalized, "_", " ", 0);
1182 return NT_STATUS_OK;
1185 /*********************************************************************
1186 ********************************************************************/
1188 bool winbindd_can_contact_domain(struct winbindd_domain *domain)
1190 struct winbindd_tdc_domain *tdc = NULL;
1191 TALLOC_CTX *frame = talloc_stackframe();
1192 bool ret = false;
1194 /* We can contact the domain if it is our primary domain */
1196 if (domain->primary) {
1197 ret = true;
1198 goto done;
1201 /* Trust the TDC cache and not the winbindd_domain flags */
1203 if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
1204 DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
1205 domain->name));
1206 ret = false;
1207 goto done;
1210 /* Can always contact a domain that is in out forest */
1212 if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
1213 ret = true;
1214 goto done;
1218 * On a _member_ server, we cannot contact the domain if it
1219 * is running AD and we have no inbound trust.
1222 if (!IS_DC &&
1223 domain->active_directory &&
1224 ((tdc->trust_flags & NETR_TRUST_FLAG_INBOUND) != NETR_TRUST_FLAG_INBOUND))
1226 DEBUG(10, ("winbindd_can_contact_domain: %s is an AD domain "
1227 "and we have no inbound trust.\n", domain->name));
1228 goto done;
1231 /* Assume everything else is ok (probably not true but what
1232 can you do?) */
1234 ret = true;
1236 done:
1237 talloc_destroy(frame);
1239 return ret;
1242 /*********************************************************************
1243 ********************************************************************/
1245 bool winbindd_internal_child(struct winbindd_child *child)
1247 if ((child == idmap_child()) || (child == locator_child())) {
1248 return True;
1251 return False;
1254 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1256 /*********************************************************************
1257 ********************************************************************/
1259 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1261 char *var = NULL;
1262 char addr[INET6_ADDRSTRLEN];
1263 const char *kdc = NULL;
1264 int lvl = 11;
1266 if (!domain || !domain->alt_name || !*domain->alt_name) {
1267 return;
1270 if (domain->initialized && !domain->active_directory) {
1271 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1272 domain->alt_name));
1273 return;
1276 print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
1277 kdc = addr;
1278 if (!*kdc) {
1279 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1280 domain->alt_name));
1281 kdc = domain->dcname;
1284 if (!kdc || !*kdc) {
1285 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1286 domain->alt_name));
1287 return;
1290 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1291 domain->alt_name) == -1) {
1292 return;
1295 DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1296 var, kdc));
1298 setenv(var, kdc, 1);
1299 free(var);
1302 /*********************************************************************
1303 ********************************************************************/
1305 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1307 struct winbindd_domain *our_dom = find_our_domain();
1309 winbindd_set_locator_kdc_env(domain);
1311 if (domain != our_dom) {
1312 winbindd_set_locator_kdc_env(our_dom);
1316 /*********************************************************************
1317 ********************************************************************/
1319 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1321 char *var = NULL;
1323 if (!domain || !domain->alt_name || !*domain->alt_name) {
1324 return;
1327 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1328 domain->alt_name) == -1) {
1329 return;
1332 unsetenv(var);
1333 free(var);
1335 #else
1337 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1339 return;
1342 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1344 return;
1347 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */
1349 void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
1351 resp->data.auth.nt_status = NT_STATUS_V(result);
1352 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
1354 /* we might have given a more useful error above */
1355 if (*resp->data.auth.error_string == '\0')
1356 fstrcpy(resp->data.auth.error_string,
1357 get_friendly_nt_error_msg(result));
1358 resp->data.auth.pam_error = nt_status_to_pam(result);
1361 bool is_domain_offline(const struct winbindd_domain *domain)
1363 if (!lp_winbind_offline_logon()) {
1364 return false;
1366 if (get_global_winbindd_state_offline()) {
1367 return true;
1369 return !domain->online;
1372 bool is_domain_online(const struct winbindd_domain *domain)
1374 return !is_domain_offline(domain);
1378 * Parse an char array into a list of sids.
1380 * The input sidstr should consist of 0-terminated strings
1381 * representing sids, separated by newline characters '\n'.
1382 * The list is terminated by an empty string, i.e.
1383 * character '\0' directly following a character '\n'
1384 * (or '\0' right at the start of sidstr).
1386 bool parse_sidlist(TALLOC_CTX *mem_ctx, const char *sidstr,
1387 struct dom_sid **sids, uint32_t *num_sids)
1389 const char *p;
1391 p = sidstr;
1392 if (p == NULL)
1393 return False;
1395 while (p[0] != '\0') {
1396 struct dom_sid sid;
1397 const char *q = NULL;
1399 if (!dom_sid_parse_endp(p, &sid, &q)) {
1400 DEBUG(1, ("Could not parse sid %s\n", p));
1401 return false;
1403 if ((q == NULL) || (q[0] != '\n')) {
1404 DEBUG(1, ("Got invalid sidstr: %s\n", p));
1405 return false;
1407 if (!NT_STATUS_IS_OK(add_sid_to_array(mem_ctx, &sid, sids,
1408 num_sids)))
1410 return False;
1412 p = q+1;
1414 return True;