2 Unix SMB/CIFS implementation.
4 Winbind ADS backend functions
6 Copyright (C) Andrew Tridgell 2001
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
8 Copyright (C) Gerald (Jerry) Carter 2004
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "rpc_client/rpc_client.h"
27 #include "../librpc/gen_ndr/ndr_netlogon_c.h"
28 #include "../libds/common/flags.h"
30 #include "../libcli/ldap/ldap_ndr.h"
31 #include "../libcli/security/security.h"
32 #include "../libds/common/flag_mapping.h"
33 #include "libsmb/samlogon_cache.h"
39 #define DBGC_CLASS DBGC_WINBIND
41 extern struct winbindd_methods reconnect_methods;
42 extern struct winbindd_methods msrpc_methods;
44 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
47 * Check if cached connection can be reused. If the connection cannot
48 * be reused the ADS_STRUCT is freed and the pointer is set to NULL.
50 static void ads_cached_connection_reuse(ADS_STRUCT **adsp)
53 ADS_STRUCT *ads = *adsp;
57 time_t now = time(NULL);
59 expire = MIN(ads->auth.tgt_expire, ads->auth.tgs_expire);
61 DEBUG(7, ("Current tickets expire in %d seconds (at %d, time "
62 "is now %d)\n", (uint32_t)expire - (uint32_t)now,
63 (uint32_t) expire, (uint32_t) now));
65 if ( ads->config.realm && (expire > now)) {
68 /* we own this ADS_STRUCT so make sure it goes away */
69 DEBUG(7,("Deleting expired krb5 credential cache\n"));
72 ads_kdestroy(WINBIND_CCACHE_NAME);
79 * @brief Establish a connection to a DC
81 * @param[out] adsp ADS_STRUCT that will be created
82 * @param[in] target_realm Realm of domain to connect to
83 * @param[in] target_dom_name 'workgroup' name of domain to connect to
84 * @param[in] ldap_server DNS name of server to connect to
85 * @param[in] password Our machine acount secret
86 * @param[in] auth_realm Realm of local domain for creating krb token
87 * @param[in] renewable Renewable ticket time
91 static ADS_STATUS ads_cached_connection_connect(ADS_STRUCT **adsp,
92 const char *target_realm,
93 const char *target_dom_name,
94 const char *ldap_server,
101 struct sockaddr_storage dc_ss;
104 if (auth_realm == NULL) {
105 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
108 /* we don't want this to affect the users ccache */
109 setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
111 ads = ads_init(target_realm, target_dom_name, ldap_server);
113 DEBUG(1,("ads_init for domain %s failed\n", target_dom_name));
114 return ADS_ERROR(LDAP_NO_MEMORY);
117 SAFE_FREE(ads->auth.password);
118 SAFE_FREE(ads->auth.realm);
120 ads->auth.renewable = renewable;
121 ads->auth.password = password;
123 ads->auth.realm = SMB_STRDUP(auth_realm);
124 if (!strupper_m(ads->auth.realm)) {
126 return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR);
129 /* Setup the server affinity cache. We don't reaally care
130 about the name. Just setup affinity and the KRB5_CONFIG
132 get_dc_name(ads->server.workgroup, ads->server.realm, dc_name, &dc_ss);
134 status = ads_connect(ads);
135 if (!ADS_ERR_OK(status)) {
136 DEBUG(1,("ads_connect for domain %s failed: %s\n",
137 target_dom_name, ads_errstr(status)));
142 /* set the flag that says we don't own the memory even
143 though we do so that ads_destroy() won't destroy the
144 structure we pass back by reference */
146 ads->is_mine = False;
153 ADS_STATUS ads_idmap_cached_connection(ADS_STRUCT **adsp, const char *dom_name)
155 char *ldap_server, *realm, *password;
156 struct winbindd_domain *wb_dom;
159 ads_cached_connection_reuse(adsp);
165 * At this point we only have the NetBIOS domain name.
166 * Check if we can get server nam and realm from SAF cache
167 * and the domain list.
169 ldap_server = saf_fetch(talloc_tos(), dom_name);
170 DEBUG(10, ("ldap_server from saf cache: '%s'\n",
171 ldap_server ? ldap_server : ""));
173 wb_dom = find_domain_from_name(dom_name);
174 if (wb_dom == NULL) {
175 DEBUG(10, ("could not find domain '%s'\n", dom_name));
176 return ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
179 DEBUG(10, ("find_domain_from_name found realm '%s' for "
180 " domain '%s'\n", wb_dom->alt_name, dom_name));
182 if (!get_trust_pw_clear(dom_name, &password, NULL, NULL)) {
183 TALLOC_FREE(ldap_server);
184 return ADS_ERROR_NT(NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
188 SMB_ASSERT(wb_dom->alt_name != NULL);
189 realm = SMB_STRDUP(wb_dom->alt_name);
191 struct winbindd_domain *our_domain = wb_dom;
193 /* always give preference to the alt_name in our
194 primary domain if possible */
196 if (!wb_dom->primary) {
197 our_domain = find_our_domain();
200 if (our_domain->alt_name != NULL) {
201 realm = SMB_STRDUP(our_domain->alt_name);
203 realm = SMB_STRDUP(lp_realm());
207 status = ads_cached_connection_connect(
208 adsp, /* Returns ads struct. */
209 wb_dom->alt_name, /* realm to connect to. */
210 dom_name, /* 'workgroup' name for ads_init */
211 ldap_server, /* DNS name to connect to. */
212 password, /* password for auth realm. */
213 realm, /* realm used for krb5 ticket. */
214 0); /* renewable ticket time. */
217 TALLOC_FREE(ldap_server);
223 return our ads connections structure for a domain. We keep the connection
224 open to make things faster
226 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
229 char *password, *realm;
231 DEBUG(10,("ads_cached_connection\n"));
232 ads_cached_connection_reuse((ADS_STRUCT **)&domain->private_data);
234 if (domain->private_data) {
235 return (ADS_STRUCT *)domain->private_data;
238 /* the machine acct password might have change - fetch it every time */
240 if (!get_trust_pw_clear(domain->name, &password, NULL, NULL)) {
245 SMB_ASSERT(domain->alt_name != NULL);
246 realm = SMB_STRDUP(domain->alt_name);
249 struct winbindd_domain *our_domain = domain;
252 /* always give preference to the alt_name in our
253 primary domain if possible */
255 if ( !domain->primary )
256 our_domain = find_our_domain();
258 if (our_domain->alt_name != NULL) {
259 realm = SMB_STRDUP( our_domain->alt_name );
262 realm = SMB_STRDUP( lp_realm() );
265 status = ads_cached_connection_connect(
266 (ADS_STRUCT **)&domain->private_data,
270 WINBINDD_PAM_AUTH_KRB5_RENEW_TIME);
273 if (!ADS_ERR_OK(status)) {
274 /* if we get ECONNREFUSED then it might be a NT4
275 server, fall back to MSRPC */
276 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
277 status.err.rc == ECONNREFUSED) {
278 /* 'reconnect_methods' is the MS-RPC backend. */
279 DEBUG(1,("Trying MSRPC methods\n"));
280 domain->backend = &reconnect_methods;
285 return (ADS_STRUCT *)domain->private_data;
288 /* Query display info for a realm. This is the basic user list fn */
289 static NTSTATUS query_user_list(struct winbindd_domain *domain,
291 uint32_t *num_entries,
292 struct wbint_userinfo **pinfo)
294 ADS_STRUCT *ads = NULL;
295 const char *attrs[] = { "*", NULL };
298 LDAPMessage *res = NULL;
299 LDAPMessage *msg = NULL;
300 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
304 DEBUG(3,("ads: query_user_list\n"));
306 if ( !winbindd_can_contact_domain( domain ) ) {
307 DEBUG(10,("query_user_list: No incoming trust for domain %s\n",
312 ads = ads_cached_connection(domain);
315 domain->last_status = NT_STATUS_SERVER_DISABLED;
319 rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
320 if (!ADS_ERR_OK(rc)) {
321 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
322 status = ads_ntstatus(rc);
325 DEBUG(1,("query_user_list ads_search returned NULL res\n"));
329 count = ads_count_replies(ads, res);
331 DEBUG(1,("query_user_list: No users found\n"));
335 (*pinfo) = talloc_zero_array(mem_ctx, struct wbint_userinfo, count);
337 status = NT_STATUS_NO_MEMORY;
343 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
344 struct wbint_userinfo *info = &((*pinfo)[count]);
349 ok = ads_pull_uint32(ads, msg, "sAMAccountType", &atype);
351 DBG_INFO("Object lacks sAMAccountType attribute\n");
354 if (ds_atype_map(atype) != SID_NAME_USER) {
355 DBG_INFO("Not a user account? atype=0x%x\n", atype);
359 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
360 info->full_name = ads_pull_string(ads, mem_ctx, msg, "displayName");
361 if (info->full_name == NULL) {
362 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
364 info->homedir = NULL;
366 info->primary_gid = (gid_t)-1;
368 if (!ads_pull_sid(ads, msg, "objectSid",
370 DEBUG(1, ("No sid for %s !?\n", info->acct_name));
374 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
375 DEBUG(1, ("No primary group for %s !?\n",
379 sid_compose(&info->group_sid, &domain->sid, group);
384 (*num_entries) = count;
385 ads_msgfree(ads, res);
387 for (i=0; i<count; i++) {
388 struct wbint_userinfo *info = &((*pinfo)[i]);
389 const char *gecos = NULL;
390 gid_t primary_gid = (gid_t)-1;
392 status = nss_get_info_cached(domain, &info->user_sid, mem_ctx,
393 &info->homedir, &info->shell,
394 &gecos, &primary_gid);
395 if (!NT_STATUS_IS_OK(status)) {
397 * Deliberately ignore this error, there might be more
404 info->full_name = gecos;
406 info->primary_gid = primary_gid;
409 status = NT_STATUS_OK;
411 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
417 /* list all domain groups */
418 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
420 uint32_t *num_entries,
421 struct wb_acct_info **info)
423 ADS_STRUCT *ads = NULL;
424 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
425 "name", "objectSid", NULL};
428 LDAPMessage *res = NULL;
429 LDAPMessage *msg = NULL;
430 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
432 bool enum_dom_local_groups = False;
436 DEBUG(3,("ads: enum_dom_groups\n"));
438 if ( !winbindd_can_contact_domain( domain ) ) {
439 DEBUG(10,("enum_dom_groups: No incoming trust for domain %s\n",
444 /* only grab domain local groups for our domain */
445 if ( domain->active_directory && strequal(lp_realm(), domain->alt_name) ) {
446 enum_dom_local_groups = True;
449 /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
452 * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
453 * default value, it MUST be absent. In case of extensible matching the
454 * "dnattr" boolean defaults to FALSE and so it must be only be present
457 * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
458 * filter using bitwise matching rule then the buggy AD fails to decode
459 * the extensible match. As a workaround set it to TRUE and thereby add
460 * the dnAttributes "dn" field to cope with those older AD versions.
461 * It should not harm and won't put any additional load on the AD since
462 * none of the dn components have a bitmask-attribute.
464 * Thanks to Ralf Haferkamp for input and testing - Guenther */
466 filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))",
467 ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
468 ADS_LDAP_MATCHING_RULE_BIT_AND,
469 enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
471 if (filter == NULL) {
472 status = NT_STATUS_NO_MEMORY;
476 ads = ads_cached_connection(domain);
479 domain->last_status = NT_STATUS_SERVER_DISABLED;
483 rc = ads_search_retry(ads, &res, filter, attrs);
484 if (!ADS_ERR_OK(rc)) {
485 status = ads_ntstatus(rc);
486 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
489 DEBUG(1,("enum_dom_groups ads_search returned NULL res\n"));
493 count = ads_count_replies(ads, res);
495 DEBUG(1,("enum_dom_groups: No groups found\n"));
499 (*info) = talloc_zero_array(mem_ctx, struct wb_acct_info, count);
501 status = NT_STATUS_NO_MEMORY;
507 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
512 name = ads_pull_username(ads, mem_ctx, msg);
513 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
514 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
515 DEBUG(1,("No sid for %s !?\n", name));
519 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
520 DEBUG(1,("No rid for %s !?\n", name));
524 fstrcpy((*info)[i].acct_name, name);
525 fstrcpy((*info)[i].acct_desc, gecos);
526 (*info)[i].rid = rid;
532 status = NT_STATUS_OK;
534 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
538 ads_msgfree(ads, res);
543 /* list all domain local groups */
544 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
546 uint32_t *num_entries,
547 struct wb_acct_info **info)
550 * This is a stub function only as we returned the domain
551 * local groups in enum_dom_groups() if the domain->native field
552 * was true. This is a simple performance optimization when
555 * if we ever need to enumerate domain local groups separately,
556 * then this optimization in enum_dom_groups() will need
564 /* convert a single name to a sid in a domain - use rpc methods */
565 static NTSTATUS name_to_sid(struct winbindd_domain *domain,
567 const char *domain_name,
571 enum lsa_SidType *type)
573 return msrpc_methods.name_to_sid(domain, mem_ctx, domain_name, name,
577 /* convert a domain SID to a user or group name - use rpc methods */
578 static NTSTATUS sid_to_name(struct winbindd_domain *domain,
580 const struct dom_sid *sid,
583 enum lsa_SidType *type)
585 return msrpc_methods.sid_to_name(domain, mem_ctx, sid,
586 domain_name, name, type);
589 /* convert a list of rids to names - use rpc methods */
590 static NTSTATUS rids_to_names(struct winbindd_domain *domain,
592 const struct dom_sid *sid,
597 enum lsa_SidType **types)
599 return msrpc_methods.rids_to_names(domain, mem_ctx, sid,
601 domain_name, names, types);
604 /* If you are looking for "dn_lookup": Yes, it used to be here!
605 * It has gone now since it was a major speed bottleneck in
606 * lookup_groupmem (its only use). It has been replaced by
607 * an rpc lookup sids call... R.I.P. */
609 /* Lookup user information from a rid */
610 static NTSTATUS query_user(struct winbindd_domain *domain,
612 const struct dom_sid *sid,
613 struct wbint_userinfo *info)
615 ADS_STRUCT *ads = NULL;
616 const char *attrs[] = { "*", NULL };
619 LDAPMessage *msg = NULL;
623 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
624 struct netr_SamInfo3 *user = NULL;
629 DEBUG(3,("ads: query_user\n"));
631 info->homedir = NULL;
634 /* try netsamlogon cache first */
636 if ( (user = netsamlogon_cache_get( mem_ctx, sid )) != NULL )
638 DEBUG(5,("query_user: Cache lookup succeeded for %s\n",
639 sid_string_dbg(sid)));
641 sid_compose(&info->user_sid, &domain->sid, user->base.rid);
642 sid_compose(&info->group_sid, &domain->sid, user->base.primary_gid);
644 info->acct_name = talloc_strdup(mem_ctx, user->base.account_name.string);
645 info->full_name = talloc_strdup(mem_ctx, user->base.full_name.string);
647 nss_get_info_cached( domain, sid, mem_ctx,
648 &info->homedir, &info->shell, &info->full_name,
650 info->primary_gid = gid;
654 if (info->full_name == NULL) {
655 /* this might fail so we don't check the return code */
656 wcache_query_user_fullname(domain,
665 if ( !winbindd_can_contact_domain(domain)) {
666 DEBUG(8,("query_user: No incoming trust from domain %s\n",
669 /* We still need to generate some basic information
670 about the user even if we cannot contact the
671 domain. Most of this stuff we can deduce. */
673 sid_copy( &info->user_sid, sid );
675 /* Assume "Domain Users" for the primary group */
677 sid_compose(&info->group_sid, &domain->sid, DOMAIN_RID_USERS );
679 /* Try to fill in what the nss_info backend can do */
681 nss_get_info_cached( domain, sid, mem_ctx,
682 &info->homedir, &info->shell, &info->full_name,
684 info->primary_gid = gid;
689 /* no cache...do the query */
691 if ( (ads = ads_cached_connection(domain)) == NULL ) {
692 domain->last_status = NT_STATUS_SERVER_DISABLED;
693 return NT_STATUS_SERVER_DISABLED;
696 sidstr = ldap_encode_ndr_dom_sid(talloc_tos(), sid);
698 ret = asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
701 return NT_STATUS_NO_MEMORY;
703 rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
705 if (!ADS_ERR_OK(rc)) {
706 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
707 sid_string_dbg(sid), ads_errstr(rc)));
708 return ads_ntstatus(rc);
710 DEBUG(1,("query_user(sid=%s) ads_search returned NULL res\n",
711 sid_string_dbg(sid)));
712 return NT_STATUS_INTERNAL_ERROR;
715 count = ads_count_replies(ads, msg);
717 DEBUG(1,("query_user(sid=%s): Not found\n",
718 sid_string_dbg(sid)));
719 ads_msgfree(ads, msg);
720 return NT_STATUS_NO_SUCH_USER;
723 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
725 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
726 DEBUG(1,("No primary group for %s !?\n",
727 sid_string_dbg(sid)));
728 ads_msgfree(ads, msg);
729 return NT_STATUS_NO_SUCH_USER;
731 sid_copy(&info->user_sid, sid);
732 sid_compose(&info->group_sid, &domain->sid, group_rid);
735 * We have to fetch the "name" attribute before doing the
736 * nss_get_info_cached call. nss_get_info_cached might destroy
737 * the ads struct, potentially invalidating the ldap message.
739 full_name = ads_pull_string(ads, mem_ctx, msg, "displayName");
740 if (full_name == NULL) {
741 full_name = ads_pull_string(ads, mem_ctx, msg, "name");
744 ads_msgfree(ads, msg);
747 status = nss_get_info_cached( domain, sid, mem_ctx,
748 &info->homedir, &info->shell, &info->full_name,
750 info->primary_gid = gid;
751 if (!NT_STATUS_IS_OK(status)) {
752 DEBUG(1, ("nss_get_info_cached failed: %s\n",
757 if (info->full_name == NULL) {
758 info->full_name = full_name;
760 TALLOC_FREE(full_name);
763 status = NT_STATUS_OK;
765 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
769 /* Lookup groups a user is a member of - alternate method, for when
770 tokenGroups are not available. */
771 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
774 struct dom_sid *primary_group,
775 uint32_t *p_num_groups, struct dom_sid **user_sids)
778 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
780 LDAPMessage *res = NULL;
781 LDAPMessage *msg = NULL;
784 const char *group_attrs[] = {"objectSid", NULL};
786 uint32_t num_groups = 0;
788 DEBUG(3,("ads: lookup_usergroups_member\n"));
790 if ( !winbindd_can_contact_domain( domain ) ) {
791 DEBUG(10,("lookup_usergroups_members: No incoming trust for domain %s\n",
796 ads = ads_cached_connection(domain);
799 domain->last_status = NT_STATUS_SERVER_DISABLED;
803 if (!(escaped_dn = escape_ldap_string(talloc_tos(), user_dn))) {
804 status = NT_STATUS_NO_MEMORY;
808 ldap_exp = talloc_asprintf(mem_ctx,
809 "(&(member=%s)(objectCategory=group)(groupType:dn:%s:=%d))",
811 ADS_LDAP_MATCHING_RULE_BIT_AND,
812 GROUP_TYPE_SECURITY_ENABLED);
814 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
815 TALLOC_FREE(escaped_dn);
816 status = NT_STATUS_NO_MEMORY;
820 TALLOC_FREE(escaped_dn);
822 rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
824 if (!ADS_ERR_OK(rc)) {
825 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
826 return ads_ntstatus(rc);
828 DEBUG(1,("lookup_usergroups ads_search returned NULL res\n"));
829 return NT_STATUS_INTERNAL_ERROR;
833 count = ads_count_replies(ads, res);
838 /* always add the primary group to the sid array */
839 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
841 if (!NT_STATUS_IS_OK(status)) {
846 for (msg = ads_first_entry(ads, res); msg;
847 msg = ads_next_entry(ads, msg)) {
848 struct dom_sid group_sid;
850 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
851 DEBUG(1,("No sid for this group ?!?\n"));
855 /* ignore Builtin groups from ADS - Guenther */
856 if (sid_check_is_in_builtin(&group_sid)) {
860 status = add_sid_to_array(mem_ctx, &group_sid,
861 user_sids, &num_groups);
862 if (!NT_STATUS_IS_OK(status)) {
869 *p_num_groups = num_groups;
870 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
872 DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
875 ads_msgfree(ads, res);
880 /* Lookup groups a user is a member of - alternate method, for when
881 tokenGroups are not available. */
882 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
885 struct dom_sid *primary_group,
886 uint32_t *p_num_groups,
887 struct dom_sid **user_sids)
890 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
892 const char *attrs[] = {"memberOf", NULL};
893 uint32_t num_groups = 0;
894 struct dom_sid *group_sids = NULL;
896 char **strings = NULL;
897 size_t num_strings = 0, num_sids = 0;
900 DEBUG(3,("ads: lookup_usergroups_memberof\n"));
902 if ( !winbindd_can_contact_domain( domain ) ) {
903 DEBUG(10,("lookup_usergroups_memberof: No incoming trust for "
904 "domain %s\n", domain->name));
908 ads = ads_cached_connection(domain);
911 domain->last_status = NT_STATUS_SERVER_DISABLED;
912 return NT_STATUS_UNSUCCESSFUL;
915 rc = ads_search_retry_extended_dn_ranged(ads, mem_ctx, user_dn, attrs,
916 ADS_EXTENDED_DN_HEX_STRING,
917 &strings, &num_strings);
919 if (!ADS_ERR_OK(rc)) {
920 DEBUG(1,("lookup_usergroups_memberof ads_search "
921 "member=%s: %s\n", user_dn, ads_errstr(rc)));
922 return ads_ntstatus(rc);
928 /* always add the primary group to the sid array */
929 status = add_sid_to_array(mem_ctx, primary_group, user_sids,
931 if (!NT_STATUS_IS_OK(status)) {
935 group_sids = talloc_zero_array(mem_ctx, struct dom_sid, num_strings + 1);
937 status = NT_STATUS_NO_MEMORY;
941 for (i=0; i<num_strings; i++) {
942 rc = ads_get_sid_from_extended_dn(mem_ctx, strings[i],
943 ADS_EXTENDED_DN_HEX_STRING,
945 if (!ADS_ERR_OK(rc)) {
946 /* ignore members without SIDs */
947 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
948 NT_STATUS_NOT_FOUND)) {
952 status = ads_ntstatus(rc);
960 DEBUG(1,("No memberOf for this user?!?\n"));
961 status = NT_STATUS_NO_MEMORY;
965 for (i=0; i<num_sids; i++) {
967 /* ignore Builtin groups from ADS - Guenther */
968 if (sid_check_is_in_builtin(&group_sids[i])) {
972 status = add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
974 if (!NT_STATUS_IS_OK(status)) {
980 *p_num_groups = num_groups;
981 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
983 DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n",
987 TALLOC_FREE(strings);
988 TALLOC_FREE(group_sids);
994 /* Lookup groups a user is a member of. */
995 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
997 const struct dom_sid *sid,
998 uint32_t *p_num_groups, struct dom_sid **user_sids)
1000 ADS_STRUCT *ads = NULL;
1001 const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
1004 LDAPMessage *msg = NULL;
1005 char *user_dn = NULL;
1006 struct dom_sid *sids;
1008 struct dom_sid primary_group;
1009 uint32_t primary_group_rid;
1010 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1011 uint32_t num_groups = 0;
1013 DEBUG(3,("ads: lookup_usergroups\n"));
1016 status = lookup_usergroups_cached(domain, mem_ctx, sid,
1017 p_num_groups, user_sids);
1018 if (NT_STATUS_IS_OK(status)) {
1019 return NT_STATUS_OK;
1022 if ( !winbindd_can_contact_domain( domain ) ) {
1023 DEBUG(10,("lookup_usergroups: No incoming trust for domain %s\n",
1026 /* Tell the cache manager not to remember this one */
1028 return NT_STATUS_SYNCHRONIZATION_REQUIRED;
1031 ads = ads_cached_connection(domain);
1034 domain->last_status = NT_STATUS_SERVER_DISABLED;
1035 status = NT_STATUS_SERVER_DISABLED;
1039 rc = ads_search_retry_sid(ads, &msg, sid, attrs);
1041 if (!ADS_ERR_OK(rc)) {
1042 status = ads_ntstatus(rc);
1043 DEBUG(1, ("lookup_usergroups(sid=%s) ads_search tokenGroups: "
1044 "%s\n", sid_string_dbg(sid), ads_errstr(rc)));
1048 count = ads_count_replies(ads, msg);
1050 status = NT_STATUS_UNSUCCESSFUL;
1051 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
1052 "invalid number of results (count=%d)\n",
1053 sid_string_dbg(sid), count));
1058 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
1059 sid_string_dbg(sid)));
1060 status = NT_STATUS_UNSUCCESSFUL;
1064 user_dn = ads_get_dn(ads, mem_ctx, msg);
1065 if (user_dn == NULL) {
1066 status = NT_STATUS_NO_MEMORY;
1070 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
1071 DEBUG(1,("%s: No primary group for sid=%s !?\n",
1072 domain->name, sid_string_dbg(sid)));
1076 sid_compose(&primary_group, &domain->sid, primary_group_rid);
1078 count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
1080 /* there must always be at least one group in the token,
1081 unless we are talking to a buggy Win2k server */
1083 /* actually this only happens when the machine account has no read
1084 * permissions on the tokenGroup attribute - gd */
1088 /* no tokenGroups */
1090 /* lookup what groups this user is a member of by DN search on
1093 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
1095 &num_groups, user_sids);
1096 *p_num_groups = num_groups;
1097 if (NT_STATUS_IS_OK(status)) {
1101 /* lookup what groups this user is a member of by DN search on
1104 status = lookup_usergroups_member(domain, mem_ctx, user_dn,
1106 &num_groups, user_sids);
1107 *p_num_groups = num_groups;
1114 status = add_sid_to_array(mem_ctx, &primary_group, user_sids,
1116 if (!NT_STATUS_IS_OK(status)) {
1120 for (i=0;i<count;i++) {
1122 /* ignore Builtin groups from ADS - Guenther */
1123 if (sid_check_is_in_builtin(&sids[i])) {
1127 status = add_sid_to_array_unique(mem_ctx, &sids[i],
1128 user_sids, &num_groups);
1129 if (!NT_STATUS_IS_OK(status)) {
1134 *p_num_groups = (uint32_t)num_groups;
1135 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1137 DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
1138 sid_string_dbg(sid)));
1140 TALLOC_FREE(user_dn);
1141 ads_msgfree(ads, msg);
1145 /* Lookup aliases a user is member of - use rpc methods */
1146 static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
1147 TALLOC_CTX *mem_ctx,
1148 uint32_t num_sids, const struct dom_sid *sids,
1149 uint32_t *num_aliases, uint32_t **alias_rids)
1151 return msrpc_methods.lookup_useraliases(domain, mem_ctx, num_sids, sids,
1152 num_aliases, alias_rids);
1155 static NTSTATUS add_primary_group_members(
1156 ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, uint32_t rid,
1157 char ***all_members, size_t *num_all_members)
1160 NTSTATUS status = NT_STATUS_NO_MEMORY;
1162 const char *attrs[] = { "dn", NULL };
1163 LDAPMessage *res = NULL;
1169 filter = talloc_asprintf(
1170 mem_ctx, "(&(objectCategory=user)(primaryGroupID=%u))",
1172 if (filter == NULL) {
1176 args.control = ADS_EXTENDED_DN_OID;
1177 args.val = ADS_EXTENDED_DN_HEX_STRING;
1178 args.critical = True;
1180 rc = ads_do_search_all_args(ads, ads->config.bind_path,
1181 LDAP_SCOPE_SUBTREE, filter, attrs, &args,
1184 if (!ADS_ERR_OK(rc)) {
1185 status = ads_ntstatus(rc);
1186 DEBUG(1,("%s: ads_search: %s\n", __func__, ads_errstr(rc)));
1190 DEBUG(1,("%s: ads_search returned NULL res\n", __func__));
1194 num_members = ads_count_replies(ads, res);
1196 DEBUG(10, ("%s: Got %ju primary group members\n", __func__,
1197 (uintmax_t)num_members));
1199 if (num_members == 0) {
1200 status = NT_STATUS_OK;
1204 members = talloc_realloc(mem_ctx, *all_members, char *,
1205 *num_all_members + num_members);
1206 if (members == NULL) {
1207 DEBUG(1, ("%s: talloc_realloc failed\n", __func__));
1210 *all_members = members;
1212 for (msg = ads_first_entry(ads, res); msg != NULL;
1213 msg = ads_next_entry(ads, msg)) {
1216 dn = ads_get_dn(ads, members, msg);
1218 DEBUG(1, ("%s: ads_get_dn failed\n", __func__));
1222 members[*num_all_members] = dn;
1223 *num_all_members += 1;
1226 status = NT_STATUS_OK;
1229 ads_msgfree(ads, res);
1231 TALLOC_FREE(filter);
1236 find the members of a group, given a group rid and domain
1238 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
1239 TALLOC_CTX *mem_ctx,
1240 const struct dom_sid *group_sid,
1241 enum lsa_SidType type,
1242 uint32_t *num_names,
1243 struct dom_sid **sid_mem, char ***names,
1244 uint32_t **name_types)
1247 ADS_STRUCT *ads = NULL;
1249 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1251 char **members = NULL;
1253 size_t num_members = 0;
1255 struct dom_sid *sid_mem_nocache = NULL;
1256 char **names_nocache = NULL;
1257 enum lsa_SidType *name_types_nocache = NULL;
1258 char **domains_nocache = NULL; /* only needed for rpccli_lsa_lookup_sids */
1259 uint32_t num_nocache = 0;
1260 TALLOC_CTX *tmp_ctx = NULL;
1263 DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
1264 sid_string_dbg(group_sid)));
1268 tmp_ctx = talloc_new(mem_ctx);
1270 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1271 status = NT_STATUS_NO_MEMORY;
1275 if (!sid_peek_rid(group_sid, &rid)) {
1276 DEBUG(1, ("%s: sid_peek_rid failed\n", __func__));
1277 status = NT_STATUS_INVALID_PARAMETER;
1281 if ( !winbindd_can_contact_domain( domain ) ) {
1282 DEBUG(10,("lookup_groupmem: No incoming trust for domain %s\n",
1284 return NT_STATUS_OK;
1287 ads = ads_cached_connection(domain);
1290 domain->last_status = NT_STATUS_SERVER_DISABLED;
1294 if ((sidbinstr = ldap_encode_ndr_dom_sid(talloc_tos(), group_sid)) == NULL) {
1295 status = NT_STATUS_NO_MEMORY;
1299 /* search for all members of the group */
1300 ldap_exp = talloc_asprintf(tmp_ctx, "(objectSid=%s)", sidbinstr);
1301 TALLOC_FREE(sidbinstr);
1302 if (ldap_exp == NULL) {
1303 DEBUG(1, ("ads: lookup_groupmem: talloc_asprintf for ldap_exp failed!\n"));
1304 status = NT_STATUS_NO_MEMORY;
1308 args.control = ADS_EXTENDED_DN_OID;
1309 args.val = ADS_EXTENDED_DN_HEX_STRING;
1310 args.critical = True;
1312 rc = ads_ranged_search(ads, tmp_ctx, LDAP_SCOPE_SUBTREE, ads->config.bind_path,
1313 ldap_exp, &args, "member", &members, &num_members);
1315 if (!ADS_ERR_OK(rc)) {
1316 DEBUG(0,("ads_ranged_search failed with: %s\n", ads_errstr(rc)));
1317 status = NT_STATUS_UNSUCCESSFUL;
1321 DEBUG(10, ("ads lookup_groupmem: got %d sids via extended dn call\n", (int)num_members));
1323 status = add_primary_group_members(ads, mem_ctx, rid,
1324 &members, &num_members);
1325 if (!NT_STATUS_IS_OK(status)) {
1326 DEBUG(10, ("%s: add_primary_group_members failed: %s\n",
1327 __func__, nt_errstr(status)));
1331 DEBUG(10, ("%s: Got %d sids after adding primary group members\n",
1332 __func__, (int)num_members));
1334 /* Now that we have a list of sids, we need to get the
1335 * lists of names and name_types belonging to these sids.
1336 * even though conceptually not quite clean, we use the
1337 * RPC call lsa_lookup_sids for this since it can handle a
1338 * list of sids. ldap calls can just resolve one sid at a time.
1340 * At this stage, the sids are still hidden in the exetended dn
1341 * member output format. We actually do a little better than
1342 * stated above: In extracting the sids from the member strings,
1343 * we try to resolve as many sids as possible from the
1344 * cache. Only the rest is passed to the lsa_lookup_sids call. */
1347 (*sid_mem) = talloc_zero_array(mem_ctx, struct dom_sid, num_members);
1348 (*names) = talloc_zero_array(mem_ctx, char *, num_members);
1349 (*name_types) = talloc_zero_array(mem_ctx, uint32_t, num_members);
1350 (sid_mem_nocache) = talloc_zero_array(tmp_ctx, struct dom_sid, num_members);
1352 if ((members == NULL) || (*sid_mem == NULL) ||
1353 (*names == NULL) || (*name_types == NULL) ||
1354 (sid_mem_nocache == NULL))
1356 DEBUG(1, ("ads: lookup_groupmem: talloc failed\n"));
1357 status = NT_STATUS_NO_MEMORY;
1364 (*name_types) = NULL;
1367 for (i=0; i<num_members; i++) {
1368 enum lsa_SidType name_type;
1369 char *name, *domain_name;
1372 rc = ads_get_sid_from_extended_dn(tmp_ctx, members[i], args.val,
1374 if (!ADS_ERR_OK(rc)) {
1375 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
1376 NT_STATUS_NOT_FOUND)) {
1377 /* Group members can be objects, like Exchange
1378 * Public Folders, that don't have a SID. Skip
1383 status = ads_ntstatus(rc);
1387 if (lookup_cached_sid(mem_ctx, &sid, &domain_name, &name,
1389 DEBUG(10,("ads: lookup_groupmem: got sid %s from "
1390 "cache\n", sid_string_dbg(&sid)));
1391 sid_copy(&(*sid_mem)[*num_names], &sid);
1392 (*names)[*num_names] = fill_domain_username_talloc(
1398 (*name_types)[*num_names] = name_type;
1402 DEBUG(10, ("ads: lookup_groupmem: sid %s not found in "
1403 "cache\n", sid_string_dbg(&sid)));
1404 sid_copy(&(sid_mem_nocache)[num_nocache], &sid);
1409 DEBUG(10, ("ads: lookup_groupmem: %d sids found in cache, "
1410 "%d left for lsa_lookupsids\n", *num_names, num_nocache));
1412 /* handle sids not resolved from cache by lsa_lookup_sids */
1413 if (num_nocache > 0) {
1415 status = winbindd_lookup_sids(tmp_ctx,
1421 &name_types_nocache);
1423 if (!(NT_STATUS_IS_OK(status) ||
1424 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
1425 NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)))
1427 DEBUG(1, ("lsa_lookupsids call failed with %s "
1428 "- retrying...\n", nt_errstr(status)));
1430 status = winbindd_lookup_sids(tmp_ctx,
1436 &name_types_nocache);
1439 if (NT_STATUS_IS_OK(status) ||
1440 NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
1442 /* Copy the entries over from the "_nocache" arrays
1443 * to the result arrays, skipping the gaps the
1444 * lookup_sids call left. */
1445 for (i=0; i < num_nocache; i++) {
1446 if (((names_nocache)[i] != NULL) &&
1447 ((name_types_nocache)[i] != SID_NAME_UNKNOWN))
1449 sid_copy(&(*sid_mem)[*num_names],
1450 &sid_mem_nocache[i]);
1451 (*names)[*num_names] =
1452 fill_domain_username_talloc(
1457 (*name_types)[*num_names] = name_types_nocache[i];
1462 else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
1463 DEBUG(10, ("lookup_groupmem: lsa_lookup_sids could "
1464 "not map any SIDs at all.\n"));
1465 /* Don't handle this as an error here.
1466 * There is nothing left to do with respect to the
1467 * overall result... */
1469 else if (!NT_STATUS_IS_OK(status)) {
1470 DEBUG(10, ("lookup_groupmem: Error looking up %d "
1471 "sids via rpc_lsa_lookup_sids: %s\n",
1472 (int)num_members, nt_errstr(status)));
1477 status = NT_STATUS_OK;
1478 DEBUG(3,("ads lookup_groupmem for sid=%s succeeded\n",
1479 sid_string_dbg(group_sid)));
1483 TALLOC_FREE(tmp_ctx);
1488 /* find the sequence number for a domain */
1489 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32_t *seq)
1491 ADS_STRUCT *ads = NULL;
1494 DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1496 if ( !winbindd_can_contact_domain( domain ) ) {
1497 DEBUG(10,("sequence: No incoming trust for domain %s\n",
1500 return NT_STATUS_OK;
1503 *seq = DOM_SEQUENCE_NONE;
1505 ads = ads_cached_connection(domain);
1508 domain->last_status = NT_STATUS_SERVER_DISABLED;
1509 return NT_STATUS_UNSUCCESSFUL;
1512 rc = ads_USN(ads, seq);
1514 if (!ADS_ERR_OK(rc)) {
1516 /* its a dead connection, destroy it */
1518 if (domain->private_data) {
1519 ads = (ADS_STRUCT *)domain->private_data;
1520 ads->is_mine = True;
1522 ads_kdestroy(WINBIND_CCACHE_NAME);
1523 domain->private_data = NULL;
1526 return ads_ntstatus(rc);
1529 /* find the lockout policy of a domain - use rpc methods */
1530 static NTSTATUS lockout_policy(struct winbindd_domain *domain,
1531 TALLOC_CTX *mem_ctx,
1532 struct samr_DomInfo12 *policy)
1534 return msrpc_methods.lockout_policy(domain, mem_ctx, policy);
1537 /* find the password policy of a domain - use rpc methods */
1538 static NTSTATUS password_policy(struct winbindd_domain *domain,
1539 TALLOC_CTX *mem_ctx,
1540 struct samr_DomInfo1 *policy)
1542 return msrpc_methods.password_policy(domain, mem_ctx, policy);
1545 /* get a list of trusted domains */
1546 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1547 TALLOC_CTX *mem_ctx,
1548 struct netr_DomainTrustList *trusts)
1550 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1554 struct rpc_pipe_client *cli;
1556 struct dcerpc_binding_handle *b;
1558 DEBUG(3,("ads: trusted_domains\n"));
1560 ZERO_STRUCTP(trusts);
1562 /* If this is our primary domain or a root in our forest,
1563 query for all trusts. If not, then just look for domain
1564 trusts in the target forest */
1566 if (domain->primary || domain_is_forest_root(domain)) {
1567 flags = NETR_TRUST_FLAG_OUTBOUND |
1568 NETR_TRUST_FLAG_INBOUND |
1569 NETR_TRUST_FLAG_IN_FOREST;
1571 flags = NETR_TRUST_FLAG_IN_FOREST;
1574 result = cm_connect_netlogon(domain, &cli);
1576 if (!NT_STATUS_IS_OK(result)) {
1577 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1578 "for PIPE_NETLOGON (%s)\n",
1579 domain->name, nt_errstr(result)));
1580 return NT_STATUS_UNSUCCESSFUL;
1583 b = cli->binding_handle;
1585 result = dcerpc_netr_DsrEnumerateDomainTrusts(b, mem_ctx,
1590 if (!NT_STATUS_IS_OK(result)) {
1594 if (!W_ERROR_IS_OK(werr)) {
1595 return werror_to_ntstatus(werr);
1597 if (trusts->count == 0) {
1598 return NT_STATUS_OK;
1601 /* Copy across names and sids */
1604 for (i = 0; i < trusts->count; i++) {
1605 struct netr_DomainTrust *trust = &trusts->array[i];
1606 struct winbindd_domain d;
1611 * drop external trusts if this is not our primary
1612 * domain. This means that the returned number of
1613 * domains may be less that the ones actually trusted
1617 if ((trust->trust_attributes
1618 == LSA_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN) &&
1621 DEBUG(10,("trusted_domains: Skipping external trusted "
1622 "domain %s because it is outside of our "
1624 trust->netbios_name));
1628 /* add to the trusted domain cache */
1630 d.name = discard_const_p(char, trust->netbios_name);
1631 d.alt_name = discard_const_p(char, trust->dns_name);
1634 sid_copy(&d.sid, trust->sid);
1636 sid_copy(&d.sid, &global_sid_NULL);
1639 if ( domain->primary ) {
1640 DEBUG(10,("trusted_domains(ads): Searching "
1641 "trusted domain list of %s and storing "
1642 "trust flags for domain %s\n",
1643 domain->name, d.alt_name));
1645 d.domain_flags = trust->trust_flags;
1646 d.domain_type = trust->trust_type;
1647 d.domain_trust_attribs = trust->trust_attributes;
1649 wcache_tdc_add_domain( &d );
1651 } else if (domain_is_forest_root(domain)) {
1652 /* Check if we already have this record. If
1653 * we are following our forest root that is not
1654 * our primary domain, we want to keep trust
1655 * flags from the perspective of our primary
1656 * domain not our forest root. */
1657 struct winbindd_tdc_domain *exist = NULL;
1659 exist = wcache_tdc_fetch_domain(
1660 talloc_tos(), trust->netbios_name);
1662 DEBUG(10,("trusted_domains(ads): Searching "
1663 "trusted domain list of %s and "
1664 "storing trust flags for domain "
1665 "%s\n", domain->name, d.alt_name));
1666 d.domain_flags = trust->trust_flags;
1667 d.domain_type = trust->trust_type;
1668 d.domain_trust_attribs =
1669 trust->trust_attributes;
1671 wcache_tdc_add_domain( &d );
1676 /* This gets a little tricky. If we are
1677 following a transitive forest trust, then
1678 innerit the flags, type, and attribs from
1679 the domain we queried to make sure we don't
1680 record the view of the trust from the wrong
1681 side. Always view it from the side of our
1682 primary domain. --jerry */
1683 struct winbindd_tdc_domain *parent = NULL;
1685 DEBUG(10,("trusted_domains(ads): Searching "
1686 "trusted domain list of %s and inheriting "
1687 "trust flags for domain %s\n",
1688 domain->name, d.alt_name));
1690 parent = wcache_tdc_fetch_domain(talloc_tos(),
1693 d.domain_flags = parent->trust_flags;
1694 d.domain_type = parent->trust_type;
1695 d.domain_trust_attribs = parent->trust_attribs;
1697 d.domain_flags = domain->domain_flags;
1698 d.domain_type = domain->domain_type;
1699 d.domain_trust_attribs =
1700 domain->domain_trust_attribs;
1702 TALLOC_FREE(parent);
1704 wcache_tdc_add_domain( &d );
1711 /* the ADS backend methods are exposed via this structure */
1712 struct winbindd_methods ads_methods = {