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/>.
26 #include "../libcli/security/security.h"
29 #define DBGC_CLASS DBGC_WINBIND
31 extern struct winbindd_methods cache_methods;
34 * @file winbindd_util.cq
36 * Winbind daemon for NT domain authentication nss module.
40 /* The list of trusted domains. Note that the list can be deleted and
41 recreated using the init_domain_list() function so pointers to
42 individual winbindd_domain structures cannot be made. Keep a copy of
43 the domain name instead. */
45 static struct winbindd_domain *_domain_list = NULL;
47 struct winbindd_domain *domain_list(void)
51 if ((!_domain_list) && (!init_domain_list())) {
52 smb_panic("Init_domain_list failed");
58 /* Free all entries in the trusted domain list */
60 static void free_domain_list(void)
62 struct winbindd_domain *domain = _domain_list;
65 struct winbindd_domain *next = domain->next;
67 DLIST_REMOVE(_domain_list, domain);
73 static bool is_internal_domain(const struct dom_sid *sid)
78 return (sid_check_is_domain(sid) || sid_check_is_builtin(sid));
81 static bool is_in_internal_domain(const struct dom_sid *sid)
86 return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid));
90 /* Add a trusted domain to our list of domains */
91 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
92 struct winbindd_methods *methods,
93 const struct dom_sid *sid)
95 struct winbindd_domain *domain;
96 const char *alternative_name = NULL;
97 char *idmap_config_option;
99 const char **ignored_domains, **dom;
101 ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
102 for (dom=ignored_domains; dom && *dom; dom++) {
103 if (gen_fnmatch(*dom, domain_name) == 0) {
104 DEBUG(2,("Ignoring domain '%s'\n", domain_name));
109 /* ignore alt_name if we are not in an AD domain */
111 if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
112 alternative_name = alt_name;
115 /* We can't call domain_list() as this function is called from
116 init_domain_list() and we'll get stuck in a loop. */
117 for (domain = _domain_list; domain; domain = domain->next) {
118 if (strequal(domain_name, domain->name) ||
119 strequal(domain_name, domain->alt_name))
124 if (alternative_name && *alternative_name)
126 if (strequal(alternative_name, domain->name) ||
127 strequal(alternative_name, domain->alt_name))
135 if (is_null_sid(sid)) {
139 if (dom_sid_equal(sid, &domain->sid)) {
145 if (domain != NULL) {
147 * We found a match. Possibly update the SID
150 && dom_sid_equal(&domain->sid, &global_sid_NULL)) {
151 sid_copy( &domain->sid, sid );
156 /* Create new domain entry */
158 if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
163 ZERO_STRUCTP(domain);
165 domain->children = SMB_MALLOC_ARRAY(
166 struct winbindd_child, lp_winbind_max_domain_connections());
167 if (domain->children == NULL) {
171 memset(domain->children, 0,
172 sizeof(struct winbindd_child)
173 * lp_winbind_max_domain_connections());
175 fstrcpy(domain->name, domain_name);
176 if (alternative_name) {
177 fstrcpy(domain->alt_name, alternative_name);
180 domain->methods = methods;
181 domain->backend = NULL;
182 domain->internal = is_internal_domain(sid);
183 domain->sequence_number = DOM_SEQUENCE_NONE;
184 domain->last_seq_check = 0;
185 domain->initialized = False;
186 domain->online = is_internal_domain(sid);
187 domain->check_online_timeout = 0;
188 domain->dc_probe_pid = (pid_t)-1;
190 sid_copy(&domain->sid, sid);
193 /* Link to domain list */
194 DLIST_ADD_END(_domain_list, domain, struct winbindd_domain *);
196 wcache_tdc_add_domain( domain );
198 idmap_config_option = talloc_asprintf(talloc_tos(), "idmap config %s",
200 if (idmap_config_option == NULL) {
201 DEBUG(0, ("talloc failed, not looking for idmap config\n"));
205 param = lp_parm_const_string(-1, idmap_config_option, "range", NULL);
207 DEBUG(10, ("%s : range = %s\n", idmap_config_option,
208 param ? param : "not defined"));
211 unsigned low_id, high_id;
212 if (sscanf(param, "%u - %u", &low_id, &high_id) != 2) {
213 DEBUG(1, ("invalid range syntax in %s: %s\n",
214 idmap_config_option, param));
217 if (low_id > high_id) {
218 DEBUG(1, ("invalid range in %s: %s\n",
219 idmap_config_option, param));
222 domain->have_idmap_config = true;
223 domain->id_range_low = low_id;
224 domain->id_range_high = high_id;
229 DEBUG(2,("Added domain %s %s %s\n",
230 domain->name, domain->alt_name,
231 &domain->sid?sid_string_dbg(&domain->sid):""));
236 bool domain_is_forest_root(const struct winbindd_domain *domain)
238 const uint32_t fr_flags =
239 (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
241 return ((domain->domain_flags & fr_flags) == fr_flags);
244 /********************************************************************
245 rescan our domains looking for new trusted domains
246 ********************************************************************/
248 struct trustdom_state {
249 struct winbindd_domain *domain;
250 struct winbindd_request request;
253 static void trustdom_list_done(struct tevent_req *req);
254 static void rescan_forest_root_trusts( void );
255 static void rescan_forest_trusts( void );
257 static void add_trusted_domains( struct winbindd_domain *domain )
259 struct trustdom_state *state;
260 struct tevent_req *req;
262 state = TALLOC_ZERO_P(NULL, struct trustdom_state);
264 DEBUG(0, ("talloc failed\n"));
267 state->domain = domain;
269 state->request.length = sizeof(state->request);
270 state->request.cmd = WINBINDD_LIST_TRUSTDOM;
272 req = wb_domain_request_send(state, winbind_event_context(),
273 domain, &state->request);
275 DEBUG(1, ("wb_domain_request_send failed\n"));
279 tevent_req_set_callback(req, trustdom_list_done, state);
282 static void trustdom_list_done(struct tevent_req *req)
284 struct trustdom_state *state = tevent_req_callback_data(
285 req, struct trustdom_state);
286 struct winbindd_response *response;
290 res = wb_domain_request_recv(req, state, &response, &err);
291 if ((res == -1) || (response->result != WINBINDD_OK)) {
292 DEBUG(1, ("Could not receive trustdoms\n"));
297 p = (char *)response->extra_data.data;
299 while ((p != NULL) && (*p != '\0')) {
300 char *q, *sidstr, *alt_name;
302 struct winbindd_domain *domain;
303 char *alternate_name = NULL;
305 alt_name = strchr(p, '\\');
306 if (alt_name == NULL) {
307 DEBUG(0, ("Got invalid trustdom response\n"));
314 sidstr = strchr(alt_name, '\\');
315 if (sidstr == NULL) {
316 DEBUG(0, ("Got invalid trustdom response\n"));
323 q = strchr(sidstr, '\n');
327 if (!string_to_sid(&sid, sidstr)) {
328 DEBUG(0, ("Got invalid trustdom response\n"));
332 /* use the real alt_name if we have one, else pass in NULL */
334 if ( !strequal( alt_name, "(null)" ) )
335 alternate_name = alt_name;
337 /* If we have an existing domain structure, calling
338 add_trusted_domain() will update the SID if
339 necessary. This is important because we need the
340 SID for sibling domains */
342 if ( find_domain_from_name_noinit(p) != NULL ) {
343 domain = add_trusted_domain(p, alternate_name,
347 domain = add_trusted_domain(p, alternate_name,
351 setup_domain_child(domain);
360 Cases to consider when scanning trusts:
361 (a) we are calling from a child domain (primary && !forest_root)
362 (b) we are calling from the root of the forest (primary && forest_root)
363 (c) we are calling from a trusted forest domain (!primary
367 if (state->domain->primary) {
368 /* If this is our primary domain and we are not in the
369 forest root, we have to scan the root trusts first */
371 if (!domain_is_forest_root(state->domain))
372 rescan_forest_root_trusts();
374 rescan_forest_trusts();
376 } else if (domain_is_forest_root(state->domain)) {
377 /* Once we have done root forest trust search, we can
378 go on to search the trusted forests */
380 rescan_forest_trusts();
388 /********************************************************************
389 Scan the trusts of our forest root
390 ********************************************************************/
392 static void rescan_forest_root_trusts( void )
394 struct winbindd_tdc_domain *dom_list = NULL;
395 size_t num_trusts = 0;
398 /* The only transitive trusts supported by Windows 2003 AD are
399 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
400 first two are handled in forest and listed by
401 DsEnumerateDomainTrusts(). Forest trusts are not so we
402 have to do that ourselves. */
404 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
407 for ( i=0; i<num_trusts; i++ ) {
408 struct winbindd_domain *d = NULL;
410 /* Find the forest root. Don't necessarily trust
411 the domain_list() as our primary domain may not
412 have been initialized. */
414 if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
418 /* Here's the forest root */
420 d = find_domain_from_name_noinit( dom_list[i].domain_name );
423 d = add_trusted_domain( dom_list[i].domain_name,
424 dom_list[i].dns_name,
428 setup_domain_child(d);
436 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
437 "for domain tree root %s (%s)\n",
438 d->name, d->alt_name ));
440 d->domain_flags = dom_list[i].trust_flags;
441 d->domain_type = dom_list[i].trust_type;
442 d->domain_trust_attribs = dom_list[i].trust_attribs;
444 add_trusted_domains( d );
449 TALLOC_FREE( dom_list );
454 /********************************************************************
455 scan the transitive forest trusts (not our own)
456 ********************************************************************/
459 static void rescan_forest_trusts( void )
461 struct winbindd_domain *d = NULL;
462 struct winbindd_tdc_domain *dom_list = NULL;
463 size_t num_trusts = 0;
466 /* The only transitive trusts supported by Windows 2003 AD are
467 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
468 first two are handled in forest and listed by
469 DsEnumerateDomainTrusts(). Forest trusts are not so we
470 have to do that ourselves. */
472 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
475 for ( i=0; i<num_trusts; i++ ) {
476 uint32 flags = dom_list[i].trust_flags;
477 uint32 type = dom_list[i].trust_type;
478 uint32 attribs = dom_list[i].trust_attribs;
480 d = find_domain_from_name_noinit( dom_list[i].domain_name );
482 /* ignore our primary and internal domains */
484 if ( d && (d->internal || d->primary ) )
487 if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
488 (type == NETR_TRUST_TYPE_UPLEVEL) &&
489 (attribs == NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
491 /* add the trusted domain if we don't know
495 d = add_trusted_domain( dom_list[i].domain_name,
496 dom_list[i].dns_name,
500 setup_domain_child(d);
508 DEBUG(10,("Following trust path for domain %s (%s)\n",
509 d->name, d->alt_name ));
510 add_trusted_domains( d );
514 TALLOC_FREE( dom_list );
519 /*********************************************************************
520 The process of updating the trusted domain list is a three step
523 (b) ask the root domain in our forest
524 (c) ask the a DC in any Win2003 trusted forests
525 *********************************************************************/
527 void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
528 struct timeval now, void *private_data)
532 /* I use to clear the cache here and start over but that
533 caused problems in child processes that needed the
534 trust dom list early on. Removing it means we
535 could have some trusted domains listed that have been
536 removed from our primary domain's DC until a full
537 restart. This should be ok since I think this is what
538 Windows does as well. */
540 /* this will only add new domains we didn't already know about
541 in the domain_list()*/
543 add_trusted_domains( find_our_domain() );
545 te = tevent_add_timer(
546 ev, NULL, timeval_current_ofs(WINBINDD_RESCAN_FREQ, 0),
547 rescan_trusted_domains, NULL);
549 * If te == NULL, there's not much we can do here. Don't fail, the
550 * only thing we miss is new trusted domains.
556 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
557 struct winbindd_cli_state *state)
559 /* Ensure null termination */
560 state->request->domain_name
561 [sizeof(state->request->domain_name)-1]='\0';
562 state->request->data.init_conn.dcname
563 [sizeof(state->request->data.init_conn.dcname)-1]='\0';
565 if (strlen(state->request->data.init_conn.dcname) > 0) {
566 fstrcpy(domain->dcname, state->request->data.init_conn.dcname);
569 if (domain->internal) {
570 domain->initialized = true;
572 init_dc_connection(domain);
575 if (!domain->initialized) {
576 /* If we return error here we can't do any cached authentication,
577 but we may be in disconnected mode and can't initialize correctly.
578 Do what the previous code did and just return without initialization,
579 once we go online we'll re-initialize.
581 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
582 "online = %d\n", domain->name, (int)domain->online ));
585 fstrcpy(state->response->data.domain_info.name, domain->name);
586 fstrcpy(state->response->data.domain_info.alt_name, domain->alt_name);
587 sid_to_fstring(state->response->data.domain_info.sid, &domain->sid);
589 state->response->data.domain_info.native_mode
590 = domain->native_mode;
591 state->response->data.domain_info.active_directory
592 = domain->active_directory;
593 state->response->data.domain_info.primary
599 /* Look up global info for the winbind daemon */
600 bool init_domain_list(void)
602 struct winbindd_domain *domain;
603 int role = lp_server_role();
605 /* Free existing list */
610 domain = add_trusted_domain("BUILTIN", NULL, &cache_methods,
611 &global_sid_Builtin);
613 setup_domain_child(domain);
618 domain = add_trusted_domain(get_global_sam_name(), NULL,
619 &cache_methods, get_global_sam_sid());
621 if ( role != ROLE_DOMAIN_MEMBER ) {
622 domain->primary = True;
624 setup_domain_child(domain);
627 /* Add ourselves as the first entry. */
629 if ( role == ROLE_DOMAIN_MEMBER ) {
630 struct dom_sid our_sid;
632 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
633 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
637 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
638 &cache_methods, &our_sid);
640 domain->primary = True;
641 setup_domain_child(domain);
643 /* Even in the parent winbindd we'll need to
644 talk to the DC, so try and see if we can
645 contact it. Theoretically this isn't neccessary
646 as the init_dc_connection() in init_child_recv()
647 will do this, but we can start detecting the DC
649 set_domain_online_request(domain);
657 * Given a domain name, return the struct winbindd domain info for it
659 * @note Do *not* pass lp_workgroup() to this function. domain_list
660 * may modify it's value, and free that pointer. Instead, our local
661 * domain may be found by calling find_our_domain().
665 * @return The domain structure for the named domain, if it is working.
668 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
670 struct winbindd_domain *domain;
672 /* Search through list */
674 for (domain = domain_list(); domain != NULL; domain = domain->next) {
675 if (strequal(domain_name, domain->name) ||
676 (domain->alt_name[0] &&
677 strequal(domain_name, domain->alt_name))) {
687 struct winbindd_domain *find_domain_from_name(const char *domain_name)
689 struct winbindd_domain *domain;
691 domain = find_domain_from_name_noinit(domain_name);
696 if (!domain->initialized)
697 init_dc_connection(domain);
702 /* Given a domain sid, return the struct winbindd domain info for it */
704 struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid)
706 struct winbindd_domain *domain;
708 /* Search through list */
710 for (domain = domain_list(); domain != NULL; domain = domain->next) {
711 if (dom_sid_compare_domain(sid, &domain->sid) == 0)
720 /* Given a domain sid, return the struct winbindd domain info for it */
722 struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid)
724 struct winbindd_domain *domain;
726 domain = find_domain_from_sid_noinit(sid);
731 if (!domain->initialized)
732 init_dc_connection(domain);
737 struct winbindd_domain *find_our_domain(void)
739 struct winbindd_domain *domain;
741 /* Search through list */
743 for (domain = domain_list(); domain != NULL; domain = domain->next) {
748 smb_panic("Could not find our domain");
752 struct winbindd_domain *find_root_domain(void)
754 struct winbindd_domain *ours = find_our_domain();
756 if (ours->forest_name[0] == '\0') {
760 return find_domain_from_name( ours->forest_name );
763 struct winbindd_domain *find_builtin_domain(void)
765 struct winbindd_domain *domain;
767 domain = find_domain_from_sid(&global_sid_Builtin);
768 if (domain == NULL) {
769 smb_panic("Could not find BUILTIN domain");
775 /* Find the appropriate domain to lookup a name or SID */
777 struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid)
779 /* SIDs in the S-1-22-{1,2} domain should be handled by our passdb */
781 if ( sid_check_is_in_unix_groups(sid) ||
782 sid_check_is_unix_groups(sid) ||
783 sid_check_is_in_unix_users(sid) ||
784 sid_check_is_unix_users(sid) )
786 return find_domain_from_sid(get_global_sam_sid());
789 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
790 * one to contact the external DC's. On member servers the internal
791 * domains are different: These are part of the local SAM. */
793 DEBUG(10, ("find_lookup_domain_from_sid(%s)\n", sid_string_dbg(sid)));
795 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
796 DEBUG(10, ("calling find_domain_from_sid\n"));
797 return find_domain_from_sid(sid);
800 /* On a member server a query for SID or name can always go to our
803 DEBUG(10, ("calling find_our_domain\n"));
804 return find_our_domain();
807 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
809 if ( strequal(domain_name, unix_users_domain_name() ) ||
810 strequal(domain_name, unix_groups_domain_name() ) )
813 * The "Unix User" and "Unix Group" domain our handled by
816 return find_domain_from_name_noinit( get_global_sam_name() );
819 if (IS_DC || strequal(domain_name, "BUILTIN") ||
820 strequal(domain_name, get_global_sam_name()))
821 return find_domain_from_name_noinit(domain_name);
824 return find_our_domain();
827 /* Is this a domain which we may assume no DOMAIN\ prefix? */
829 static bool assume_domain(const char *domain)
831 /* never assume the domain on a standalone server */
833 if ( lp_server_role() == ROLE_STANDALONE )
836 /* domain member servers may possibly assume for the domain name */
838 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
839 if ( !strequal(lp_workgroup(), domain) )
842 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
846 /* only left with a domain controller */
848 if ( strequal(get_global_sam_name(), domain) ) {
855 /* Parse a string of the form DOMAIN\user into a domain and a user */
857 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
859 char *p = strchr(domuser,*lp_winbind_separator());
862 fstrcpy(user, domuser);
864 if ( assume_domain(lp_workgroup())) {
865 fstrcpy(domain, lp_workgroup());
866 } else if ((p = strchr(domuser, '@')) != NULL) {
867 fstrcpy(domain, p + 1);
868 user[PTR_DIFF(p, domuser)] = 0;
874 fstrcpy(domain, domuser);
875 domain[PTR_DIFF(p, domuser)] = 0;
883 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
884 char **domain, char **user)
886 fstring fstr_domain, fstr_user;
887 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
890 *domain = talloc_strdup(mem_ctx, fstr_domain);
891 *user = talloc_strdup(mem_ctx, fstr_user);
892 return ((*domain != NULL) && (*user != NULL));
895 /* add a domain user name to a buffer */
896 void parse_add_domuser(void *buf, char *domuser, int *len)
902 p = strchr(domuser, *lp_winbind_separator());
906 fstrcpy(domain, domuser);
907 domain[PTR_DIFF(p, domuser)] = 0;
910 if (assume_domain(domain)) {
913 *len -= (PTR_DIFF(p, domuser));
917 safe_strcpy((char *)buf, user, *len);
920 /* Ensure an incoming username from NSS is fully qualified. Replace the
921 incoming fstring with DOMAIN <separator> user. Returns the same
922 values as parse_domain_user() but also replaces the incoming username.
923 Used to ensure all names are fully qualified within winbindd.
924 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
925 The protocol definitions of auth_crap, chng_pswd_auth_crap
926 really should be changed to use this instead of doing things
929 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
931 if (!parse_domain_user(username_inout, domain, user)) {
934 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
935 domain, *lp_winbind_separator(),
941 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
942 'winbind separator' options.
944 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
947 If we are a PDC or BDC, and this is for our domain, do likewise.
949 Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
950 username is then unqualified in unix
952 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
954 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
958 fstrcpy(tmp_user, user);
959 strlower_m(tmp_user);
961 if (can_assume && assume_domain(domain)) {
962 strlcpy(name, tmp_user, sizeof(fstring));
964 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
965 domain, *lp_winbind_separator(),
971 * talloc version of fill_domain_username()
972 * return NULL on talloc failure.
974 char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
979 char *tmp_user, *name;
981 tmp_user = talloc_strdup(mem_ctx, user);
982 strlower_m(tmp_user);
984 if (can_assume && assume_domain(domain)) {
987 name = talloc_asprintf(mem_ctx, "%s%c%s",
989 *lp_winbind_separator(),
991 TALLOC_FREE(tmp_user);
998 * Client list accessor functions
1001 static struct winbindd_cli_state *_client_list;
1002 static int _num_clients;
1004 /* Return list of all connected clients */
1006 struct winbindd_cli_state *winbindd_client_list(void)
1008 return _client_list;
1011 /* Add a connection to the list */
1013 void winbindd_add_client(struct winbindd_cli_state *cli)
1015 DLIST_ADD(_client_list, cli);
1019 /* Remove a client from the list */
1021 void winbindd_remove_client(struct winbindd_cli_state *cli)
1023 DLIST_REMOVE(_client_list, cli);
1027 /* Return number of open clients */
1029 int winbindd_num_clients(void)
1031 return _num_clients;
1034 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1035 TALLOC_CTX *mem_ctx,
1036 const struct dom_sid *user_sid,
1037 uint32_t *p_num_groups, struct dom_sid **user_sids)
1039 struct netr_SamInfo3 *info3 = NULL;
1040 NTSTATUS status = NT_STATUS_NO_MEMORY;
1041 uint32_t num_groups = 0;
1043 DEBUG(3,(": lookup_usergroups_cached\n"));
1048 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1050 if (info3 == NULL) {
1051 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1054 if (info3->base.groups.count == 0) {
1056 return NT_STATUS_UNSUCCESSFUL;
1059 /* Skip Domain local groups outside our domain.
1060 We'll get these from the getsidaliases() RPC call. */
1061 status = sid_array_from_info3(mem_ctx, info3,
1066 if (!NT_STATUS_IS_OK(status)) {
1072 *p_num_groups = num_groups;
1073 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1075 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1080 /*********************************************************************
1081 We use this to remove spaces from user and group names
1082 ********************************************************************/
1084 NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
1085 struct winbindd_domain *domain,
1091 if (!name || !normalized) {
1092 return NT_STATUS_INVALID_PARAMETER;
1095 if (!lp_winbind_normalize_names()) {
1096 return NT_STATUS_PROCEDURE_NOT_FOUND;
1099 /* Alias support and whitespace replacement are mutually
1102 nt_status = resolve_username_to_alias(mem_ctx, domain,
1104 if (NT_STATUS_IS_OK(nt_status)) {
1105 /* special return code to let the caller know we
1106 mapped to an alias */
1107 return NT_STATUS_FILE_RENAMED;
1110 /* check for an unreachable domain */
1112 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1113 DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
1115 set_domain_offline(domain);
1119 /* deal with whitespace */
1121 *normalized = talloc_strdup(mem_ctx, name);
1122 if (!(*normalized)) {
1123 return NT_STATUS_NO_MEMORY;
1126 all_string_sub( *normalized, " ", "_", 0 );
1128 return NT_STATUS_OK;
1131 /*********************************************************************
1132 We use this to do the inverse of normalize_name_map()
1133 ********************************************************************/
1135 NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
1140 struct winbindd_domain *domain = find_our_domain();
1142 if (!name || !normalized) {
1143 return NT_STATUS_INVALID_PARAMETER;
1146 if (!lp_winbind_normalize_names()) {
1147 return NT_STATUS_PROCEDURE_NOT_FOUND;
1150 /* Alias support and whitespace replacement are mutally
1153 /* When mapping from an alias to a username, we don't know the
1154 domain. But we only need a domain structure to cache
1155 a successful lookup , so just our own domain structure for
1158 nt_status = resolve_alias_to_username(mem_ctx, domain,
1160 if (NT_STATUS_IS_OK(nt_status)) {
1161 /* Special return code to let the caller know we mapped
1163 return NT_STATUS_FILE_RENAMED;
1166 /* check for an unreachable domain */
1168 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1169 DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
1171 set_domain_offline(domain);
1175 /* deal with whitespace */
1177 *normalized = talloc_strdup(mem_ctx, name);
1178 if (!(*normalized)) {
1179 return NT_STATUS_NO_MEMORY;
1182 all_string_sub(*normalized, "_", " ", 0);
1184 return NT_STATUS_OK;
1187 /*********************************************************************
1188 ********************************************************************/
1190 bool winbindd_can_contact_domain(struct winbindd_domain *domain)
1192 struct winbindd_tdc_domain *tdc = NULL;
1193 TALLOC_CTX *frame = talloc_stackframe();
1196 /* We can contact the domain if it is our primary domain */
1198 if (domain->primary) {
1202 /* Trust the TDC cache and not the winbindd_domain flags */
1204 if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
1205 DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
1210 /* Can always contact a domain that is in out forest */
1212 if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
1218 * On a _member_ server, we cannot contact the domain if it
1219 * is running AD and we have no inbound trust.
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));
1231 /* Assume everything else is ok (probably not true but what
1237 talloc_destroy(frame);
1242 /*********************************************************************
1243 ********************************************************************/
1245 bool winbindd_internal_child(struct winbindd_child *child)
1247 if ((child == idmap_child()) || (child == locator_child())) {
1254 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1256 /*********************************************************************
1257 ********************************************************************/
1259 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1262 char addr[INET6_ADDRSTRLEN];
1263 const char *kdc = NULL;
1266 if (!domain || !domain->alt_name || !*domain->alt_name) {
1270 if (domain->initialized && !domain->active_directory) {
1271 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1276 print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
1279 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1281 kdc = domain->dcname;
1284 if (!kdc || !*kdc) {
1285 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1290 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1291 domain->alt_name) == -1) {
1295 DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1298 setenv(var, kdc, 1);
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)
1323 if (!domain || !domain->alt_name || !*domain->alt_name) {
1327 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1328 domain->alt_name) == -1) {
1337 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1342 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
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()) {
1366 if (get_global_winbindd_state_offline()) {
1369 return !domain->online;