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/>.
25 #include "lib/util_unixsids.h"
27 #include "../libcli/security/security.h"
28 #include "../libcli/auth/pam_errors.h"
29 #include "passdb/machine_sid.h"
31 #include "source4/lib/messaging/messaging.h"
32 #include "librpc/gen_ndr/ndr_lsa.h"
33 #include "auth/credentials/credentials.h"
34 #include "libsmb/samlogon_cache.h"
37 #define DBGC_CLASS DBGC_WINBIND
39 static struct winbindd_domain *
40 add_trusted_domain_from_tdc(const struct winbindd_tdc_domain *tdc);
43 * @file winbindd_util.c
45 * Winbind daemon for NT domain authentication nss module.
49 /* The list of trusted domains. Note that the list can be deleted and
50 recreated using the init_domain_list() function so pointers to
51 individual winbindd_domain structures cannot be made. Keep a copy of
52 the domain name instead. */
54 static struct winbindd_domain *_domain_list = NULL;
56 struct winbindd_domain *domain_list(void)
60 if ((!_domain_list) && (!init_domain_list())) {
61 smb_panic("Init_domain_list failed");
67 /* Free all entries in the trusted domain list */
69 static void free_domain_list(void)
71 struct winbindd_domain *domain = _domain_list;
74 struct winbindd_domain *next = domain->next;
76 DLIST_REMOVE(_domain_list, domain);
83 * Iterator for winbindd's domain list.
84 * To be used (e.g.) in tevent based loops.
86 struct winbindd_domain *wb_next_domain(struct winbindd_domain *domain)
89 domain = domain_list();
91 domain = domain->next;
94 if ((domain != NULL) &&
95 (lp_server_role() != ROLE_ACTIVE_DIRECTORY_DC) &&
96 sid_check_is_our_sam(&domain->sid))
98 domain = domain->next;
104 static bool is_internal_domain(const struct dom_sid *sid)
109 return (sid_check_is_our_sam(sid) || sid_check_is_builtin(sid));
112 static bool is_in_internal_domain(const struct dom_sid *sid)
117 return (sid_check_is_in_our_sam(sid) || sid_check_is_in_builtin(sid));
121 /* Add a trusted domain to our list of domains.
122 If the domain already exists in the list,
123 return it and don't re-initialize. */
125 static struct winbindd_domain *
126 add_trusted_domain(const char *domain_name, const char *alt_name,
127 const struct dom_sid *sid)
129 struct winbindd_tdc_domain tdc;
133 tdc.domain_name = domain_name;
134 tdc.dns_name = alt_name;
136 sid_copy(&tdc.sid, sid);
139 return add_trusted_domain_from_tdc(&tdc);
142 /* Add a trusted domain out of a trusted domain cache
145 static struct winbindd_domain *
146 add_trusted_domain_from_tdc(const struct winbindd_tdc_domain *tdc)
148 struct winbindd_domain *domain;
149 const char *alternative_name = NULL;
150 const char **ignored_domains, **dom;
151 int role = lp_server_role();
152 const char *domain_name = tdc->domain_name;
153 const struct dom_sid *sid = &tdc->sid;
155 if (is_null_sid(sid)) {
159 ignored_domains = lp_parm_string_list(-1, "winbind", "ignore domains", NULL);
160 for (dom=ignored_domains; dom && *dom; dom++) {
161 if (gen_fnmatch(*dom, domain_name) == 0) {
162 DEBUG(2,("Ignoring domain '%s'\n", domain_name));
167 /* use alt_name if available to allow DNS lookups */
169 if (tdc->dns_name && *tdc->dns_name) {
170 alternative_name = tdc->dns_name;
173 /* We can't call domain_list() as this function is called from
174 init_domain_list() and we'll get stuck in a loop. */
175 for (domain = _domain_list; domain; domain = domain->next) {
176 if (strequal(domain_name, domain->name) ||
177 strequal(domain_name, domain->alt_name))
182 if (alternative_name) {
183 if (strequal(alternative_name, domain->name) ||
184 strequal(alternative_name, domain->alt_name))
191 if (dom_sid_equal(sid, &domain->sid)) {
197 if (domain != NULL) {
199 * We found a match on domain->name or
200 * domain->alt_name. Possibly update the SID
201 * if the stored SID was the NULL SID
202 * and return the matching entry.
205 && dom_sid_equal(&domain->sid, &global_sid_NULL)) {
206 sid_copy( &domain->sid, sid );
211 /* Create new domain entry */
212 domain = talloc_zero(NULL, struct winbindd_domain);
213 if (domain == NULL) {
217 domain->children = talloc_zero_array(domain,
218 struct winbindd_child,
219 lp_winbind_max_domain_connections());
220 if (domain->children == NULL) {
225 domain->name = talloc_strdup(domain, domain_name);
226 if (domain->name == NULL) {
231 if (alternative_name) {
232 domain->alt_name = talloc_strdup(domain, alternative_name);
233 if (domain->alt_name == NULL) {
239 domain->backend = NULL;
240 domain->internal = is_internal_domain(sid);
241 domain->sequence_number = DOM_SEQUENCE_NONE;
242 domain->last_seq_check = 0;
243 domain->initialized = false;
244 domain->online = is_internal_domain(sid);
245 domain->check_online_timeout = 0;
246 domain->dc_probe_pid = (pid_t)-1;
248 sid_copy(&domain->sid, sid);
250 domain->domain_flags = tdc->trust_flags;
251 domain->domain_type = tdc->trust_type;
252 domain->domain_trust_attribs = tdc->trust_attribs;
254 /* Is this our primary domain ? */
255 if (role == ROLE_DOMAIN_MEMBER) {
256 domain->primary = strequal(domain_name, lp_workgroup());
258 domain->primary = strequal(domain_name, get_global_sam_name());
261 if (domain->primary) {
262 if (role == ROLE_ACTIVE_DIRECTORY_DC) {
263 domain->active_directory = true;
265 if (lp_security() == SEC_ADS) {
266 domain->active_directory = true;
268 } else if (!domain->internal) {
269 if (domain->domain_type == LSA_TRUST_TYPE_UPLEVEL) {
270 domain->active_directory = true;
274 /* Link to domain list */
275 DLIST_ADD_END(_domain_list, domain);
277 wcache_tdc_add_domain( domain );
279 setup_domain_child(domain);
282 ("Added domain %s %s %s\n", domain->name, domain->alt_name,
283 !is_null_sid(&domain->sid) ? sid_string_dbg(&domain->sid) : ""));
288 bool domain_is_forest_root(const struct winbindd_domain *domain)
290 const uint32_t fr_flags =
291 (NETR_TRUST_FLAG_TREEROOT|NETR_TRUST_FLAG_IN_FOREST);
293 return ((domain->domain_flags & fr_flags) == fr_flags);
296 /********************************************************************
297 rescan our domains looking for new trusted domains
298 ********************************************************************/
300 struct trustdom_state {
301 struct winbindd_domain *domain;
302 struct winbindd_request request;
305 static void trustdom_list_done(struct tevent_req *req);
306 static void rescan_forest_root_trusts( void );
307 static void rescan_forest_trusts( void );
309 static void add_trusted_domains( struct winbindd_domain *domain )
311 struct trustdom_state *state;
312 struct tevent_req *req;
314 state = talloc_zero(NULL, struct trustdom_state);
316 DEBUG(0, ("talloc failed\n"));
319 state->domain = domain;
321 state->request.length = sizeof(state->request);
322 state->request.cmd = WINBINDD_LIST_TRUSTDOM;
324 req = wb_domain_request_send(state, server_event_context(),
325 domain, &state->request);
327 DEBUG(1, ("wb_domain_request_send failed\n"));
331 tevent_req_set_callback(req, trustdom_list_done, state);
334 static void trustdom_list_done(struct tevent_req *req)
336 struct trustdom_state *state = tevent_req_callback_data(
337 req, struct trustdom_state);
338 struct winbindd_response *response;
341 struct winbindd_tdc_domain trust_params = {0};
343 bool within_forest = false;
346 * Only when we enumerate our primary domain
347 * or our forest root domain, we should keep
348 * the NETR_TRUST_FLAG_IN_FOREST flag, in
349 * all other cases we need to clear it as the domain
350 * is not part of our forest.
352 if (state->domain->primary) {
353 within_forest = true;
354 } else if (domain_is_forest_root(state->domain)) {
355 within_forest = true;
358 res = wb_domain_request_recv(req, state, &response, &err);
359 if ((res == -1) || (response->result != WINBINDD_OK)) {
360 DBG_WARNING("Could not receive trusts for domain %s\n",
361 state->domain->name);
366 if (response->length < sizeof(struct winbindd_response)) {
367 DBG_ERR("ill-formed trustdom response - short length\n");
372 extra_len = response->length - sizeof(struct winbindd_response);
374 p = (char *)response->extra_data.data;
376 while ((p - (char *)response->extra_data.data) < extra_len) {
377 char *q, *sidstr, *alt_name;
379 DBG_DEBUG("parsing response line '%s'\n", p);
381 ZERO_STRUCT(trust_params);
382 trust_params.domain_name = p;
384 alt_name = strchr(p, '\\');
385 if (alt_name == NULL) {
386 DBG_ERR("Got invalid trustdom response\n");
393 sidstr = strchr(alt_name, '\\');
394 if (sidstr == NULL) {
395 DBG_ERR("Got invalid trustdom response\n");
402 /* use the real alt_name if we have one, else pass in NULL */
403 if (!strequal(alt_name, "(null)")) {
404 trust_params.dns_name = alt_name;
407 q = strtok(sidstr, "\\");
409 DBG_ERR("Got invalid trustdom response\n");
413 if (!string_to_sid(&trust_params.sid, sidstr)) {
414 DEBUG(0, ("Got invalid trustdom response\n"));
418 q = strtok(NULL, "\\");
420 DBG_ERR("Got invalid trustdom response\n");
424 trust_params.trust_flags = (uint32_t)strtoul(q, NULL, 10);
426 q = strtok(NULL, "\\");
428 DBG_ERR("Got invalid trustdom response\n");
432 trust_params.trust_type = (uint32_t)strtoul(q, NULL, 10);
434 q = strtok(NULL, "\n");
436 DBG_ERR("Got invalid trustdom response\n");
440 trust_params.trust_attribs = (uint32_t)strtoul(q, NULL, 10);
442 if (!within_forest) {
443 trust_params.trust_flags &= ~NETR_TRUST_FLAG_IN_FOREST;
446 if (!state->domain->primary) {
447 trust_params.trust_flags &= ~NETR_TRUST_FLAG_PRIMARY;
451 * We always call add_trusted_domain() cause on an existing
452 * domain structure, it will update the SID if necessary.
453 * This is important because we need the SID for sibling
456 (void)add_trusted_domain_from_tdc(&trust_params);
458 p = q + strlen(q) + 1;
462 Cases to consider when scanning trusts:
463 (a) we are calling from a child domain (primary && !forest_root)
464 (b) we are calling from the root of the forest (primary && forest_root)
465 (c) we are calling from a trusted forest domain (!primary
469 if (state->domain->primary) {
470 /* If this is our primary domain and we are not in the
471 forest root, we have to scan the root trusts first */
473 if (!domain_is_forest_root(state->domain))
474 rescan_forest_root_trusts();
476 rescan_forest_trusts();
478 } else if (domain_is_forest_root(state->domain)) {
479 /* Once we have done root forest trust search, we can
480 go on to search the trusted forests */
482 rescan_forest_trusts();
490 /********************************************************************
491 Scan the trusts of our forest root
492 ********************************************************************/
494 static void rescan_forest_root_trusts( void )
496 struct winbindd_tdc_domain *dom_list = NULL;
497 size_t num_trusts = 0;
500 /* The only transitive trusts supported by Windows 2003 AD are
501 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
502 first two are handled in forest and listed by
503 DsEnumerateDomainTrusts(). Forest trusts are not so we
504 have to do that ourselves. */
506 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
509 for ( i=0; i<num_trusts; i++ ) {
510 struct winbindd_domain *d = NULL;
512 /* Find the forest root. Don't necessarily trust
513 the domain_list() as our primary domain may not
514 have been initialized. */
516 if ( !(dom_list[i].trust_flags & NETR_TRUST_FLAG_TREEROOT) ) {
520 /* Here's the forest root */
522 d = find_domain_from_name_noinit( dom_list[i].domain_name );
525 d = add_trusted_domain_from_tdc(&dom_list[i]);
532 DEBUG(10,("rescan_forest_root_trusts: Following trust path "
533 "for domain tree root %s (%s)\n",
534 d->name, d->alt_name ));
536 d->domain_flags = dom_list[i].trust_flags;
537 d->domain_type = dom_list[i].trust_type;
538 d->domain_trust_attribs = dom_list[i].trust_attribs;
540 add_trusted_domains( d );
545 TALLOC_FREE( dom_list );
550 /********************************************************************
551 scan the transitive forest trusts (not our own)
552 ********************************************************************/
555 static void rescan_forest_trusts( void )
557 struct winbindd_domain *d = NULL;
558 struct winbindd_tdc_domain *dom_list = NULL;
559 size_t num_trusts = 0;
562 /* The only transitive trusts supported by Windows 2003 AD are
563 (a) Parent-Child, (b) Tree-Root, and (c) Forest. The
564 first two are handled in forest and listed by
565 DsEnumerateDomainTrusts(). Forest trusts are not so we
566 have to do that ourselves. */
568 if ( !wcache_tdc_fetch_list( &dom_list, &num_trusts ) )
571 for ( i=0; i<num_trusts; i++ ) {
572 uint32_t flags = dom_list[i].trust_flags;
573 uint32_t type = dom_list[i].trust_type;
574 uint32_t attribs = dom_list[i].trust_attribs;
576 d = find_domain_from_name_noinit( dom_list[i].domain_name );
578 /* ignore our primary and internal domains */
580 if ( d && (d->internal || d->primary ) )
583 if ( (flags & NETR_TRUST_FLAG_INBOUND) &&
584 (type == LSA_TRUST_TYPE_UPLEVEL) &&
585 (attribs == LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) )
587 /* add the trusted domain if we don't know
591 d = add_trusted_domain_from_tdc(&dom_list[i]);
598 DEBUG(10,("Following trust path for domain %s (%s)\n",
599 d->name, d->alt_name ));
600 add_trusted_domains( d );
604 TALLOC_FREE( dom_list );
609 /*********************************************************************
610 The process of updating the trusted domain list is a three step
613 (b) ask the root domain in our forest
614 (c) ask the a DC in any Win2003 trusted forests
615 *********************************************************************/
617 void rescan_trusted_domains(struct tevent_context *ev, struct tevent_timer *te,
618 struct timeval now, void *private_data)
622 /* I use to clear the cache here and start over but that
623 caused problems in child processes that needed the
624 trust dom list early on. Removing it means we
625 could have some trusted domains listed that have been
626 removed from our primary domain's DC until a full
627 restart. This should be ok since I think this is what
628 Windows does as well. */
630 /* this will only add new domains we didn't already know about
631 in the domain_list()*/
633 add_trusted_domains( find_our_domain() );
635 te = tevent_add_timer(
636 ev, NULL, timeval_current_ofs(WINBINDD_RESCAN_FREQ, 0),
637 rescan_trusted_domains, NULL);
639 * If te == NULL, there's not much we can do here. Don't fail, the
640 * only thing we miss is new trusted domains.
646 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
647 struct winbindd_cli_state *state)
649 /* Ensure null termination */
650 state->request->domain_name
651 [sizeof(state->request->domain_name)-1]='\0';
652 state->request->data.init_conn.dcname
653 [sizeof(state->request->data.init_conn.dcname)-1]='\0';
655 if (strlen(state->request->data.init_conn.dcname) > 0) {
656 fstrcpy(domain->dcname, state->request->data.init_conn.dcname);
659 init_dc_connection(domain, false);
661 if (!domain->initialized) {
662 /* If we return error here we can't do any cached authentication,
663 but we may be in disconnected mode and can't initialize correctly.
664 Do what the previous code did and just return without initialization,
665 once we go online we'll re-initialize.
667 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
668 "online = %d\n", domain->name, (int)domain->online ));
671 fstrcpy(state->response->data.domain_info.name, domain->name);
672 fstrcpy(state->response->data.domain_info.alt_name, domain->alt_name);
673 sid_to_fstring(state->response->data.domain_info.sid, &domain->sid);
675 state->response->data.domain_info.native_mode
676 = domain->native_mode;
677 state->response->data.domain_info.active_directory
678 = domain->active_directory;
679 state->response->data.domain_info.primary
685 static void wb_imsg_new_trusted_domain(struct imessaging_context *msg,
688 struct server_id server_id,
691 TALLOC_CTX *frame = talloc_stackframe();
692 struct lsa_TrustDomainInfoInfoEx info;
693 enum ndr_err_code ndr_err;
694 struct winbindd_domain *d = NULL;
696 DEBUG(5, ("wb_imsg_new_trusted_domain\n"));
703 ndr_err = ndr_pull_struct_blob_all(data, frame, &info,
704 (ndr_pull_flags_fn_t)ndr_pull_lsa_TrustDomainInfoInfoEx);
705 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
710 d = find_domain_from_name_noinit(info.netbios_name.string);
716 d = add_trusted_domain(info.netbios_name.string,
717 info.domain_name.string,
734 if (info.trust_direction & LSA_TRUST_DIRECTION_INBOUND) {
735 d->domain_flags |= NETR_TRUST_FLAG_INBOUND;
737 if (info.trust_direction & LSA_TRUST_DIRECTION_OUTBOUND) {
738 d->domain_flags |= NETR_TRUST_FLAG_OUTBOUND;
740 if (info.trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST) {
741 d->domain_flags |= NETR_TRUST_FLAG_IN_FOREST;
743 d->domain_type = info.trust_type;
744 d->domain_trust_attribs = info.trust_attributes;
750 * We did not get the secret when we queried secrets.tdb, so read it
751 * from secrets.tdb and re-sync the databases
753 static bool migrate_secrets_tdb_to_ldb(struct winbindd_domain *domain)
756 struct cli_credentials *creds;
757 NTSTATUS can_migrate = pdb_get_trust_credentials(domain->name,
758 NULL, domain, &creds);
759 if (!NT_STATUS_IS_OK(can_migrate)) {
760 DEBUG(0, ("Failed to fetch our own, local AD domain join "
761 "password for winbindd's internal use, both from "
762 "secrets.tdb and secrets.ldb: %s\n",
763 nt_errstr(can_migrate)));
768 * NOTE: It is very unlikely we end up here if there is an
769 * oldpass, because a new password is created at
770 * classicupgrade, so this is not a concern.
772 ok = secrets_store_machine_pw_sync(cli_credentials_get_password(creds),
774 cli_credentials_get_domain(creds),
775 cli_credentials_get_realm(creds),
776 cli_credentials_get_salt_principal(creds),
777 0, /* Supported enc types, unused */
779 cli_credentials_get_password_last_changed_time(creds),
780 cli_credentials_get_secure_channel_type(creds),
781 false /* do_delete: Do not delete */);
784 DEBUG(0, ("Failed to write our our own, "
785 "local AD domain join password for "
786 "winbindd's internal use into secrets.tdb\n"));
792 /* Look up global info for the winbind daemon */
793 bool init_domain_list(void)
795 int role = lp_server_role();
796 struct pdb_domain_info *pdb_domain_info = NULL;
799 /* Free existing list */
804 (void)add_trusted_domain("BUILTIN", NULL, &global_sid_Builtin);
809 * In case the passdb backend is passdb_dsdb the domain SID comes from
810 * dsdb, not from secrets.tdb. As we use the domain SID in various
811 * places, we must ensure the domain SID is migrated from dsdb to
812 * secrets.tdb before get_global_sam_sid() is called the first time.
814 * The migration is done as part of the passdb_dsdb initialisation,
815 * calling pdb_get_domain_info() triggers it.
817 pdb_domain_info = pdb_get_domain_info(talloc_tos());
819 if ( role == ROLE_ACTIVE_DIRECTORY_DC ) {
820 struct winbindd_domain *domain;
821 enum netr_SchannelType sec_chan_type;
822 const char *account_name;
823 struct samr_Password current_nt_hash;
826 if (pdb_domain_info == NULL) {
827 DEBUG(0, ("Failed to fetch our own, local AD "
828 "domain info from sam.ldb\n"));
831 domain = add_trusted_domain(pdb_domain_info->name,
832 pdb_domain_info->dns_domain,
833 &pdb_domain_info->sid);
834 TALLOC_FREE(pdb_domain_info);
835 if (domain == NULL) {
836 DEBUG(0, ("Failed to add our own, local AD "
837 "domain to winbindd's internal list\n"));
842 * We need to call this to find out if we are an RODC
844 ok = get_trust_pw_hash(domain->name,
845 current_nt_hash.hash,
850 * If get_trust_pw_hash() fails, then try and
851 * fetch the password from the more recent of
852 * secrets.{ldb,tdb} using the
853 * pdb_get_trust_credentials()
855 ok = migrate_secrets_tdb_to_ldb(domain);
858 DEBUG(0, ("Failed to migrate our own, "
859 "local AD domain join password for "
860 "winbindd's internal use into "
864 ok = get_trust_pw_hash(domain->name,
865 current_nt_hash.hash,
869 DEBUG(0, ("Failed to find our our own, just "
870 "written local AD domain join "
871 "password for winbindd's internal "
872 "use in secrets.tdb\n"));
876 if (sec_chan_type == SEC_CHAN_RODC) {
881 (void)add_trusted_domain(get_global_sam_name(), NULL,
882 get_global_sam_sid());
884 /* Add ourselves as the first entry. */
886 if ( role == ROLE_DOMAIN_MEMBER ) {
887 struct winbindd_domain *domain;
888 struct dom_sid our_sid;
890 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
891 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
895 domain = add_trusted_domain(lp_workgroup(), lp_realm(),
898 /* Even in the parent winbindd we'll need to
899 talk to the DC, so try and see if we can
900 contact it. Theoretically this isn't neccessary
901 as the init_dc_connection() in init_child_recv()
902 will do this, but we can start detecting the DC
904 set_domain_online_request(domain);
908 status = imessaging_register(winbind_imessaging_context(), NULL,
909 MSG_WINBIND_NEW_TRUSTED_DOMAIN,
910 wb_imsg_new_trusted_domain);
911 if (!NT_STATUS_IS_OK(status)) {
912 DEBUG(0, ("imessaging_register(MSG_WINBIND_NEW_TRUSTED_DOMAIN) - %s\n",
921 * Given a domain name, return the struct winbindd domain info for it
923 * @note Do *not* pass lp_workgroup() to this function. domain_list
924 * may modify it's value, and free that pointer. Instead, our local
925 * domain may be found by calling find_our_domain().
929 * @return The domain structure for the named domain, if it is working.
932 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
934 struct winbindd_domain *domain;
936 /* Search through list */
938 for (domain = domain_list(); domain != NULL; domain = domain->next) {
939 if (strequal(domain_name, domain->name)) {
942 if (domain->alt_name == NULL) {
945 if (strequal(domain_name, domain->alt_name)) {
955 struct winbindd_domain *find_domain_from_name(const char *domain_name)
957 struct winbindd_domain *domain;
959 domain = find_domain_from_name_noinit(domain_name);
964 if (!domain->initialized)
965 init_dc_connection(domain, false);
970 /* Given a domain sid, return the struct winbindd domain info for it */
972 struct winbindd_domain *find_domain_from_sid_noinit(const struct dom_sid *sid)
974 struct winbindd_domain *domain;
976 /* Search through list */
978 for (domain = domain_list(); domain != NULL; domain = domain->next) {
979 if (dom_sid_compare_domain(sid, &domain->sid) == 0)
988 /* Given a domain sid, return the struct winbindd domain info for it */
990 struct winbindd_domain *find_domain_from_sid(const struct dom_sid *sid)
992 struct winbindd_domain *domain;
994 domain = find_domain_from_sid_noinit(sid);
999 if (!domain->initialized)
1000 init_dc_connection(domain, false);
1005 struct winbindd_domain *find_our_domain(void)
1007 struct winbindd_domain *domain;
1009 /* Search through list */
1011 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1012 if (domain->primary)
1016 smb_panic("Could not find our domain");
1020 /* Find the appropriate domain to lookup a name or SID */
1022 struct winbindd_domain *find_lookup_domain_from_sid(const struct dom_sid *sid)
1024 DBG_DEBUG("SID [%s]\n", sid_string_dbg(sid));
1027 * SIDs in the S-1-22-{1,2} domain and well-known SIDs should be handled
1031 if ( sid_check_is_in_unix_groups(sid) ||
1032 sid_check_is_unix_groups(sid) ||
1033 sid_check_is_in_unix_users(sid) ||
1034 sid_check_is_unix_users(sid) ||
1035 sid_check_is_wellknown_domain(sid, NULL) ||
1036 sid_check_is_in_wellknown_domain(sid) )
1038 return find_domain_from_sid(get_global_sam_sid());
1041 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
1042 * one to contact the external DC's. On member servers the internal
1043 * domains are different: These are part of the local SAM. */
1045 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
1046 DEBUG(10, ("calling find_domain_from_sid\n"));
1047 return find_domain_from_sid(sid);
1050 /* On a member server a query for SID or name can always go to our
1053 DEBUG(10, ("calling find_our_domain\n"));
1054 return find_our_domain();
1057 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
1059 if ( strequal(domain_name, unix_users_domain_name() ) ||
1060 strequal(domain_name, unix_groups_domain_name() ) )
1063 * The "Unix User" and "Unix Group" domain our handled by
1066 return find_domain_from_name_noinit( get_global_sam_name() );
1069 if (IS_DC || strequal(domain_name, "BUILTIN") ||
1070 strequal(domain_name, get_global_sam_name()))
1071 return find_domain_from_name_noinit(domain_name);
1074 return find_our_domain();
1077 /* Is this a domain which we may assume no DOMAIN\ prefix? */
1079 static bool assume_domain(const char *domain)
1081 /* never assume the domain on a standalone server */
1083 if ( lp_server_role() == ROLE_STANDALONE )
1086 /* domain member servers may possibly assume for the domain name */
1088 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
1089 if ( !strequal(lp_workgroup(), domain) )
1092 if ( lp_winbind_use_default_domain() )
1096 /* only left with a domain controller */
1098 if ( strequal(get_global_sam_name(), domain) ) {
1105 /* Parse a string of the form DOMAIN\user into a domain and a user */
1107 bool parse_domain_user(const char *domuser, fstring domain, fstring user)
1109 char *p = strchr(domuser,*lp_winbind_separator());
1112 fstrcpy(user, domuser);
1113 p = strchr(domuser, '@');
1115 if ( assume_domain(lp_workgroup()) && p == NULL) {
1116 fstrcpy(domain, lp_workgroup());
1117 } else if (p != NULL) {
1118 fstrcpy(domain, p + 1);
1119 user[PTR_DIFF(p, domuser)] = 0;
1125 fstrcpy(domain, domuser);
1126 domain[PTR_DIFF(p, domuser)] = 0;
1129 return strupper_m(domain);
1132 bool parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
1133 char **domain, char **user)
1135 fstring fstr_domain, fstr_user;
1136 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
1139 *domain = talloc_strdup(mem_ctx, fstr_domain);
1140 *user = talloc_strdup(mem_ctx, fstr_user);
1141 return ((*domain != NULL) && (*user != NULL));
1144 /* Ensure an incoming username from NSS is fully qualified. Replace the
1145 incoming fstring with DOMAIN <separator> user. Returns the same
1146 values as parse_domain_user() but also replaces the incoming username.
1147 Used to ensure all names are fully qualified within winbindd.
1148 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
1149 The protocol definitions of auth_crap, chng_pswd_auth_crap
1150 really should be changed to use this instead of doing things
1153 bool canonicalize_username(fstring username_inout, fstring domain, fstring user)
1155 if (!parse_domain_user(username_inout, domain, user)) {
1158 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
1159 domain, *lp_winbind_separator(),
1165 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
1166 'winbind separator' options.
1168 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
1171 If we are a PDC or BDC, and this is for our domain, do likewise.
1173 On an AD DC we always fill DOMAIN\\USERNAME.
1175 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
1177 void fill_domain_username(fstring name, const char *domain, const char *user, bool can_assume)
1181 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
1185 fstrcpy(tmp_user, user);
1186 (void)strlower_m(tmp_user);
1188 if (can_assume && assume_domain(domain)) {
1189 strlcpy(name, tmp_user, sizeof(fstring));
1191 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
1192 domain, *lp_winbind_separator(),
1198 * talloc version of fill_domain_username()
1199 * return NULL on talloc failure.
1201 char *fill_domain_username_talloc(TALLOC_CTX *mem_ctx,
1206 char *tmp_user, *name;
1208 if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC) {
1212 tmp_user = talloc_strdup(mem_ctx, user);
1213 if (!strlower_m(tmp_user)) {
1214 TALLOC_FREE(tmp_user);
1218 if (can_assume && assume_domain(domain)) {
1221 name = talloc_asprintf(mem_ctx, "%s%c%s",
1223 *lp_winbind_separator(),
1225 TALLOC_FREE(tmp_user);
1232 * Client list accessor functions
1235 static struct winbindd_cli_state *_client_list;
1236 static int _num_clients;
1238 /* Return list of all connected clients */
1240 struct winbindd_cli_state *winbindd_client_list(void)
1242 return _client_list;
1245 /* Return list-tail of all connected clients */
1247 struct winbindd_cli_state *winbindd_client_list_tail(void)
1249 return DLIST_TAIL(_client_list);
1252 /* Return previous (read:newer) client in list */
1254 struct winbindd_cli_state *
1255 winbindd_client_list_prev(struct winbindd_cli_state *cli)
1257 return DLIST_PREV(cli);
1260 /* Add a connection to the list */
1262 void winbindd_add_client(struct winbindd_cli_state *cli)
1264 cli->last_access = time(NULL);
1265 DLIST_ADD(_client_list, cli);
1269 /* Remove a client from the list */
1271 void winbindd_remove_client(struct winbindd_cli_state *cli)
1273 DLIST_REMOVE(_client_list, cli);
1277 /* Move a client to head or list */
1279 void winbindd_promote_client(struct winbindd_cli_state *cli)
1281 cli->last_access = time(NULL);
1282 DLIST_PROMOTE(_client_list, cli);
1285 /* Return number of open clients */
1287 int winbindd_num_clients(void)
1289 return _num_clients;
1292 NTSTATUS lookup_usergroups_cached(TALLOC_CTX *mem_ctx,
1293 const struct dom_sid *user_sid,
1294 uint32_t *p_num_groups, struct dom_sid **user_sids)
1296 struct netr_SamInfo3 *info3 = NULL;
1297 NTSTATUS status = NT_STATUS_NO_MEMORY;
1298 uint32_t num_groups = 0;
1300 DEBUG(3,(": lookup_usergroups_cached\n"));
1305 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1307 if (info3 == NULL) {
1308 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1312 * Before bug #7843 the "Domain Local" groups were added with a
1313 * lookupuseraliases call, but this isn't done anymore for our domain
1314 * so we need to resolve resource groups here.
1316 * When to use Resource Groups:
1317 * http://technet.microsoft.com/en-us/library/cc753670%28v=WS.10%29.aspx
1319 status = sid_array_from_info3(mem_ctx, info3,
1324 if (!NT_STATUS_IS_OK(status)) {
1330 *p_num_groups = num_groups;
1331 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1333 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1338 /*********************************************************************
1339 We use this to remove spaces from user and group names
1340 ********************************************************************/
1342 NTSTATUS normalize_name_map(TALLOC_CTX *mem_ctx,
1343 const char *domain_name,
1347 struct winbindd_domain *domain = NULL;
1350 if (!name || !normalized) {
1351 return NT_STATUS_INVALID_PARAMETER;
1354 if (!lp_winbind_normalize_names()) {
1355 return NT_STATUS_PROCEDURE_NOT_FOUND;
1358 domain = find_domain_from_name_noinit(domain_name);
1359 if (domain == NULL) {
1360 DBG_ERR("Failed to find domain '%s'\n", domain_name);
1361 return NT_STATUS_NO_SUCH_DOMAIN;
1364 /* Alias support and whitespace replacement are mutually
1367 nt_status = resolve_username_to_alias(mem_ctx, domain,
1369 if (NT_STATUS_IS_OK(nt_status)) {
1370 /* special return code to let the caller know we
1371 mapped to an alias */
1372 return NT_STATUS_FILE_RENAMED;
1375 /* check for an unreachable domain */
1377 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1378 DEBUG(5,("normalize_name_map: Setting domain %s offline\n",
1380 set_domain_offline(domain);
1384 /* deal with whitespace */
1386 *normalized = talloc_strdup(mem_ctx, name);
1387 if (!(*normalized)) {
1388 return NT_STATUS_NO_MEMORY;
1391 all_string_sub( *normalized, " ", "_", 0 );
1393 return NT_STATUS_OK;
1396 /*********************************************************************
1397 We use this to do the inverse of normalize_name_map()
1398 ********************************************************************/
1400 NTSTATUS normalize_name_unmap(TALLOC_CTX *mem_ctx,
1405 struct winbindd_domain *domain = find_our_domain();
1407 if (!name || !normalized) {
1408 return NT_STATUS_INVALID_PARAMETER;
1411 if (!lp_winbind_normalize_names()) {
1412 return NT_STATUS_PROCEDURE_NOT_FOUND;
1415 /* Alias support and whitespace replacement are mutally
1418 /* When mapping from an alias to a username, we don't know the
1419 domain. But we only need a domain structure to cache
1420 a successful lookup , so just our own domain structure for
1423 nt_status = resolve_alias_to_username(mem_ctx, domain,
1425 if (NT_STATUS_IS_OK(nt_status)) {
1426 /* Special return code to let the caller know we mapped
1428 return NT_STATUS_FILE_RENAMED;
1431 /* check for an unreachable domain */
1433 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
1434 DEBUG(5,("normalize_name_unmap: Setting domain %s offline\n",
1436 set_domain_offline(domain);
1440 /* deal with whitespace */
1442 *normalized = talloc_strdup(mem_ctx, name);
1443 if (!(*normalized)) {
1444 return NT_STATUS_NO_MEMORY;
1447 all_string_sub(*normalized, "_", " ", 0);
1449 return NT_STATUS_OK;
1452 /*********************************************************************
1453 ********************************************************************/
1455 bool winbindd_can_contact_domain(struct winbindd_domain *domain)
1457 struct winbindd_tdc_domain *tdc = NULL;
1458 TALLOC_CTX *frame = talloc_stackframe();
1461 /* We can contact the domain if it is our primary domain */
1463 if (domain->primary) {
1468 /* Trust the TDC cache and not the winbindd_domain flags */
1470 if ((tdc = wcache_tdc_fetch_domain(frame, domain->name)) == NULL) {
1471 DEBUG(10,("winbindd_can_contact_domain: %s not found in cache\n",
1477 /* Can always contact a domain that is in out forest */
1479 if (tdc->trust_flags & NETR_TRUST_FLAG_IN_FOREST) {
1485 * On a _member_ server, we cannot contact the domain if it
1486 * is running AD and we have no inbound trust.
1490 domain->active_directory &&
1491 ((tdc->trust_flags & NETR_TRUST_FLAG_INBOUND) != NETR_TRUST_FLAG_INBOUND))
1493 DEBUG(10, ("winbindd_can_contact_domain: %s is an AD domain "
1494 "and we have no inbound trust.\n", domain->name));
1498 /* Assume everything else is ok (probably not true but what
1504 talloc_destroy(frame);
1509 /*********************************************************************
1510 ********************************************************************/
1512 bool winbindd_internal_child(struct winbindd_child *child)
1514 if ((child == idmap_child()) || (child == locator_child())) {
1521 #ifdef HAVE_KRB5_LOCATE_PLUGIN_H
1523 /*********************************************************************
1524 ********************************************************************/
1526 static void winbindd_set_locator_kdc_env(const struct winbindd_domain *domain)
1529 char addr[INET6_ADDRSTRLEN];
1530 const char *kdc = NULL;
1533 if (!domain || !domain->alt_name || !*domain->alt_name) {
1537 if (domain->initialized && !domain->active_directory) {
1538 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s not AD\n",
1543 print_sockaddr(addr, sizeof(addr), &domain->dcaddr);
1546 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC IP\n",
1548 kdc = domain->dcname;
1551 if (!kdc || !*kdc) {
1552 DEBUG(lvl,("winbindd_set_locator_kdc_env: %s no DC at all\n",
1557 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1558 domain->alt_name) == -1) {
1562 DEBUG(lvl,("winbindd_set_locator_kdc_env: setting var: %s to: %s\n",
1565 setenv(var, kdc, 1);
1569 /*********************************************************************
1570 ********************************************************************/
1572 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1574 struct winbindd_domain *our_dom = find_our_domain();
1576 winbindd_set_locator_kdc_env(domain);
1578 if (domain != our_dom) {
1579 winbindd_set_locator_kdc_env(our_dom);
1583 /*********************************************************************
1584 ********************************************************************/
1586 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1590 if (!domain || !domain->alt_name || !*domain->alt_name) {
1594 if (asprintf_strupper_m(&var, "%s_%s", WINBINDD_LOCATOR_KDC_ADDRESS,
1595 domain->alt_name) == -1) {
1604 void winbindd_set_locator_kdc_envs(const struct winbindd_domain *domain)
1609 void winbindd_unset_locator_kdc_env(const struct winbindd_domain *domain)
1614 #endif /* HAVE_KRB5_LOCATE_PLUGIN_H */
1616 void set_auth_errors(struct winbindd_response *resp, NTSTATUS result)
1618 resp->data.auth.nt_status = NT_STATUS_V(result);
1619 fstrcpy(resp->data.auth.nt_status_string, nt_errstr(result));
1621 /* we might have given a more useful error above */
1622 if (*resp->data.auth.error_string == '\0')
1623 fstrcpy(resp->data.auth.error_string,
1624 get_friendly_nt_error_msg(result));
1625 resp->data.auth.pam_error = nt_status_to_pam(result);
1628 bool is_domain_offline(const struct winbindd_domain *domain)
1630 if (get_global_winbindd_state_offline()) {
1633 return !domain->online;
1636 bool is_domain_online(const struct winbindd_domain *domain)
1638 return !is_domain_offline(domain);
1642 * Parse an char array into a list of sids.
1644 * The input sidstr should consist of 0-terminated strings
1645 * representing sids, separated by newline characters '\n'.
1646 * The list is terminated by an empty string, i.e.
1647 * character '\0' directly following a character '\n'
1648 * (or '\0' right at the start of sidstr).
1650 bool parse_sidlist(TALLOC_CTX *mem_ctx, const char *sidstr,
1651 struct dom_sid **sids, uint32_t *num_sids)
1659 while (p[0] != '\0') {
1661 const char *q = NULL;
1663 if (!dom_sid_parse_endp(p, &sid, &q)) {
1664 DEBUG(1, ("Could not parse sid %s\n", p));
1668 DEBUG(1, ("Got invalid sidstr: %s\n", p));
1671 if (!NT_STATUS_IS_OK(add_sid_to_array(mem_ctx, &sid, sids,
1681 bool parse_xidlist(TALLOC_CTX *mem_ctx, const char *xidstr,
1682 struct unixid **pxids, uint32_t *pnum_xids)
1685 struct unixid *xids = NULL;
1686 uint32_t num_xids = 0;
1693 while (p[0] != '\0') {
1696 unsigned long long id;
1701 xid = (struct unixid) { .type = ID_TYPE_UID };
1704 xid = (struct unixid) { .type = ID_TYPE_GID };
1712 id = strtoull(p, &endp, 10);
1713 if ((id == ULLONG_MAX) && (errno == ERANGE)) {
1716 if (*endp != '\n') {
1722 if ((unsigned long long)xid.id != id) {
1726 tmp = talloc_realloc(mem_ctx, xids, struct unixid, num_xids+1);
1732 xids[num_xids] = xid;
1737 *pnum_xids = num_xids;