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"
27 #include "../libcli/auth/pam_errors.h"
28 #include "passdb/machine_sid.h"
31 #define DBGC_CLASS DBGC_WINBIND
33 extern struct winbindd_methods cache_methods;
36 * @file winbindd_util.cq
38 * Winbind daemon for NT domain authentication nss module.
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)
53 if ((!_domain_list) && (!init_domain_list())) {
54 smb_panic("Init_domain_list failed");
60 /* Free all entries in the trusted domain list */
62 static void free_domain_list(void)
64 struct winbindd_domain *domain = _domain_list;
67 struct winbindd_domain *next = domain->next;
69 DLIST_REMOVE(_domain_list, domain);
75 static bool is_internal_domain(const struct dom_sid *sid)
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)
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;
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));
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))
127 if (alternative_name && *alternative_name)
129 if (strequal(alternative_name, domain->name) ||
130 strequal(alternative_name, domain->alt_name))
138 if (is_null_sid(sid)) {
142 if (dom_sid_equal(sid, &domain->sid)) {
148 if (domain != NULL) {
150 * We found a match. Possibly update the SID
153 && dom_sid_equal(&domain->sid, &global_sid_NULL)) {
154 sid_copy( &domain->sid, sid );
159 /* Create new domain entry */
160 domain = talloc_zero(NULL, struct winbindd_domain);
161 if (domain == NULL) {
165 domain->children = talloc_zero_array(domain,
166 struct winbindd_child,
167 lp_winbind_max_domain_connections());
168 if (domain->children == NULL) {
173 domain->name = talloc_strdup(domain, domain_name);
174 if (domain->name == NULL) {
179 if (alternative_name) {
180 domain->alt_name = talloc_strdup(domain, alternative_name);
181 if (domain->alt_name == 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;
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",
216 if (idmap_config_option == NULL) {
217 DEBUG(0, ("talloc failed, not looking for idmap config\n"));
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"));
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));
233 if (low_id > high_id) {
234 DEBUG(1, ("invalid range in %s: %s\n",
235 idmap_config_option, param));
238 domain->have_idmap_config = true;
239 domain->id_range_low = low_id;
240 domain->id_range_high = high_id;
245 DEBUG(2,("Added domain %s %s %s\n",
246 domain->name, domain->alt_name,
247 &domain->sid?sid_string_dbg(&domain->sid):""));
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);
280 DEBUG(0, ("talloc failed\n"));
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);
291 DEBUG(1, ("wb_domain_request_send failed\n"));
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;
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"));
313 p = (char *)response->extra_data.data;
315 while ((p != NULL) && (*p != '\0')) {
316 char *q, *sidstr, *alt_name;
318 struct winbindd_domain *domain;
319 char *alternate_name = NULL;
322 alt_name = strchr(p, '\\');
323 if (alt_name == NULL) {
324 DEBUG(0, ("Got invalid trustdom response\n"));
331 sidstr = strchr(alt_name, '\\');
332 if (sidstr == NULL) {
333 DEBUG(0, ("Got invalid trustdom response\n"));
340 q = strchr(sidstr, '\n');
344 if (!string_to_sid(&sid, sidstr)) {
345 DEBUG(0, ("Got invalid trustdom response\n"));
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
363 domain = add_trusted_domain(p, alternate_name,
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);
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
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();
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();
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;
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 ) )
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) ) {
438 /* Here's the forest root */
440 d = find_domain_from_name_noinit( dom_list[i].domain_name );
443 d = add_trusted_domain( dom_list[i].domain_name,
444 dom_list[i].dns_name,
448 setup_domain_child(d);
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 );
469 TALLOC_FREE( dom_list );
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;
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 ) )
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 ) )
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
515 d = add_trusted_domain( dom_list[i].domain_name,
516 dom_list[i].dns_name,
520 setup_domain_child(d);
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 );
539 /*********************************************************************
540 The process of updating the trusted domain list is a three step
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)
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.
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;
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
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 */
630 domain = add_trusted_domain("BUILTIN", NULL, &cache_methods,
631 &global_sid_Builtin);
633 setup_domain_child(domain);
638 domain = add_trusted_domain(get_global_sam_name(), NULL,
639 &cache_methods, get_global_sam_sid());
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"));
654 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
655 &cache_methods, &our_sid);
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
665 set_domain_online_request(domain);
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().
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))) {
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);
712 if (!domain->initialized)
713 init_dc_connection(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)
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);
747 if (!domain->initialized)
748 init_dc_connection(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) {
764 smb_panic("Could not find our domain");
768 struct winbindd_domain *find_root_domain(void)
770 struct winbindd_domain *ours = find_our_domain();
772 if (ours->forest_name == 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");
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
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
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 )
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) )
858 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
862 /* only left with a domain controller */
864 if ( strequal(get_global_sam_name(), domain) ) {
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());
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;
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)) {
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
918 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
920 if (!parse_domain_user(username_inout, domain, user)) {
923 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
924 domain, *lp_winbind_separator(),
930 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
931 'winbind separator' options.
933 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
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)
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));
953 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
954 domain, *lp_winbind_separator(),
960 * talloc version of fill_domain_username()
961 * return NULL on talloc failure.
963 char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
968 char *tmp_user, *name;
970 tmp_user = talloc_strdup(mem_ctx, user);
971 if (!strlower_m(tmp_user)) {
972 TALLOC_FREE(tmp_user);
976 if (can_assume && assume_domain(domain)) {
979 name = talloc_asprintf(mem_ctx, "%s%c%s",
981 *lp_winbind_separator(),
983 TALLOC_FREE(tmp_user);
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);
1011 /* Remove a client from the list */
1013 void winbindd_remove_client(struct winbindd_cli_state *cli)
1015 DLIST_REMOVE(_client_list, cli);
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"));
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) {
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,
1064 if (!NT_STATUS_IS_OK(status)) {
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"));
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,
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
1100 nt_status = resolve_username_to_alias(mem_ctx, domain,
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",
1113 set_domain_offline(domain);
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,
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
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
1156 nt_status = resolve_alias_to_username(mem_ctx, domain,
1158 if (NT_STATUS_IS_OK(nt_status)) {
1159 /* Special return code to let the caller know we mapped
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",
1169 set_domain_offline(domain);
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();
1194 /* We can contact the domain if it is our primary domain */
1196 if (domain->primary) {
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",
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;
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)
1395 while (p[0] != '\0') {
1397 const char *q = NULL;
1399 if (!dom_sid_parse_endp(p, &sid, &q)) {
1400 DEBUG(1, ("Could not parse sid %s\n", p));
1403 if ((q == NULL) || (q[0] != '\n')) {
1404 DEBUG(1, ("Got invalid sidstr: %s\n", p));
1407 if (!NT_STATUS_IS_OK(add_sid_to_array(mem_ctx, &sid, sids,