upgradeprovision: do not try to remove/change attribute before the RID Set object...
[Samba/vl.git] / source3 / winbindd / winbindd_util.c
blobbd1e1b5bee9e621a951fa66c909e1c42bb7a7ad6
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"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 extern struct winbindd_methods cache_methods;
32 /**
33 * @file winbindd_util.c
35 * Winbind daemon for NT domain authentication nss module.
36 **/
39 /* The list of trusted domains. Note that the list can be deleted and
40 recreated using the init_domain_list() function so pointers to
41 individual winbindd_domain structures cannot be made. Keep a copy of
42 the domain name instead. */
44 static struct winbindd_domain *_domain_list = NULL;
46 struct winbindd_domain *domain_list(void)
48 /* Initialise list */
50 if ((!_domain_list) && (!init_domain_list())) {
51 smb_panic("Init_domain_list failed");
54 return _domain_list;
57 /* Free all entries in the trusted domain list */
59 static void free_domain_list(void)
61 struct winbindd_domain *domain = _domain_list;
63 while(domain) {
64 struct winbindd_domain *next = domain->next;
66 DLIST_REMOVE(_domain_list, domain);
67 SAFE_FREE(domain);
68 domain = next;
72 static bool is_internal_domain(const struct dom_sid *sid)
74 if (sid == NULL)
75 return False;
77 return (sid_check_is_domain(sid) || sid_check_is_builtin(sid));
80 static bool is_in_internal_domain(const struct dom_sid *sid)
82 if (sid == NULL)
83 return False;
85 return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid));
89 /* Add a trusted domain to our list of domains */
90 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
91 struct winbindd_methods *methods,
92 const struct dom_sid *sid)
94 struct winbindd_domain *domain;
95 const char *alternative_name = NULL;
96 char *idmap_config_option;
97 const char *param;
98 const char **ignored_domains, **dom;
100 ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
101 for (dom=ignored_domains; dom && *dom; dom++) {
102 if (gen_fnmatch(*dom, domain_name) == 0) {
103 DEBUG(2,("Ignoring domain '%s'\n", domain_name));
104 return NULL;
108 /* ignore alt_name if we are not in an AD domain */
110 if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
111 alternative_name = alt_name;
114 /* We can't call domain_list() as this function is called from
115 init_domain_list() and we'll get stuck in a loop. */
116 for (domain = _domain_list; domain; domain = domain->next) {
117 if (strequal(domain_name, domain->name) ||
118 strequal(domain_name, domain->alt_name))
120 break;
123 if (alternative_name && *alternative_name)
125 if (strequal(alternative_name, domain->name) ||
126 strequal(alternative_name, domain->alt_name))
128 break;
132 if (sid)
134 if (is_null_sid(sid)) {
135 continue;
138 if (sid_equal(sid, &domain->sid)) {
139 break;
144 if (domain != NULL) {
146 * We found a match. Possibly update the SID
148 if ((sid != NULL)
149 && sid_equal(&domain->sid, &global_sid_NULL)) {
150 sid_copy( &domain->sid, sid );
152 return domain;
155 /* Create new domain entry */
157 if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
158 return NULL;
160 /* Fill in fields */
162 ZERO_STRUCTP(domain);
164 fstrcpy(domain->name, domain_name);
165 if (alternative_name) {
166 fstrcpy(domain->alt_name, alternative_name);
169 domain->methods = methods;
170 domain->backend = NULL;
171 domain->internal = is_internal_domain(sid);
172 domain->sequence_number = DOM_SEQUENCE_NONE;
173 domain->last_seq_check = 0;
174 domain->initialized = False;
175 domain->online = is_internal_domain(sid);
176 domain->check_online_timeout = 0;
177 domain->dc_probe_pid = (pid_t)-1;
178 if (sid) {
179 sid_copy(&domain->sid, sid);
182 /* Link to domain list */
183 DLIST_ADD_END(_domain_list, domain, struct winbindd_domain *);
185 wcache_tdc_add_domain( domain );
187 idmap_config_option = talloc_asprintf(talloc_tos(), "idmap config %s",
188 domain->name);
189 if (idmap_config_option == NULL) {
190 DEBUG(0, ("talloc failed, not looking for idmap config\n"));
191 goto done;
194 param = lp_parm_const_string(-1, idmap_config_option, "range", NULL);
196 DEBUG(10, ("%s : range = %s\n", idmap_config_option,
197 param ? param : "not defined"));
199 if (param != NULL) {
200 unsigned low_id, high_id;
201 if (sscanf(param, "%u - %u", &low_id, &high_id) != 2) {
202 DEBUG(1, ("invalid range syntax in %s: %s\n",
203 idmap_config_option, param));
204 goto done;
206 if (low_id > high_id) {
207 DEBUG(1, ("invalid range in %s: %s\n",
208 idmap_config_option, param));
209 goto done;
211 domain->have_idmap_config = true;
212 domain->id_range_low = low_id;
213 domain->id_range_high = high_id;
216 done:
218 DEBUG(2,("Added domain %s %s %s\n",
219 domain->name, domain->alt_name,
220 &domain->sid?sid_string_dbg(&domain->sid):""));
222 return domain;
225 bool domain_is_forest_root(const struct winbindd_domain *domain)
227 const uint32_t fr_flags =
228 (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
230 return ((domain->domain_flags & fr_flags) == fr_flags);
233 /********************************************************************
234 rescan our domains looking for new trusted domains
235 ********************************************************************/
237 struct trustdom_state {
238 struct winbindd_domain *domain;
239 struct winbindd_request request;
242 static void trustdom_list_done(struct tevent_req *req);
243 static void rescan_forest_root_trusts( void );
244 static void rescan_forest_trusts( void );
246 static void add_trusted_domains( struct winbindd_domain *domain )
248 struct trustdom_state *state;
249 struct tevent_req *req;
251 state = TALLOC_ZERO_P(NULL, struct trustdom_state);
252 if (state == NULL) {
253 DEBUG(0, ("talloc failed\n"));
254 return;
256 state->domain = domain;
258 state->request.length = sizeof(state->request);
259 state->request.cmd = WINBINDD_LIST_TRUSTDOM;
261 req = wb_domain_request_send(state, winbind_event_context(),
262 domain, &state->request);
263 if (req == NULL) {
264 DEBUG(1, ("wb_domain_request_send failed\n"));
265 TALLOC_FREE(state);
266 return;
268 tevent_req_set_callback(req, trustdom_list_done, state);
271 static void trustdom_list_done(struct tevent_req *req)
273 struct trustdom_state *state = tevent_req_callback_data(
274 req, struct trustdom_state);
275 struct winbindd_response *response;
276 int res, err;
277 char *p;
279 res = wb_domain_request_recv(req, state, &response, &err);
280 if ((res == -1) || (response->result != WINBINDD_OK)) {
281 DEBUG(1, ("Could not receive trustdoms\n"));
282 TALLOC_FREE(state);
283 return;
286 p = (char *)response->extra_data.data;
288 while ((p != NULL) && (*p != '\0')) {
289 char *q, *sidstr, *alt_name;
290 struct dom_sid sid;
291 struct winbindd_domain *domain;
292 char *alternate_name = NULL;
294 alt_name = strchr(p, '\\');
295 if (alt_name == NULL) {
296 DEBUG(0, ("Got invalid trustdom response\n"));
297 break;
300 *alt_name = '\0';
301 alt_name += 1;
303 sidstr = strchr(alt_name, '\\');
304 if (sidstr == NULL) {
305 DEBUG(0, ("Got invalid trustdom response\n"));
306 break;
309 *sidstr = '\0';
310 sidstr += 1;
312 q = strchr(sidstr, '\n');
313 if (q != NULL)
314 *q = '\0';
316 if (!string_to_sid(&sid, sidstr)) {
317 DEBUG(0, ("Got invalid trustdom response\n"));
318 break;
321 /* use the real alt_name if we have one, else pass in NULL */
323 if ( !strequal( alt_name, "(null)" ) )
324 alternate_name = alt_name;
326 /* If we have an existing domain structure, calling
327 add_trusted_domain() will update the SID if
328 necessary. This is important because we need the
329 SID for sibling domains */
331 if ( find_domain_from_name_noinit(p) != NULL ) {
332 domain = add_trusted_domain(p, alternate_name,
333 &cache_methods,
334 &sid);
335 } else {
336 domain = add_trusted_domain(p, alternate_name,
337 &cache_methods,
338 &sid);
339 if (domain) {
340 setup_domain_child(domain);
343 p=q;
344 if (p != NULL)
345 p += 1;
349 Cases to consider when scanning trusts:
350 (a) we are calling from a child domain (primary && !forest_root)
351 (b) we are calling from the root of the forest (primary && forest_root)
352 (c) we are calling from a trusted forest domain (!primary
353 && !forest_root)
356 if (state->domain->primary) {
357 /* If this is our primary domain and we are not in the
358 forest root, we have to scan the root trusts first */
360 if (!domain_is_forest_root(state->domain))
361 rescan_forest_root_trusts();
362 else
363 rescan_forest_trusts();
365 } else if (domain_is_forest_root(state->domain)) {
366 /* Once we have done root forest trust search, we can
367 go on to search the trusted forests */
369 rescan_forest_trusts();
372 TALLOC_FREE(state);
374 return;
377 /********************************************************************
378 Scan the trusts of our forest root
379 ********************************************************************/
381 static void rescan_forest_root_trusts( void )
383 struct winbindd_tdc_domain *dom_list = NULL;
384 size_t num_trusts = 0;
385 int i;
387 /* The only transitive trusts supported by Windows 2003 AD are
388 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
389 first two are handled in forest and listed by
390 DsEnumerateDomainTrusts(). Forest trusts are not so we
391 have to do that ourselves. */
393 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
394 return;
396 for ( i=0; i<num_trusts; i++ ) {
397 struct winbindd_domain *d = NULL;
399 /* Find the forest root. Don't necessarily trust
400 the domain_list() as our primary domain may not
401 have been initialized. */
403 if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
404 continue;
407 /* Here's the forest root */
409 d = find_domain_from_name_noinit( dom_list[i].domain_name );
411 if ( !d ) {
412 d = add_trusted_domain( dom_list[i].domain_name,
413 dom_list[i].dns_name,
414 &cache_methods,
415 &dom_list[i].sid );
416 if (d != NULL) {
417 setup_domain_child(d);
421 if (d == NULL) {
422 continue;
425 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
426 "for domain tree root %s (%s)\n",
427 d->name, d->alt_name ));
429 d->domain_flags = dom_list[i].trust_flags;
430 d->domain_type = dom_list[i].trust_type;
431 d->domain_trust_attribs = dom_list[i].trust_attribs;
433 add_trusted_domains( d );
435 break;
438 TALLOC_FREE( dom_list );
440 return;
443 /********************************************************************
444 scan the transitive forest trusts (not our own)
445 ********************************************************************/
448 static void rescan_forest_trusts( void )
450 struct winbindd_domain *d = NULL;
451 struct winbindd_tdc_domain *dom_list = NULL;
452 size_t num_trusts = 0;
453 int i;
455 /* The only transitive trusts supported by Windows 2003 AD are
456 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
457 first two are handled in forest and listed by
458 DsEnumerateDomainTrusts(). Forest trusts are not so we
459 have to do that ourselves. */
461 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
462 return;
464 for ( i=0; i<num_trusts; i++ ) {
465 uint32 flags = dom_list[i].trust_flags;
466 uint32 type = dom_list[i].trust_type;
467 uint32 attribs = dom_list[i].trust_attribs;
469 d = find_domain_from_name_noinit( dom_list[i].domain_name );
471 /* ignore our primary and internal domains */
473 if ( d && (d->internal || d->primary ) )
474 continue;
476 if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
477 (type == NETR_TRUST_TYPE_UPLEVEL) &&
478 (attribs == NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
480 /* add the trusted domain if we don't know
481 about it */
483 if ( !d ) {
484 d = add_trusted_domain( dom_list[i].domain_name,
485 dom_list[i].dns_name,
486 &cache_methods,
487 &dom_list[i].sid );
488 if (d != NULL) {
489 setup_domain_child(d);
493 if (d == NULL) {
494 continue;
497 DEBUG(10,("Following trust path for domain %s (%s)\n",
498 d->name, d->alt_name ));
499 add_trusted_domains( d );
503 TALLOC_FREE( dom_list );
505 return;
508 /*********************************************************************
509 The process of updating the trusted domain list is a three step
510 async process:
511 (a) ask our domain
512 (b) ask the root domain in our forest
513 (c) ask the a DC in any Win2003 trusted forests
514 *********************************************************************/
516 void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
517 struct timeval now, void *private_data)
519 TALLOC_FREE(te);
521 /* I use to clear the cache here and start over but that
522 caused problems in child processes that needed the
523 trust dom list early on. Removing it means we
524 could have some trusted domains listed that have been
525 removed from our primary domain's DC until a full
526 restart. This should be ok since I think this is what
527 Windows does as well. */
529 /* this will only add new domains we didn't already know about
530 in the domain_list()*/
532 add_trusted_domains( find_our_domain() );
534 te = tevent_add_timer(
535 ev, NULL, timeval_current_ofs(WINBINDD_RESCAN_FREQ, 0),
536 rescan_trusted_domains, NULL);
538 * If te == NULL, there's not much we can do here. Don't fail, the
539 * only thing we miss is new trusted domains.
542 return;
545 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
546 struct winbindd_cli_state *state)
548 /* Ensure null termination */
549 state->request->domain_name
550 [sizeof(state->request->domain_name)-1]='\0';
551 state->request->data.init_conn.dcname
552 [sizeof(state->request->data.init_conn.dcname)-1]='\0';
554 if (strlen(state->request->data.init_conn.dcname) > 0) {
555 fstrcpy(domain->dcname, state->request->data.init_conn.dcname);
558 if (domain->internal) {
559 domain->initialized = true;
560 } else {
561 init_dc_connection(domain);
564 if (!domain->initialized) {
565 /* If we return error here we can't do any cached authentication,
566 but we may be in disconnected mode and can't initialize correctly.
567 Do what the previous code did and just return without initialization,
568 once we go online we'll re-initialize.
570 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
571 "online = %d\n", domain->name, (int)domain->online ));
574 fstrcpy(state->response->data.domain_info.name, domain->name);
575 fstrcpy(state->response->data.domain_info.alt_name, domain->alt_name);
576 sid_to_fstring(state->response->data.domain_info.sid, &domain->sid);
578 state->response->data.domain_info.native_mode
579 = domain->native_mode;
580 state->response->data.domain_info.active_directory
581 = domain->active_directory;
582 state->response->data.domain_info.primary
583 = domain->primary;
585 return WINBINDD_OK;
588 /* Look up global info for the winbind daemon */
589 bool init_domain_list(void)
591 struct winbindd_domain *domain;
592 int role = lp_server_role();
594 /* Free existing list */
595 free_domain_list();
597 /* BUILTIN domain */
599 domain = add_trusted_domain("BUILTIN", NULL, &cache_methods,
600 &global_sid_Builtin);
601 if (domain) {
602 setup_domain_child(domain);
605 /* Local SAM */
607 domain = add_trusted_domain(get_global_sam_name(), NULL,
608 &cache_methods, get_global_sam_sid());
609 if (domain) {
610 if ( role != ROLE_DOMAIN_MEMBER ) {
611 domain->primary = True;
613 setup_domain_child(domain);
616 /* Add ourselves as the first entry. */
618 if ( role == ROLE_DOMAIN_MEMBER ) {
619 struct dom_sid our_sid;
621 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
622 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
623 return False;
626 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
627 &cache_methods, &our_sid);
628 if (domain) {
629 domain->primary = True;
630 setup_domain_child(domain);
632 /* Even in the parent winbindd we'll need to
633 talk to the DC, so try and see if we can
634 contact it. Theoretically this isn't neccessary
635 as the init_dc_connection() in init_child_recv()
636 will do this, but we can start detecting the DC
637 early here. */
638 set_domain_online_request(domain);
642 return True;
645 void check_domain_trusted( const char *name, const struct dom_sid *user_sid )
647 struct winbindd_domain *domain;
648 struct dom_sid dom_sid;
649 uint32 rid;
651 /* Check if we even care */
653 if (!lp_allow_trusted_domains())
654 return;
656 domain = find_domain_from_name_noinit( name );
657 if ( domain )
658 return;
660 sid_copy( &dom_sid, user_sid );
661 if ( !sid_split_rid( &dom_sid, &rid ) )
662 return;
664 /* add the newly discovered trusted domain */
666 domain = add_trusted_domain( name, NULL, &cache_methods,
667 &dom_sid);
669 if ( !domain )
670 return;
672 /* assume this is a trust from a one-way transitive
673 forest trust */
675 domain->active_directory = True;
676 domain->domain_flags = NETR_TRUST_FLAG_OUTBOUND;
677 domain->domain_type = NETR_TRUST_TYPE_UPLEVEL;
678 domain->internal = False;
679 domain->online = True;
681 setup_domain_child(domain);
683 wcache_tdc_add_domain( domain );
685 return;
689 * Given a domain name, return the struct winbindd domain info for it
691 * @note Do *not* pass lp_workgroup() to this function. domain_list
692 * may modify it's value, and free that pointer. Instead, our local
693 * domain may be found by calling find_our_domain().
694 * directly.
697 * @return The domain structure for the named domain, if it is working.
700 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
702 struct winbindd_domain *domain;
704 /* Search through list */
706 for (domain = domain_list(); domain != NULL; domain = domain->next) {
707 if (strequal(domain_name, domain->name) ||
708 (domain->alt_name[0] &&
709 strequal(domain_name, domain->alt_name))) {
710 return domain;
714 /* Not found */
716 return NULL;
719 struct winbindd_domain *find_domain_from_name(const char *domain_name)
721 struct winbindd_domain *domain;
723 domain = find_domain_from_name_noinit(domain_name);
725 if (domain == NULL)
726 return NULL;
728 if (!domain->initialized)
729 init_dc_connection(domain);
731 return domain;
734 /* Given a domain sid, return the struct winbindd domain info for it */
736 struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid)
738 struct winbindd_domain *domain;
740 /* Search through list */
742 for (domain = domain_list(); domain != NULL; domain = domain->next) {
743 if (sid_compare_domain(sid, &domain->sid) == 0)
744 return domain;
747 /* Not found */
749 return NULL;
752 /* Given a domain sid, return the struct winbindd domain info for it */
754 struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid)
756 struct winbindd_domain *domain;
758 domain = find_domain_from_sid_noinit(sid);
760 if (domain == NULL)
761 return NULL;
763 if (!domain->initialized)
764 init_dc_connection(domain);
766 return domain;
769 struct winbindd_domain *find_our_domain(void)
771 struct winbindd_domain *domain;
773 /* Search through list */
775 for (domain = domain_list(); domain != NULL; domain = domain->next) {
776 if (domain->primary)
777 return domain;
780 smb_panic("Could not find our domain");
781 return NULL;
784 struct winbindd_domain *find_root_domain(void)
786 struct winbindd_domain *ours = find_our_domain();
788 if (ours->forest_name[0] == '\0') {
789 return NULL;
792 return find_domain_from_name( ours->forest_name );
795 struct winbindd_domain *find_builtin_domain(void)
797 struct winbindd_domain *domain;
799 domain = find_domain_from_sid(&global_sid_Builtin);
800 if (domain == NULL) {
801 smb_panic("Could not find BUILTIN domain");
804 return domain;
807 /* Find the appropriate domain to lookup a name or SID */
809 struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid)
811 /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
813 if ( sid_check_is_in_unix_groups(sid) ||
814 sid_check_is_unix_groups(sid) ||
815 sid_check_is_in_unix_users(sid) ||
816 sid_check_is_unix_users(sid) )
818 return find_domain_from_sid(get_global_sam_sid());
821 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
822 * one to contact the external DC's. On member servers the internal
823 * domains are different: These are part of the local SAM. */
825 DEBUG(10, ("find_lookup_domain_from_sid(%s)\n", sid_string_dbg(sid)));
827 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
828 DEBUG(10, ("calling find_domain_from_sid\n"));
829 return find_domain_from_sid(sid);
832 /* On a member server a query for SID or name can always go to our
833 * primary DC. */
835 DEBUG(10, ("calling find_our_domain\n"));
836 return find_our_domain();
839 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
841 if ( strequal(domain_name, unix_users_domain_name() ) ||
842 strequal(domain_name, unix_groups_domain_name() ) )
845 * The "Unix User" and "Unix Group" domain our handled by
846 * passdb
848 return find_domain_from_name_noinit( get_global_sam_name() );
851 if (IS_DC || strequal(domain_name, "BUILTIN") ||
852 strequal(domain_name, get_global_sam_name()))
853 return find_domain_from_name_noinit(domain_name);
856 return find_our_domain();
859 /* Is this a domain which we may assume no DOMAIN\ prefix? */
861 static bool assume_domain(const char *domain)
863 /* never assume the domain on a standalone server */
865 if ( lp_server_role() == ROLE_STANDALONE )
866 return False;
868 /* domain member servers may possibly assume for the domain name */
870 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
871 if ( !strequal(lp_workgroup(), domain) )
872 return False;
874 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
875 return True;
878 /* only left with a domain controller */
880 if ( strequal(get_global_sam_name(), domain) ) {
881 return True;
884 return False;
887 /* Parse a string of the form DOMAIN\user into a domain and a user */
889 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
891 char *p = strchr(domuser,*lp_winbind_separator());
893 if ( !p ) {
894 fstrcpy(user, domuser);
896 if ( assume_domain(lp_workgroup())) {
897 fstrcpy(domain, lp_workgroup());
898 } else if ((p = strchr(domuser, '@')) != NULL) {
899 fstrcpy(domain, p + 1);
900 user[PTR_DIFF(p, domuser)] = 0;
901 } else {
902 return False;
904 } else {
905 fstrcpy(user, p+1);
906 fstrcpy(domain, domuser);
907 domain[PTR_DIFF(p, domuser)] = 0;
910 strupper_m(domain);
912 return True;
915 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
916 char **domain, char **user)
918 fstring fstr_domain, fstr_user;
919 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
920 return False;
922 *domain = talloc_strdup(mem_ctx, fstr_domain);
923 *user = talloc_strdup(mem_ctx, fstr_user);
924 return ((*domain != NULL) && (*user != NULL));
927 /* add a domain user name to a buffer */
928 void parse_add_domuser(void *buf, char *domuser, int *len)
930 fstring domain;
931 char *p, *user;
933 user = domuser;
934 p = strchr(domuser, *lp_winbind_separator());
936 if (p) {
938 fstrcpy(domain, domuser);
939 domain[PTR_DIFF(p, domuser)] = 0;
940 p++;
942 if (assume_domain(domain)) {
944 user = p;
945 *len -= (PTR_DIFF(p, domuser));
949 safe_strcpy((char *)buf, user, *len);
952 /* Ensure an incoming username from NSS is fully qualified. Replace the
953 incoming fstring with DOMAIN <separator> user. Returns the same
954 values as parse_domain_user() but also replaces the incoming username.
955 Used to ensure all names are fully qualified within winbindd.
956 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
957 The protocol definitions of auth_crap, chng_pswd_auth_crap
958 really should be changed to use this instead of doing things
959 by hand. JRA. */
961 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
963 if (!parse_domain_user(username_inout, domain, user)) {
964 return False;
966 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
967 domain, *lp_winbind_separator(),
968 user);
969 return True;
973 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
974 'winbind separator' options.
975 This means:
976 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
977 lp_workgroup()
979 If we are a PDC or BDC, and this is for our domain, do likewise.
981 Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
982 username is then unqualified in unix
984 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
986 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
988 fstring tmp_user;
990 fstrcpy(tmp_user, user);
991 strlower_m(tmp_user);
993 if (can_assume && assume_domain(domain)) {
994 strlcpy(name, tmp_user, sizeof(fstring));
995 } else {
996 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
997 domain, *lp_winbind_separator(),
998 tmp_user);
1003 * talloc version of fill_domain_username()
1004 * return NULL on talloc failure.
1006 char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
1007 const char *domain,
1008 const char *user,
1009 bool can_assume)
1011 char *tmp_user, *name;
1013 tmp_user = talloc_strdup(mem_ctx, user);
1014 strlower_m(tmp_user);
1016 if (can_assume && assume_domain(domain)) {
1017 name = tmp_user;
1018 } else {
1019 name = talloc_asprintf(mem_ctx, "%s%c%s",
1020 domain,
1021 *lp_winbind_separator(),
1022 tmp_user);
1023 TALLOC_FREE(tmp_user);
1026 return name;
1030 * Client list accessor functions
1033 static struct winbindd_cli_state *_client_list;
1034 static int _num_clients;
1036 /* Return list of all connected clients */
1038 struct winbindd_cli_state *winbindd_client_list(void)
1040 return _client_list;
1043 /* Add a connection to the list */
1045 void winbindd_add_client(struct winbindd_cli_state *cli)
1047 DLIST_ADD(_client_list, cli);
1048 _num_clients++;
1051 /* Remove a client from the list */
1053 void winbindd_remove_client(struct winbindd_cli_state *cli)
1055 DLIST_REMOVE(_client_list, cli);
1056 _num_clients--;
1059 /* Return number of open clients */
1061 int winbindd_num_clients(void)
1063 return _num_clients;
1066 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1067 TALLOC_CTX *mem_ctx,
1068 const struct dom_sid *user_sid,
1069 uint32 *p_num_groups, struct dom_sid **user_sids)
1071 struct netr_SamInfo3 *info3 = NULL;
1072 NTSTATUS status = NT_STATUS_NO_MEMORY;
1073 size_t num_groups = 0;
1075 DEBUG(3,(": lookup_usergroups_cached\n"));
1077 *user_sids = NULL;
1078 *p_num_groups = 0;
1080 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1082 if (info3 == NULL) {
1083 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1086 if (info3->base.groups.count == 0) {
1087 TALLOC_FREE(info3);
1088 return NT_STATUS_UNSUCCESSFUL;
1091 /* Skip Domain local groups outside our domain.
1092 We'll get these from the getsidaliases() RPC call. */
1093 status = sid_array_from_info3(mem_ctx, info3,
1094 user_sids,
1095 &num_groups,
1096 false, true);
1098 if (!NT_STATUS_IS_OK(status)) {
1099 TALLOC_FREE(info3);
1100 return status;
1103 TALLOC_FREE(info3);
1104 *p_num_groups = num_groups;
1105 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1107 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1109 return status;
1112 /*********************************************************************
1113 We use this to remove spaces from user and group names
1114 ********************************************************************/
1116 NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
1117 struct winbindd_domain *domain,
1118 const char *name,
1119 char **normalized)
1121 NTSTATUS nt_status;
1123 if (!name || !normalized) {
1124 return NT_STATUS_INVALID_PARAMETER;
1127 if (!lp_winbind_normalize_names()) {
1128 return NT_STATUS_PROCEDURE_NOT_FOUND;
1131 /* Alias support and whitespace replacement are mutually
1132 exclusive */
1134 nt_status = resolve_username_to_alias(mem_ctx, domain,
1135 name, normalized );
1136 if (NT_STATUS_IS_OK(nt_status)) {
1137 /* special return code to let the caller know we
1138 mapped to an alias */
1139 return NT_STATUS_FILE_RENAMED;
1142 /* check for an unreachable domain */
1144 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1145 DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
1146 domain->name));
1147 set_domain_offline(domain);
1148 return nt_status;
1151 /* deal with whitespace */
1153 *normalized = talloc_strdup(mem_ctx, name);
1154 if (!(*normalized)) {
1155 return NT_STATUS_NO_MEMORY;
1158 all_string_sub( *normalized, " ", "_", 0 );
1160 return NT_STATUS_OK;
1163 /*********************************************************************
1164 We use this to do the inverse of normalize_name_map()
1165 ********************************************************************/
1167 NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
1168 char *name,
1169 char **normalized)
1171 NTSTATUS nt_status;
1172 struct winbindd_domain *domain = find_our_domain();
1174 if (!name || !normalized) {
1175 return NT_STATUS_INVALID_PARAMETER;
1178 if (!lp_winbind_normalize_names()) {
1179 return NT_STATUS_PROCEDURE_NOT_FOUND;
1182 /* Alias support and whitespace replacement are mutally
1183 exclusive */
1185 /* When mapping from an alias to a username, we don't know the
1186 domain. But we only need a domain structure to cache
1187 a successful lookup , so just our own domain structure for
1188 the seqnum. */
1190 nt_status = resolve_alias_to_username(mem_ctx, domain,
1191 name, normalized);
1192 if (NT_STATUS_IS_OK(nt_status)) {
1193 /* Special return code to let the caller know we mapped
1194 from an alias */
1195 return NT_STATUS_FILE_RENAMED;
1198 /* check for an unreachable domain */
1200 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1201 DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
1202 domain->name));
1203 set_domain_offline(domain);
1204 return nt_status;
1207 /* deal with whitespace */
1209 *normalized = talloc_strdup(mem_ctx, name);
1210 if (!(*normalized)) {
1211 return NT_STATUS_NO_MEMORY;
1214 all_string_sub(*normalized, "_", " ", 0);
1216 return NT_STATUS_OK;
1219 /*********************************************************************
1220 ********************************************************************/
1222 bool winbindd_can_contact_domain(struct winbindd_domain *domain)
1224 struct winbindd_tdc_domain *tdc = NULL;
1225 TALLOC_CTX *frame = talloc_stackframe();
1226 bool ret = false;
1228 /* We can contact the domain if it is our primary domain */
1230 if (domain->primary) {
1231 return true;
1234 /* Trust the TDC cache and not the winbindd_domain flags */
1236 if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
1237 DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
1238 domain->name));
1239 return false;
1242 /* Can always contact a domain that is in out forest */
1244 if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
1245 ret = true;
1246 goto done;
1250 * On a _member_ server, we cannot contact the domain if it
1251 * is running AD and we have no inbound trust.
1254 if (!IS_DC &&
1255 domain->active_directory &&
1256 ((tdc->trust_flags & NETR_TRUST_FLAG_INBOUND) != NETR_TRUST_FLAG_INBOUND))
1258 DEBUG(10, ("winbindd_can_contact_domain: %s is an AD domain "
1259 "and we have no inbound trust.\n", domain->name));
1260 goto done;
1263 /* Assume everything else is ok (probably not true but what
1264 can you do?) */
1266 ret = true;
1268 done:
1269 talloc_destroy(frame);
1271 return ret;
1274 /*********************************************************************
1275 ********************************************************************/
1277 bool winbindd_internal_child(struct winbindd_child *child)
1279 if ((child == idmap_child()) || (child == locator_child())) {
1280 return True;
1283 return False;
1286 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1288 /*********************************************************************
1289 ********************************************************************/
1291 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1293 char *var = NULL;
1294 char addr[INET6_ADDRSTRLEN];
1295 const char *kdc = NULL;
1296 int lvl = 11;
1298 if (!domain || !domain->alt_name || !*domain->alt_name) {
1299 return;
1302 if (domain->initialized && !domain->active_directory) {
1303 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1304 domain->alt_name));
1305 return;
1308 print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
1309 kdc = addr;
1310 if (!*kdc) {
1311 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1312 domain->alt_name));
1313 kdc = domain->dcname;
1316 if (!kdc || !*kdc) {
1317 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1318 domain->alt_name));
1319 return;
1322 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1323 domain->alt_name) == -1) {
1324 return;
1327 DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1328 var, kdc));
1330 setenv(var, kdc, 1);
1331 free(var);
1334 /*********************************************************************
1335 ********************************************************************/
1337 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1339 struct winbindd_domain *our_dom = find_our_domain();
1341 winbindd_set_locator_kdc_env(domain);
1343 if (domain != our_dom) {
1344 winbindd_set_locator_kdc_env(our_dom);
1348 /*********************************************************************
1349 ********************************************************************/
1351 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1353 char *var = NULL;
1355 if (!domain || !domain->alt_name || !*domain->alt_name) {
1356 return;
1359 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1360 domain->alt_name) == -1) {
1361 return;
1364 unsetenv(var);
1365 free(var);
1367 #else
1369 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1371 return;
1374 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1376 return;
1379 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */
1381 void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
1383 resp->data.auth.nt_status = NT_STATUS_V(result);
1384 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
1386 /* we might have given a more useful error above */
1387 if (*resp->data.auth.error_string == '\0')
1388 fstrcpy(resp->data.auth.error_string,
1389 get_friendly_nt_error_msg(result));
1390 resp->data.auth.pam_error = nt_status_to_pam(result);
1393 bool is_domain_offline(const struct winbindd_domain *domain)
1395 if (!lp_winbind_offline_logon()) {
1396 return false;
1398 if (get_global_winbindd_state_offline()) {
1399 return true;
1401 return !domain->online;