r13657: Let winbindd try to obtain the gecos field from the msSFU30Gecos
[Samba/bb.git] / source3 / nsswitch / winbindd_ads.c
blob179659f86f1c03a492459e76edf3b53f2f80e539
1 /*
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 2 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, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
26 #include "winbindd.h"
28 #ifdef HAVE_ADS
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
34 return our ads connections structure for a domain. We keep the connection
35 open to make things faster
37 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
39 ADS_STRUCT *ads;
40 ADS_STATUS status;
42 if (domain->private_data) {
43 ads = (ADS_STRUCT *)domain->private_data;
45 /* check for a valid structure */
47 DEBUG(7, ("Current tickets expire at %d, time is now %d\n",
48 (uint32) ads->auth.expire, (uint32) time(NULL)));
49 if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
50 return ads;
52 else {
53 /* we own this ADS_STRUCT so make sure it goes away */
54 ads->is_mine = True;
55 ads_destroy( &ads );
56 ads_kdestroy("MEMORY:winbind_ccache");
57 domain->private_data = NULL;
61 /* we don't want this to affect the users ccache */
62 setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
64 ads = ads_init(domain->alt_name, domain->name, NULL);
65 if (!ads) {
66 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
67 return NULL;
70 /* the machine acct password might have change - fetch it every time */
72 SAFE_FREE(ads->auth.password);
73 SAFE_FREE(ads->auth.realm);
75 if ( IS_DC ) {
76 DOM_SID sid;
77 time_t last_set_time;
79 if ( !secrets_fetch_trusted_domain_password( domain->name, &ads->auth.password, &sid, &last_set_time ) ) {
80 ads_destroy( &ads );
81 return NULL;
83 ads->auth.realm = SMB_STRDUP( ads->server.realm );
84 strupper_m( ads->auth.realm );
86 else {
87 struct winbindd_domain *our_domain = domain;
89 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
91 /* always give preference to the alt_name in our
92 primary domain if possible */
94 if ( !domain->primary )
95 our_domain = find_our_domain();
97 if ( our_domain->alt_name[0] != '\0' ) {
98 ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
99 strupper_m( ads->auth.realm );
101 else
102 ads->auth.realm = SMB_STRDUP( lp_realm() );
105 ads->auth.renewable = 1;
107 status = ads_connect(ads);
108 if (!ADS_ERR_OK(status) || !ads->config.realm) {
109 extern struct winbindd_methods msrpc_methods, cache_methods;
110 DEBUG(1,("ads_connect for domain %s failed: %s\n",
111 domain->name, ads_errstr(status)));
112 ads_destroy(&ads);
114 /* if we get ECONNREFUSED then it might be a NT4
115 server, fall back to MSRPC */
116 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
117 status.err.rc == ECONNREFUSED) {
118 DEBUG(1,("Trying MSRPC methods\n"));
119 if (domain->methods == &cache_methods) {
120 domain->backend = &msrpc_methods;
121 } else {
122 domain->methods = &msrpc_methods;
125 return NULL;
128 if (use_nss_info("sfu") && (!ads_check_sfu_mapping(ads))) {
129 DEBUG(0,("ads_cached_connection: failed to check sfu attributes\n"));
130 return NULL;
133 /* set the flag that says we don't own the memory even
134 though we do so that ads_destroy() won't destroy the
135 structure we pass back by reference */
137 ads->is_mine = False;
139 domain->private_data = (void *)ads;
140 return ads;
144 /* Query display info for a realm. This is the basic user list fn */
145 static NTSTATUS query_user_list(struct winbindd_domain *domain,
146 TALLOC_CTX *mem_ctx,
147 uint32 *num_entries,
148 WINBIND_USERINFO **info)
150 ADS_STRUCT *ads = NULL;
151 const char *attrs[] = {"userPrincipalName",
152 "sAMAccountName",
153 "name", "objectSid", "primaryGroupID",
154 "sAMAccountType",
155 ADS_ATTR_SFU_HOMEDIR_OID,
156 ADS_ATTR_SFU_SHELL_OID,
157 ADS_ATTR_SFU_GECOS_OID,
158 NULL};
159 int i, count;
160 ADS_STATUS rc;
161 void *res = NULL;
162 void *msg = NULL;
163 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
165 *num_entries = 0;
167 DEBUG(3,("ads: query_user_list\n"));
169 ads = ads_cached_connection(domain);
171 if (!ads) {
172 domain->last_status = NT_STATUS_SERVER_DISABLED;
173 goto done;
176 rc = ads_search_retry(ads, &res, "(objectClass=user)", attrs);
177 if (!ADS_ERR_OK(rc) || !res) {
178 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
179 goto done;
182 count = ads_count_replies(ads, res);
183 if (count == 0) {
184 DEBUG(1,("query_user_list: No users found\n"));
185 goto done;
188 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count);
189 if (!*info) {
190 status = NT_STATUS_NO_MEMORY;
191 goto done;
194 i = 0;
196 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
197 char *name, *gecos = NULL;
198 char *homedir = NULL;
199 char *shell = NULL;
200 uint32 group;
201 uint32 atype;
203 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
204 ads_atype_map(atype) != SID_NAME_USER) {
205 DEBUG(1,("Not a user account? atype=0x%x\n", atype));
206 continue;
209 name = ads_pull_username(ads, mem_ctx, msg);
211 if (use_nss_info("sfu")) {
212 homedir = ads_pull_string(ads, mem_ctx, msg,
213 ads->schema.sfu_homedir_attr);
214 shell = ads_pull_string(ads, mem_ctx, msg,
215 ads->schema.sfu_shell_attr);
216 gecos = ads_pull_string(ads, mem_ctx, msg,
217 ads->schema.sfu_gecos_attr);
220 if (gecos == NULL) {
221 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
224 if (!ads_pull_sid(ads, msg, "objectSid",
225 &(*info)[i].user_sid)) {
226 DEBUG(1,("No sid for %s !?\n", name));
227 continue;
229 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
230 DEBUG(1,("No primary group for %s !?\n", name));
231 continue;
234 (*info)[i].acct_name = name;
235 (*info)[i].full_name = gecos;
236 (*info)[i].homedir = homedir;
237 (*info)[i].shell = shell;
238 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
239 i++;
242 (*num_entries) = i;
243 status = NT_STATUS_OK;
245 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
247 done:
248 if (res)
249 ads_msgfree(ads, res);
251 return status;
254 /* list all domain groups */
255 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
256 TALLOC_CTX *mem_ctx,
257 uint32 *num_entries,
258 struct acct_info **info)
260 ADS_STRUCT *ads = NULL;
261 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
262 "name", "objectSid", NULL};
263 int i, count;
264 ADS_STATUS rc;
265 void *res = NULL;
266 void *msg = NULL;
267 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
268 const char *filter;
269 BOOL enum_dom_local_groups = False;
271 *num_entries = 0;
273 DEBUG(3,("ads: enum_dom_groups\n"));
275 /* only grab domain local groups for our domain */
276 if ( domain->native_mode && strequal(lp_realm(), domain->alt_name) ) {
277 enum_dom_local_groups = True;
280 /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
281 * rollup-fixes:
283 * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
284 * default value, it MUST be absent. In case of extensible matching the
285 * "dnattr" boolean defaults to FALSE and so it must be only be present
286 * when set to TRUE.
288 * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
289 * filter using bitwise matching rule then the buggy AD fails to decode
290 * the extensible match. As a workaround set it to TRUE and thereby add
291 * the dnAttributes "dn" field to cope with those older AD versions.
292 * It should not harm and won't put any additional load on the AD since
293 * none of the dn components have a bitmask-attribute.
295 * Thanks to Ralf Haferkamp for input and testing - Guenther */
297 filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))",
298 ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
299 ADS_LDAP_MATCHING_RULE_BIT_AND,
300 enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
302 if (filter == NULL) {
303 status = NT_STATUS_NO_MEMORY;
304 goto done;
307 ads = ads_cached_connection(domain);
309 if (!ads) {
310 domain->last_status = NT_STATUS_SERVER_DISABLED;
311 goto done;
314 rc = ads_search_retry(ads, &res, filter, attrs);
315 if (!ADS_ERR_OK(rc) || !res) {
316 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
317 goto done;
320 count = ads_count_replies(ads, res);
321 if (count == 0) {
322 DEBUG(1,("enum_dom_groups: No groups found\n"));
323 goto done;
326 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
327 if (!*info) {
328 status = NT_STATUS_NO_MEMORY;
329 goto done;
332 i = 0;
334 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
335 char *name, *gecos;
336 DOM_SID sid;
337 uint32 rid;
339 name = ads_pull_username(ads, mem_ctx, msg);
340 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
341 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
342 DEBUG(1,("No sid for %s !?\n", name));
343 continue;
346 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
347 DEBUG(1,("No rid for %s !?\n", name));
348 continue;
351 fstrcpy((*info)[i].acct_name, name);
352 fstrcpy((*info)[i].acct_desc, gecos);
353 (*info)[i].rid = rid;
354 i++;
357 (*num_entries) = i;
359 status = NT_STATUS_OK;
361 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
363 done:
364 if (res)
365 ads_msgfree(ads, res);
367 return status;
370 /* list all domain local groups */
371 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
372 TALLOC_CTX *mem_ctx,
373 uint32 *num_entries,
374 struct acct_info **info)
377 * This is a stub function only as we returned the domain
378 * local groups in enum_dom_groups() if the domain->native field
379 * was true. This is a simple performance optimization when
380 * using LDAP.
382 * if we ever need to enumerate domain local groups separately,
383 * then this the optimization in enum_dom_groups() will need
384 * to be split out
386 *num_entries = 0;
388 return NT_STATUS_OK;
391 /* convert a DN to a name, SID and name type
392 this might become a major speed bottleneck if groups have
393 lots of users, in which case we could cache the results
395 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
396 const char *dn,
397 char **name, uint32 *name_type, DOM_SID *sid)
399 void *res = NULL;
400 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
401 "objectSid", "sAMAccountType", NULL};
402 ADS_STATUS rc;
403 uint32 atype;
404 DEBUG(3,("ads: dn_lookup\n"));
406 rc = ads_search_retry_dn(ads, &res, dn, attrs);
408 if (!ADS_ERR_OK(rc) || !res) {
409 goto failed;
412 (*name) = ads_pull_username(ads, mem_ctx, res);
414 if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
415 goto failed;
417 (*name_type) = ads_atype_map(atype);
419 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
420 goto failed;
423 if (res)
424 ads_msgfree(ads, res);
426 return True;
428 failed:
429 if (res)
430 ads_msgfree(ads, res);
432 return False;
435 /* Lookup user information from a rid */
436 static NTSTATUS query_user(struct winbindd_domain *domain,
437 TALLOC_CTX *mem_ctx,
438 const DOM_SID *sid,
439 WINBIND_USERINFO *info)
441 ADS_STRUCT *ads = NULL;
442 const char *attrs[] = {"userPrincipalName",
443 "sAMAccountName",
444 "name",
445 "primaryGroupID",
446 ADS_ATTR_SFU_HOMEDIR_OID,
447 ADS_ATTR_SFU_SHELL_OID,
448 ADS_ATTR_SFU_GECOS_OID,
449 NULL};
450 ADS_STATUS rc;
451 int count;
452 void *msg = NULL;
453 char *ldap_exp;
454 char *sidstr;
455 uint32 group_rid;
456 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
458 DEBUG(3,("ads: query_user\n"));
460 ads = ads_cached_connection(domain);
462 if (!ads) {
463 domain->last_status = NT_STATUS_SERVER_DISABLED;
464 goto done;
467 sidstr = sid_binstring(sid);
468 asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
469 rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
470 free(ldap_exp);
471 free(sidstr);
472 if (!ADS_ERR_OK(rc) || !msg) {
473 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
474 sid_string_static(sid), ads_errstr(rc)));
475 goto done;
478 count = ads_count_replies(ads, msg);
479 if (count != 1) {
480 DEBUG(1,("query_user(sid=%s): Not found\n",
481 sid_string_static(sid)));
482 goto done;
485 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
487 if (use_nss_info("sfu")) {
488 info->homedir = ads_pull_string(ads, mem_ctx, msg,
489 ads->schema.sfu_homedir_attr);
490 info->shell = ads_pull_string(ads, mem_ctx, msg,
491 ads->schema.sfu_shell_attr);
492 info->full_name = ads_pull_string(ads, mem_ctx, msg,
493 ads->schema.sfu_gecos_attr);
496 if (info->full_name == NULL) {
497 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
500 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
501 DEBUG(1,("No primary group for %s !?\n",
502 sid_string_static(sid)));
503 goto done;
506 sid_copy(&info->user_sid, sid);
507 sid_compose(&info->group_sid, &domain->sid, group_rid);
509 status = NT_STATUS_OK;
511 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
512 done:
513 if (msg)
514 ads_msgfree(ads, msg);
516 return status;
519 /* Lookup groups a user is a member of - alternate method, for when
520 tokenGroups are not available. */
521 static NTSTATUS lookup_usergroups_alt(struct winbindd_domain *domain,
522 TALLOC_CTX *mem_ctx,
523 const char *user_dn,
524 DOM_SID *primary_group,
525 size_t *p_num_groups, DOM_SID **user_sids)
527 ADS_STATUS rc;
528 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
529 int count;
530 void *res = NULL;
531 void *msg = NULL;
532 char *ldap_exp;
533 ADS_STRUCT *ads;
534 const char *group_attrs[] = {"objectSid", NULL};
535 char *escaped_dn;
536 size_t num_groups = 0;
538 DEBUG(3,("ads: lookup_usergroups_alt\n"));
540 ads = ads_cached_connection(domain);
542 if (!ads) {
543 domain->last_status = NT_STATUS_SERVER_DISABLED;
544 goto done;
547 if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
548 status = NT_STATUS_NO_MEMORY;
549 goto done;
552 /* buggy server, no tokenGroups. Instead lookup what groups this user
553 is a member of by DN search on member*/
555 if (!(ldap_exp = talloc_asprintf(mem_ctx, "(&(member=%s)(objectClass=group))", escaped_dn))) {
556 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
557 SAFE_FREE(escaped_dn);
558 status = NT_STATUS_NO_MEMORY;
559 goto done;
562 SAFE_FREE(escaped_dn);
564 rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
566 if (!ADS_ERR_OK(rc) || !res) {
567 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
568 return ads_ntstatus(rc);
571 count = ads_count_replies(ads, res);
573 *user_sids = NULL;
574 num_groups = 0;
576 /* always add the primary group to the sid array */
577 add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups);
579 if (count > 0) {
580 for (msg = ads_first_entry(ads, res); msg;
581 msg = ads_next_entry(ads, msg)) {
582 DOM_SID group_sid;
584 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
585 DEBUG(1,("No sid for this group ?!?\n"));
586 continue;
589 add_sid_to_array(mem_ctx, &group_sid, user_sids,
590 &num_groups);
595 *p_num_groups = num_groups;
596 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
598 DEBUG(3,("ads lookup_usergroups (alt) for dn=%s\n", user_dn));
599 done:
600 if (res)
601 ads_msgfree(ads, res);
603 return status;
606 /* Lookup groups a user is a member of. */
607 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
608 TALLOC_CTX *mem_ctx,
609 const DOM_SID *sid,
610 uint32 *p_num_groups, DOM_SID **user_sids)
612 ADS_STRUCT *ads = NULL;
613 const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
614 ADS_STATUS rc;
615 int count;
616 LDAPMessage *msg = NULL;
617 char *user_dn;
618 DOM_SID *sids;
619 int i;
620 DOM_SID primary_group;
621 uint32 primary_group_rid;
622 fstring sid_string;
623 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
624 size_t num_groups = 0;
626 DEBUG(3,("ads: lookup_usergroups\n"));
627 *p_num_groups = 0;
629 ads = ads_cached_connection(domain);
631 if (!ads) {
632 domain->last_status = NT_STATUS_SERVER_DISABLED;
633 status = NT_STATUS_SERVER_DISABLED;
634 goto done;
637 rc = ads_sid_to_dn(ads, mem_ctx, sid, &user_dn);
638 if (!ADS_ERR_OK(rc)) {
639 status = ads_ntstatus(rc);
640 goto done;
643 rc = ads_search_retry_dn(ads, (void**)(void *)&msg, user_dn, attrs);
644 if (!ADS_ERR_OK(rc)) {
645 status = ads_ntstatus(rc);
646 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n",
647 sid_to_string(sid_string, sid), ads_errstr(rc)));
648 goto done;
651 if (!msg) {
652 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
653 sid_to_string(sid_string, sid)));
654 status = NT_STATUS_UNSUCCESSFUL;
655 goto done;
658 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
659 DEBUG(1,("%s: No primary group for sid=%s !?\n",
660 domain->name, sid_to_string(sid_string, sid)));
661 goto done;
664 sid_copy(&primary_group, &domain->sid);
665 sid_append_rid(&primary_group, primary_group_rid);
667 count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
669 if (msg)
670 ads_msgfree(ads, msg);
672 /* there must always be at least one group in the token,
673 unless we are talking to a buggy Win2k server */
674 if (count == 0) {
675 status = lookup_usergroups_alt(domain, mem_ctx, user_dn,
676 &primary_group,
677 &num_groups, user_sids);
678 *p_num_groups = (uint32)num_groups;
679 return status;
682 *user_sids = NULL;
683 num_groups = 0;
685 add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups);
687 for (i=0;i<count;i++) {
689 /* ignore Builtin groups from ADS - Guenther */
690 if (sid_check_is_in_builtin(&sids[i])) {
691 continue;
694 add_sid_to_array_unique(mem_ctx, &sids[i],
695 user_sids, &num_groups);
698 *p_num_groups = (uint32)num_groups;
699 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
701 DEBUG(3,("ads lookup_usergroups for sid=%s\n",
702 sid_to_string(sid_string, sid)));
703 done:
704 return status;
708 find the members of a group, given a group rid and domain
710 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
711 TALLOC_CTX *mem_ctx,
712 const DOM_SID *group_sid, uint32 *num_names,
713 DOM_SID **sid_mem, char ***names,
714 uint32 **name_types)
716 ADS_STATUS rc;
717 int count;
718 void *res=NULL;
719 ADS_STRUCT *ads = NULL;
720 char *ldap_exp;
721 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
722 char *sidstr;
723 char **members;
724 int i;
725 size_t num_members;
726 fstring sid_string;
727 BOOL more_values;
728 const char **attrs;
729 uint32 first_usn;
730 uint32 current_usn;
731 int num_retries = 0;
733 DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
734 sid_string_static(group_sid)));
736 *num_names = 0;
738 ads = ads_cached_connection(domain);
740 if (!ads) {
741 domain->last_status = NT_STATUS_SERVER_DISABLED;
742 goto done;
745 sidstr = sid_binstring(group_sid);
747 /* search for all members of the group */
748 if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
749 SAFE_FREE(sidstr);
750 DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
751 status = NT_STATUS_NO_MEMORY;
752 goto done;
754 SAFE_FREE(sidstr);
756 members = NULL;
757 num_members = 0;
759 attrs = TALLOC_ARRAY(mem_ctx, const char *, 3);
760 attrs[1] = talloc_strdup(mem_ctx, "usnChanged");
761 attrs[2] = NULL;
763 do {
764 if (num_members == 0)
765 attrs[0] = talloc_strdup(mem_ctx, "member");
767 DEBUG(10, ("Searching for attrs[0] = %s, attrs[1] = %s\n", attrs[0], attrs[1]));
769 rc = ads_search_retry(ads, &res, ldap_exp, attrs);
771 if (!ADS_ERR_OK(rc) || !res) {
772 DEBUG(1,("ads: lookup_groupmem ads_search: %s\n",
773 ads_errstr(rc)));
774 status = ads_ntstatus(rc);
775 goto done;
778 count = ads_count_replies(ads, res);
779 if (count == 0)
780 break;
782 if (num_members == 0) {
783 if (!ads_pull_uint32(ads, res, "usnChanged", &first_usn)) {
784 DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
785 goto done;
789 if (!ads_pull_uint32(ads, res, "usnChanged", &current_usn)) {
790 DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
791 goto done;
794 if (first_usn != current_usn) {
795 DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
796 " - restarting search\n"));
797 if (num_retries < 5) {
798 num_retries++;
799 num_members = 0;
800 continue;
801 } else {
802 DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
803 " - restarted search too many times, aborting!\n"));
804 status = NT_STATUS_UNSUCCESSFUL;
805 goto done;
809 members = ads_pull_strings_range(ads, mem_ctx, res,
810 "member",
811 members,
812 &attrs[0],
813 &num_members,
814 &more_values);
816 if ((members == NULL) || (num_members == 0))
817 break;
819 } while (more_values);
821 /* now we need to turn a list of members into rids, names and name types
822 the problem is that the members are in the form of distinguised names
825 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
826 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
827 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
829 for (i=0;i<num_members;i++) {
830 uint32 name_type;
831 char *name;
832 DOM_SID sid;
834 if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
835 (*names)[*num_names] = name;
836 (*name_types)[*num_names] = name_type;
837 sid_copy(&(*sid_mem)[*num_names], &sid);
838 (*num_names)++;
842 status = NT_STATUS_OK;
843 DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
844 done:
846 if (res)
847 ads_msgfree(ads, res);
849 return status;
852 /* find the sequence number for a domain */
853 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
855 ADS_STRUCT *ads = NULL;
856 ADS_STATUS rc;
858 DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
860 *seq = DOM_SEQUENCE_NONE;
862 ads = ads_cached_connection(domain);
864 if (!ads) {
865 domain->last_status = NT_STATUS_SERVER_DISABLED;
866 return NT_STATUS_UNSUCCESSFUL;
869 rc = ads_USN(ads, seq);
871 if (!ADS_ERR_OK(rc)) {
873 /* its a dead connection ; don't destroy it
874 through since ads_USN() has already done
875 that indirectly */
877 domain->private_data = NULL;
879 return ads_ntstatus(rc);
882 /* get a list of trusted domains */
883 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
884 TALLOC_CTX *mem_ctx,
885 uint32 *num_domains,
886 char ***names,
887 char ***alt_names,
888 DOM_SID **dom_sids)
890 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
891 struct ds_domain_trust *domains = NULL;
892 int count = 0;
893 int i;
894 uint32 flags = DS_DOMAIN_DIRECT_OUTBOUND;
895 struct rpc_pipe_client *cli;
897 DEBUG(3,("ads: trusted_domains\n"));
899 *num_domains = 0;
900 *alt_names = NULL;
901 *names = NULL;
902 *dom_sids = NULL;
904 result = cm_connect_netlogon(domain, &cli);
906 if (!NT_STATUS_IS_OK(result)) {
907 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
908 "for PIPE_NETLOGON (%s)\n",
909 domain->name, nt_errstr(result)));
910 return NT_STATUS_UNSUCCESSFUL;
913 if ( NT_STATUS_IS_OK(result) ) {
914 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
915 cli->cli->desthost,
916 flags, &domains,
917 (unsigned int *)&count);
920 if ( NT_STATUS_IS_OK(result) && count) {
922 /* Allocate memory for trusted domain names and sids */
924 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
925 DEBUG(0, ("trusted_domains: out of memory\n"));
926 return NT_STATUS_NO_MEMORY;
929 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
930 DEBUG(0, ("trusted_domains: out of memory\n"));
931 return NT_STATUS_NO_MEMORY;
934 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
935 DEBUG(0, ("trusted_domains: out of memory\n"));
936 return NT_STATUS_NO_MEMORY;
939 /* Copy across names and sids */
941 for (i = 0; i < count; i++) {
942 (*names)[i] = domains[i].netbios_domain;
943 (*alt_names)[i] = domains[i].dns_domain;
945 sid_copy(&(*dom_sids)[i], &domains[i].sid);
948 *num_domains = count;
951 return result;
954 /* the ADS backend methods are exposed via this structure */
955 struct winbindd_methods ads_methods = {
956 True,
957 query_user_list,
958 enum_dom_groups,
959 enum_local_groups,
960 msrpc_name_to_sid,
961 msrpc_sid_to_name,
962 query_user,
963 lookup_usergroups,
964 msrpc_lookup_useraliases,
965 lookup_groupmem,
966 sequence_number,
967 msrpc_lockout_policy,
968 msrpc_password_policy,
969 trusted_domains,
972 #endif