r20124: clean up nested extern declaration warnings
[Samba.git] / source3 / nsswitch / winbindd_ads.c
blob3505f183f212dffd8ec0d8b28ee5778bebd9cfdc
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
33 extern struct winbindd_methods reconnect_methods;
36 return our ads connections structure for a domain. We keep the connection
37 open to make things faster
39 static ADS_STRUCT *ads_cached_connection(struct winbindd_domain *domain)
41 ADS_STRUCT *ads;
42 ADS_STATUS status;
43 enum wb_posix_mapping map_type;
45 DEBUG(10,("ads_cached_connection\n"));
47 if (domain->private_data) {
48 ads = (ADS_STRUCT *)domain->private_data;
50 /* check for a valid structure */
52 DEBUG(7, ("Current tickets expire at %d, time is now %d\n",
53 (uint32) ads->auth.expire, (uint32) time(NULL)));
54 if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
55 return ads;
57 else {
58 /* we own this ADS_STRUCT so make sure it goes away */
59 ads->is_mine = True;
60 ads_destroy( &ads );
61 ads_kdestroy("MEMORY:winbind_ccache");
62 domain->private_data = NULL;
66 /* we don't want this to affect the users ccache */
67 setenv("KRB5CCNAME", "MEMORY:winbind_ccache", 1);
69 ads = ads_init(domain->alt_name, domain->name, NULL);
70 if (!ads) {
71 DEBUG(1,("ads_init for domain %s failed\n", domain->name));
72 return NULL;
75 /* the machine acct password might have change - fetch it every time */
77 SAFE_FREE(ads->auth.password);
78 SAFE_FREE(ads->auth.realm);
80 if ( IS_DC ) {
81 DOM_SID sid;
82 time_t last_set_time;
84 if ( !secrets_fetch_trusted_domain_password( domain->name, &ads->auth.password, &sid, &last_set_time ) ) {
85 ads_destroy( &ads );
86 return NULL;
88 ads->auth.realm = SMB_STRDUP( ads->server.realm );
89 strupper_m( ads->auth.realm );
91 else {
92 struct winbindd_domain *our_domain = domain;
94 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
96 /* always give preference to the alt_name in our
97 primary domain if possible */
99 if ( !domain->primary )
100 our_domain = find_our_domain();
102 if ( our_domain->alt_name[0] != '\0' ) {
103 ads->auth.realm = SMB_STRDUP( our_domain->alt_name );
104 strupper_m( ads->auth.realm );
106 else
107 ads->auth.realm = SMB_STRDUP( lp_realm() );
110 ads->auth.renewable = WINBINDD_PAM_AUTH_KRB5_RENEW_TIME;
112 status = ads_connect(ads);
113 if (!ADS_ERR_OK(status) || !ads->config.realm) {
114 DEBUG(1,("ads_connect for domain %s failed: %s\n",
115 domain->name, ads_errstr(status)));
116 ads_destroy(&ads);
118 /* if we get ECONNREFUSED then it might be a NT4
119 server, fall back to MSRPC */
120 if (status.error_type == ENUM_ADS_ERROR_SYSTEM &&
121 status.err.rc == ECONNREFUSED) {
122 /* 'reconnect_methods' is the MS-RPC backend. */
123 DEBUG(1,("Trying MSRPC methods\n"));
124 domain->backend = &reconnect_methods;
126 return NULL;
129 map_type = get_nss_info(domain->name);
131 if ((map_type == WB_POSIX_MAP_RFC2307)||
132 (map_type == WB_POSIX_MAP_SFU)) {
134 status = ads_check_posix_schema_mapping(ads, map_type);
135 if (!ADS_ERR_OK(status)) {
136 DEBUG(10,("ads_check_posix_schema_mapping failed "
137 "with: %s\n", ads_errstr(status)));
141 /* set the flag that says we don't own the memory even
142 though we do so that ads_destroy() won't destroy the
143 structure we pass back by reference */
145 ads->is_mine = False;
147 domain->private_data = (void *)ads;
148 return ads;
152 /* Query display info for a realm. This is the basic user list fn */
153 static NTSTATUS query_user_list(struct winbindd_domain *domain,
154 TALLOC_CTX *mem_ctx,
155 uint32 *num_entries,
156 WINBIND_USERINFO **info)
158 ADS_STRUCT *ads = NULL;
159 const char *attrs[] = {"userPrincipalName",
160 "sAMAccountName",
161 "name", "objectSid", "primaryGroupID",
162 "sAMAccountType",
163 ADS_ATTR_SFU_HOMEDIR_OID,
164 ADS_ATTR_SFU_SHELL_OID,
165 ADS_ATTR_SFU_GECOS_OID,
166 ADS_ATTR_RFC2307_HOMEDIR_OID,
167 ADS_ATTR_RFC2307_SHELL_OID,
168 ADS_ATTR_RFC2307_GECOS_OID,
169 NULL};
170 int i, count;
171 ADS_STATUS rc;
172 LDAPMessage *res = NULL;
173 LDAPMessage *msg = NULL;
174 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
176 *num_entries = 0;
178 DEBUG(3,("ads: query_user_list\n"));
180 ads = ads_cached_connection(domain);
182 if (!ads) {
183 domain->last_status = NT_STATUS_SERVER_DISABLED;
184 goto done;
187 rc = ads_search_retry(ads, &res, "(objectCategory=user)", attrs);
188 if (!ADS_ERR_OK(rc) || !res) {
189 DEBUG(1,("query_user_list ads_search: %s\n", ads_errstr(rc)));
190 goto done;
193 count = ads_count_replies(ads, res);
194 if (count == 0) {
195 DEBUG(1,("query_user_list: No users found\n"));
196 goto done;
199 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, WINBIND_USERINFO, count);
200 if (!*info) {
201 status = NT_STATUS_NO_MEMORY;
202 goto done;
205 i = 0;
207 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
208 char *name, *gecos = NULL;
209 char *homedir = NULL;
210 char *shell = NULL;
211 uint32 group;
212 uint32 atype;
214 if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype) ||
215 ads_atype_map(atype) != SID_NAME_USER) {
216 DEBUG(1,("Not a user account? atype=0x%x\n", atype));
217 continue;
220 name = ads_pull_username(ads, mem_ctx, msg);
222 if (get_nss_info(domain->name) && ads->schema.map_type) {
224 DEBUG(10,("pulling posix attributes (%s schema)\n",
225 wb_posix_map_str(ads->schema.map_type)));
227 homedir = ads_pull_string(ads, mem_ctx, msg,
228 ads->schema.posix_homedir_attr);
229 shell = ads_pull_string(ads, mem_ctx, msg,
230 ads->schema.posix_shell_attr);
231 gecos = ads_pull_string(ads, mem_ctx, msg,
232 ads->schema.posix_gecos_attr);
235 if (gecos == NULL) {
236 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
239 if (!ads_pull_sid(ads, msg, "objectSid",
240 &(*info)[i].user_sid)) {
241 DEBUG(1,("No sid for %s !?\n", name));
242 continue;
244 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group)) {
245 DEBUG(1,("No primary group for %s !?\n", name));
246 continue;
249 (*info)[i].acct_name = name;
250 (*info)[i].full_name = gecos;
251 (*info)[i].homedir = homedir;
252 (*info)[i].shell = shell;
253 sid_compose(&(*info)[i].group_sid, &domain->sid, group);
254 i++;
257 (*num_entries) = i;
258 status = NT_STATUS_OK;
260 DEBUG(3,("ads query_user_list gave %d entries\n", (*num_entries)));
262 done:
263 if (res)
264 ads_msgfree(ads, res);
266 return status;
269 /* list all domain groups */
270 static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
271 TALLOC_CTX *mem_ctx,
272 uint32 *num_entries,
273 struct acct_info **info)
275 ADS_STRUCT *ads = NULL;
276 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
277 "name", "objectSid", NULL};
278 int i, count;
279 ADS_STATUS rc;
280 LDAPMessage *res = NULL;
281 LDAPMessage *msg = NULL;
282 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
283 const char *filter;
284 BOOL enum_dom_local_groups = False;
286 *num_entries = 0;
288 DEBUG(3,("ads: enum_dom_groups\n"));
290 /* only grab domain local groups for our domain */
291 if ( domain->native_mode && strequal(lp_realm(), domain->alt_name) ) {
292 enum_dom_local_groups = True;
295 /* Workaround ADS LDAP bug present in MS W2K3 SP0 and W2K SP4 w/o
296 * rollup-fixes:
298 * According to Section 5.1(4) of RFC 2251 if a value of a type is it's
299 * default value, it MUST be absent. In case of extensible matching the
300 * "dnattr" boolean defaults to FALSE and so it must be only be present
301 * when set to TRUE.
303 * When it is set to FALSE and the OpenLDAP lib (correctly) encodes a
304 * filter using bitwise matching rule then the buggy AD fails to decode
305 * the extensible match. As a workaround set it to TRUE and thereby add
306 * the dnAttributes "dn" field to cope with those older AD versions.
307 * It should not harm and won't put any additional load on the AD since
308 * none of the dn components have a bitmask-attribute.
310 * Thanks to Ralf Haferkamp for input and testing - Guenther */
312 filter = talloc_asprintf(mem_ctx, "(&(objectCategory=group)(&(groupType:dn:%s:=%d)(!(groupType:dn:%s:=%d))))",
313 ADS_LDAP_MATCHING_RULE_BIT_AND, GROUP_TYPE_SECURITY_ENABLED,
314 ADS_LDAP_MATCHING_RULE_BIT_AND,
315 enum_dom_local_groups ? GROUP_TYPE_BUILTIN_LOCAL_GROUP : GROUP_TYPE_RESOURCE_GROUP);
317 if (filter == NULL) {
318 status = NT_STATUS_NO_MEMORY;
319 goto done;
322 ads = ads_cached_connection(domain);
324 if (!ads) {
325 domain->last_status = NT_STATUS_SERVER_DISABLED;
326 goto done;
329 rc = ads_search_retry(ads, &res, filter, attrs);
330 if (!ADS_ERR_OK(rc) || !res) {
331 DEBUG(1,("enum_dom_groups ads_search: %s\n", ads_errstr(rc)));
332 goto done;
335 count = ads_count_replies(ads, res);
336 if (count == 0) {
337 DEBUG(1,("enum_dom_groups: No groups found\n"));
338 goto done;
341 (*info) = TALLOC_ZERO_ARRAY(mem_ctx, struct acct_info, count);
342 if (!*info) {
343 status = NT_STATUS_NO_MEMORY;
344 goto done;
347 i = 0;
349 for (msg = ads_first_entry(ads, res); msg; msg = ads_next_entry(ads, msg)) {
350 char *name, *gecos;
351 DOM_SID sid;
352 uint32 rid;
354 name = ads_pull_username(ads, mem_ctx, msg);
355 gecos = ads_pull_string(ads, mem_ctx, msg, "name");
356 if (!ads_pull_sid(ads, msg, "objectSid", &sid)) {
357 DEBUG(1,("No sid for %s !?\n", name));
358 continue;
361 if (!sid_peek_check_rid(&domain->sid, &sid, &rid)) {
362 DEBUG(1,("No rid for %s !?\n", name));
363 continue;
366 fstrcpy((*info)[i].acct_name, name);
367 fstrcpy((*info)[i].acct_desc, gecos);
368 (*info)[i].rid = rid;
369 i++;
372 (*num_entries) = i;
374 status = NT_STATUS_OK;
376 DEBUG(3,("ads enum_dom_groups gave %d entries\n", (*num_entries)));
378 done:
379 if (res)
380 ads_msgfree(ads, res);
382 return status;
385 /* list all domain local groups */
386 static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
387 TALLOC_CTX *mem_ctx,
388 uint32 *num_entries,
389 struct acct_info **info)
392 * This is a stub function only as we returned the domain
393 * local groups in enum_dom_groups() if the domain->native field
394 * was true. This is a simple performance optimization when
395 * using LDAP.
397 * if we ever need to enumerate domain local groups separately,
398 * then this the optimization in enum_dom_groups() will need
399 * to be split out
401 *num_entries = 0;
403 return NT_STATUS_OK;
406 /* convert a DN to a name, SID and name type
407 this might become a major speed bottleneck if groups have
408 lots of users, in which case we could cache the results
410 static BOOL dn_lookup(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
411 const char *dn,
412 char **name, uint32 *name_type, DOM_SID *sid)
414 LDAPMessage *res = NULL;
415 const char *attrs[] = {"userPrincipalName", "sAMAccountName",
416 "objectSid", "sAMAccountType", NULL};
417 ADS_STATUS rc;
418 uint32 atype;
419 DEBUG(3,("ads: dn_lookup\n"));
421 rc = ads_search_retry_dn(ads, &res, dn, attrs);
423 if (!ADS_ERR_OK(rc) || !res) {
424 goto failed;
427 (*name) = ads_pull_username(ads, mem_ctx, res);
429 if (!ads_pull_uint32(ads, res, "sAMAccountType", &atype)) {
430 goto failed;
432 (*name_type) = ads_atype_map(atype);
434 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
435 goto failed;
438 if (res)
439 ads_msgfree(ads, res);
441 return True;
443 failed:
444 if (res)
445 ads_msgfree(ads, res);
447 return False;
450 /* Lookup user information from a rid */
451 static NTSTATUS query_user(struct winbindd_domain *domain,
452 TALLOC_CTX *mem_ctx,
453 const DOM_SID *sid,
454 WINBIND_USERINFO *info)
456 ADS_STRUCT *ads = NULL;
457 const char *attrs[] = {"userPrincipalName",
458 "sAMAccountName",
459 "name",
460 "primaryGroupID",
461 ADS_ATTR_SFU_HOMEDIR_OID,
462 ADS_ATTR_SFU_SHELL_OID,
463 ADS_ATTR_SFU_GECOS_OID,
464 ADS_ATTR_RFC2307_HOMEDIR_OID,
465 ADS_ATTR_RFC2307_SHELL_OID,
466 ADS_ATTR_RFC2307_GECOS_OID,
467 NULL};
468 ADS_STATUS rc;
469 int count;
470 LDAPMessage *msg = NULL;
471 char *ldap_exp;
472 char *sidstr;
473 uint32 group_rid;
474 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
476 DEBUG(3,("ads: query_user\n"));
478 ads = ads_cached_connection(domain);
480 if (!ads) {
481 domain->last_status = NT_STATUS_SERVER_DISABLED;
482 goto done;
485 sidstr = sid_binstring(sid);
486 asprintf(&ldap_exp, "(objectSid=%s)", sidstr);
487 rc = ads_search_retry(ads, &msg, ldap_exp, attrs);
488 free(ldap_exp);
489 free(sidstr);
490 if (!ADS_ERR_OK(rc) || !msg) {
491 DEBUG(1,("query_user(sid=%s) ads_search: %s\n",
492 sid_string_static(sid), ads_errstr(rc)));
493 goto done;
496 count = ads_count_replies(ads, msg);
497 if (count != 1) {
498 DEBUG(1,("query_user(sid=%s): Not found\n",
499 sid_string_static(sid)));
500 goto done;
503 info->acct_name = ads_pull_username(ads, mem_ctx, msg);
505 if (get_nss_info(domain->name) && ads->schema.map_type) {
507 DEBUG(10,("pulling posix attributes (%s schema)\n",
508 wb_posix_map_str(ads->schema.map_type)));
510 info->homedir = ads_pull_string(ads, mem_ctx, msg,
511 ads->schema.posix_homedir_attr);
512 info->shell = ads_pull_string(ads, mem_ctx, msg,
513 ads->schema.posix_shell_attr);
514 info->full_name = ads_pull_string(ads, mem_ctx, msg,
515 ads->schema.posix_gecos_attr);
518 if (info->full_name == NULL) {
519 info->full_name = ads_pull_string(ads, mem_ctx, msg, "name");
522 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &group_rid)) {
523 DEBUG(1,("No primary group for %s !?\n",
524 sid_string_static(sid)));
525 goto done;
528 sid_copy(&info->user_sid, sid);
529 sid_compose(&info->group_sid, &domain->sid, group_rid);
531 status = NT_STATUS_OK;
533 DEBUG(3,("ads query_user gave %s\n", info->acct_name));
534 done:
535 if (msg)
536 ads_msgfree(ads, msg);
538 return status;
541 /* Lookup groups a user is a member of - alternate method, for when
542 tokenGroups are not available. */
543 static NTSTATUS lookup_usergroups_member(struct winbindd_domain *domain,
544 TALLOC_CTX *mem_ctx,
545 const char *user_dn,
546 DOM_SID *primary_group,
547 size_t *p_num_groups, DOM_SID **user_sids)
549 ADS_STATUS rc;
550 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
551 int count;
552 LDAPMessage *res = NULL;
553 LDAPMessage *msg = NULL;
554 char *ldap_exp;
555 ADS_STRUCT *ads;
556 const char *group_attrs[] = {"objectSid", NULL};
557 char *escaped_dn;
558 size_t num_groups = 0;
560 DEBUG(3,("ads: lookup_usergroups_member\n"));
562 ads = ads_cached_connection(domain);
564 if (!ads) {
565 domain->last_status = NT_STATUS_SERVER_DISABLED;
566 goto done;
569 if (!(escaped_dn = escape_ldap_string_alloc(user_dn))) {
570 status = NT_STATUS_NO_MEMORY;
571 goto done;
574 if (!(ldap_exp = talloc_asprintf(mem_ctx, "(&(member=%s)(objectCategory=group))", escaped_dn))) {
575 DEBUG(1,("lookup_usergroups(dn=%s) asprintf failed!\n", user_dn));
576 SAFE_FREE(escaped_dn);
577 status = NT_STATUS_NO_MEMORY;
578 goto done;
581 SAFE_FREE(escaped_dn);
583 rc = ads_search_retry(ads, &res, ldap_exp, group_attrs);
585 if (!ADS_ERR_OK(rc) || !res) {
586 DEBUG(1,("lookup_usergroups ads_search member=%s: %s\n", user_dn, ads_errstr(rc)));
587 return ads_ntstatus(rc);
590 count = ads_count_replies(ads, res);
592 *user_sids = NULL;
593 num_groups = 0;
595 /* always add the primary group to the sid array */
596 if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
597 status = NT_STATUS_NO_MEMORY;
598 goto done;
601 if (count > 0) {
602 for (msg = ads_first_entry(ads, res); msg;
603 msg = ads_next_entry(ads, msg)) {
604 DOM_SID group_sid;
606 if (!ads_pull_sid(ads, msg, "objectSid", &group_sid)) {
607 DEBUG(1,("No sid for this group ?!?\n"));
608 continue;
611 /* ignore Builtin groups from ADS - Guenther */
612 if (sid_check_is_in_builtin(&group_sid)) {
613 continue;
616 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
617 &num_groups)) {
618 status = NT_STATUS_NO_MEMORY;
619 goto done;
625 *p_num_groups = num_groups;
626 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
628 DEBUG(3,("ads lookup_usergroups (member) succeeded for dn=%s\n", user_dn));
629 done:
630 if (res)
631 ads_msgfree(ads, res);
633 return status;
636 /* Lookup groups a user is a member of - alternate method, for when
637 tokenGroups are not available. */
638 static NTSTATUS lookup_usergroups_memberof(struct winbindd_domain *domain,
639 TALLOC_CTX *mem_ctx,
640 const char *user_dn,
641 DOM_SID *primary_group,
642 size_t *p_num_groups, DOM_SID **user_sids)
644 ADS_STATUS rc;
645 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
646 int count;
647 LDAPMessage *res = NULL;
648 ADS_STRUCT *ads;
649 const char *attrs[] = {"memberOf", NULL};
650 size_t num_groups = 0;
651 DOM_SID *group_sids = NULL;
652 int i;
654 DEBUG(3,("ads: lookup_usergroups_memberof\n"));
656 ads = ads_cached_connection(domain);
658 if (!ads) {
659 domain->last_status = NT_STATUS_SERVER_DISABLED;
660 goto done;
663 rc = ads_search_retry_extended_dn(ads, &res, user_dn, attrs,
664 ADS_EXTENDED_DN_HEX_STRING);
666 if (!ADS_ERR_OK(rc) || !res) {
667 DEBUG(1,("lookup_usergroups_memberof ads_search member=%s: %s\n",
668 user_dn, ads_errstr(rc)));
669 return ads_ntstatus(rc);
672 count = ads_count_replies(ads, res);
674 if (count == 0) {
675 status = NT_STATUS_NO_SUCH_USER;
676 goto done;
679 *user_sids = NULL;
680 num_groups = 0;
682 /* always add the primary group to the sid array */
683 if (!add_sid_to_array(mem_ctx, primary_group, user_sids, &num_groups)) {
684 status = NT_STATUS_NO_MEMORY;
685 goto done;
688 count = ads_pull_sids_from_extendeddn(ads, mem_ctx, res, "memberOf",
689 ADS_EXTENDED_DN_HEX_STRING,
690 &group_sids);
691 if (count == 0) {
692 DEBUG(1,("No memberOf for this user?!?\n"));
693 status = NT_STATUS_NO_MEMORY;
694 goto done;
697 for (i=0; i<count; i++) {
699 /* ignore Builtin groups from ADS - Guenther */
700 if (sid_check_is_in_builtin(&group_sids[i])) {
701 continue;
704 if (!add_sid_to_array(mem_ctx, &group_sids[i], user_sids,
705 &num_groups)) {
706 status = NT_STATUS_NO_MEMORY;
707 goto done;
712 *p_num_groups = num_groups;
713 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
715 DEBUG(3,("ads lookup_usergroups (memberof) succeeded for dn=%s\n", user_dn));
716 done:
717 TALLOC_FREE(group_sids);
718 if (res)
719 ads_msgfree(ads, res);
721 return status;
725 /* Lookup groups a user is a member of. */
726 static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
727 TALLOC_CTX *mem_ctx,
728 const DOM_SID *sid,
729 uint32 *p_num_groups, DOM_SID **user_sids)
731 ADS_STRUCT *ads = NULL;
732 const char *attrs[] = {"tokenGroups", "primaryGroupID", NULL};
733 ADS_STATUS rc;
734 int count;
735 LDAPMessage *msg = NULL;
736 char *user_dn = NULL;
737 DOM_SID *sids;
738 int i;
739 DOM_SID primary_group;
740 uint32 primary_group_rid;
741 fstring sid_string;
742 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
743 size_t num_groups = 0;
745 DEBUG(3,("ads: lookup_usergroups\n"));
746 *p_num_groups = 0;
748 status = lookup_usergroups_cached(domain, mem_ctx, sid,
749 p_num_groups, user_sids);
750 if (NT_STATUS_IS_OK(status)) {
751 return NT_STATUS_OK;
754 ads = ads_cached_connection(domain);
756 if (!ads) {
757 domain->last_status = NT_STATUS_SERVER_DISABLED;
758 status = NT_STATUS_SERVER_DISABLED;
759 goto done;
762 rc = ads_search_retry_sid(ads, &msg, sid, attrs);
764 if (!ADS_ERR_OK(rc)) {
765 status = ads_ntstatus(rc);
766 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: %s\n",
767 sid_to_string(sid_string, sid), ads_errstr(rc)));
768 goto done;
771 count = ads_count_replies(ads, msg);
772 if (count != 1) {
773 status = NT_STATUS_UNSUCCESSFUL;
774 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: "
775 "invalid number of results (count=%d)\n",
776 sid_to_string(sid_string, sid), count));
777 goto done;
780 if (!msg) {
781 DEBUG(1,("lookup_usergroups(sid=%s) ads_search tokenGroups: NULL msg\n",
782 sid_to_string(sid_string, sid)));
783 status = NT_STATUS_UNSUCCESSFUL;
784 goto done;
787 user_dn = ads_get_dn(ads, msg);
788 if (user_dn == NULL) {
789 status = NT_STATUS_NO_MEMORY;
790 goto done;
793 if (!ads_pull_uint32(ads, msg, "primaryGroupID", &primary_group_rid)) {
794 DEBUG(1,("%s: No primary group for sid=%s !?\n",
795 domain->name, sid_to_string(sid_string, sid)));
796 goto done;
799 sid_copy(&primary_group, &domain->sid);
800 sid_append_rid(&primary_group, primary_group_rid);
802 count = ads_pull_sids(ads, mem_ctx, msg, "tokenGroups", &sids);
804 /* there must always be at least one group in the token,
805 unless we are talking to a buggy Win2k server */
807 /* actually this only happens when the machine account has no read
808 * permissions on the tokenGroup attribute - gd */
810 if (count == 0) {
812 /* no tokenGroups */
814 /* lookup what groups this user is a member of by DN search on
815 * "memberOf" */
817 status = lookup_usergroups_memberof(domain, mem_ctx, user_dn,
818 &primary_group,
819 &num_groups, user_sids);
820 *p_num_groups = (uint32)num_groups;
821 if (NT_STATUS_IS_OK(status)) {
822 goto done;
825 /* lookup what groups this user is a member of by DN search on
826 * "member" */
828 status = lookup_usergroups_member(domain, mem_ctx, user_dn,
829 &primary_group,
830 &num_groups, user_sids);
831 *p_num_groups = (uint32)num_groups;
832 goto done;
835 *user_sids = NULL;
836 num_groups = 0;
838 if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
839 status = NT_STATUS_NO_MEMORY;
840 goto done;
843 for (i=0;i<count;i++) {
845 /* ignore Builtin groups from ADS - Guenther */
846 if (sid_check_is_in_builtin(&sids[i])) {
847 continue;
850 if (!add_sid_to_array_unique(mem_ctx, &sids[i],
851 user_sids, &num_groups)) {
852 status = NT_STATUS_NO_MEMORY;
853 goto done;
857 *p_num_groups = (uint32)num_groups;
858 status = (*user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
860 DEBUG(3,("ads lookup_usergroups (tokenGroups) succeeded for sid=%s\n",
861 sid_to_string(sid_string, sid)));
862 done:
863 ads_memfree(ads, user_dn);
864 ads_msgfree(ads, msg);
865 return status;
869 find the members of a group, given a group rid and domain
871 static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
872 TALLOC_CTX *mem_ctx,
873 const DOM_SID *group_sid, uint32 *num_names,
874 DOM_SID **sid_mem, char ***names,
875 uint32 **name_types)
877 ADS_STATUS rc;
878 int count;
879 LDAPMessage *res=NULL;
880 ADS_STRUCT *ads = NULL;
881 char *ldap_exp;
882 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
883 char *sidstr;
884 char **members;
885 int i;
886 size_t num_members;
887 fstring sid_string;
888 BOOL more_values;
889 const char **attrs;
890 uint32 first_usn;
891 uint32 current_usn;
892 int num_retries = 0;
894 DEBUG(10,("ads: lookup_groupmem %s sid=%s\n", domain->name,
895 sid_string_static(group_sid)));
897 *num_names = 0;
899 ads = ads_cached_connection(domain);
901 if (!ads) {
902 domain->last_status = NT_STATUS_SERVER_DISABLED;
903 goto done;
906 if ((sidstr = sid_binstring(group_sid)) == NULL) {
907 status = NT_STATUS_NO_MEMORY;
908 goto done;
911 /* search for all members of the group */
912 if (!(ldap_exp = talloc_asprintf(mem_ctx, "(objectSid=%s)",sidstr))) {
913 SAFE_FREE(sidstr);
914 DEBUG(1, ("ads: lookup_groupmem: tallloc_asprintf for ldap_exp failed!\n"));
915 status = NT_STATUS_NO_MEMORY;
916 goto done;
918 SAFE_FREE(sidstr);
920 members = NULL;
921 num_members = 0;
923 if ((attrs = TALLOC_ARRAY(mem_ctx, const char *, 3)) == NULL) {
924 status = NT_STATUS_NO_MEMORY;
925 goto done;
928 attrs[1] = talloc_strdup(mem_ctx, "usnChanged");
929 attrs[2] = NULL;
931 do {
932 if (num_members == 0)
933 attrs[0] = talloc_strdup(mem_ctx, "member");
935 DEBUG(10, ("Searching for attrs[0] = %s, attrs[1] = %s\n", attrs[0], attrs[1]));
937 rc = ads_search_retry(ads, &res, ldap_exp, attrs);
939 if (!ADS_ERR_OK(rc) || !res) {
940 DEBUG(1,("ads: lookup_groupmem ads_search: %s\n",
941 ads_errstr(rc)));
942 status = ads_ntstatus(rc);
943 goto done;
946 count = ads_count_replies(ads, res);
947 if (count == 0)
948 break;
950 if (num_members == 0) {
951 if (!ads_pull_uint32(ads, res, "usnChanged", &first_usn)) {
952 DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
953 goto done;
957 if (!ads_pull_uint32(ads, res, "usnChanged", &current_usn)) {
958 DEBUG(1, ("ads: lookup_groupmem could not pull usnChanged!\n"));
959 goto done;
962 if (first_usn != current_usn) {
963 DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
964 " - restarting search\n"));
965 if (num_retries < 5) {
966 num_retries++;
967 num_members = 0;
968 continue;
969 } else {
970 DEBUG(5, ("ads: lookup_groupmem USN on this record changed"
971 " - restarted search too many times, aborting!\n"));
972 status = NT_STATUS_UNSUCCESSFUL;
973 goto done;
977 members = ads_pull_strings_range(ads, mem_ctx, res,
978 "member",
979 members,
980 &attrs[0],
981 &num_members,
982 &more_values);
984 if ((members == NULL) || (num_members == 0))
985 break;
987 } while (more_values);
989 /* now we need to turn a list of members into rids, names and name types
990 the problem is that the members are in the form of distinguised names
993 (*sid_mem) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, num_members);
994 (*name_types) = TALLOC_ZERO_ARRAY(mem_ctx, uint32, num_members);
995 (*names) = TALLOC_ZERO_ARRAY(mem_ctx, char *, num_members);
997 if ((num_members != 0) &&
998 ((members == NULL) || (*sid_mem == NULL) ||
999 (*name_types == NULL) || (*names == NULL))) {
1000 DEBUG(1, ("talloc failed\n"));
1001 status = NT_STATUS_NO_MEMORY;
1002 goto done;
1005 for (i=0;i<num_members;i++) {
1006 uint32 name_type;
1007 char *name;
1008 DOM_SID sid;
1010 if (dn_lookup(ads, mem_ctx, members[i], &name, &name_type, &sid)) {
1011 (*names)[*num_names] = name;
1012 (*name_types)[*num_names] = name_type;
1013 sid_copy(&(*sid_mem)[*num_names], &sid);
1014 (*num_names)++;
1018 status = NT_STATUS_OK;
1019 DEBUG(3,("ads lookup_groupmem for sid=%s\n", sid_to_string(sid_string, group_sid)));
1020 done:
1022 if (res)
1023 ads_msgfree(ads, res);
1025 return status;
1028 /* find the sequence number for a domain */
1029 static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
1031 ADS_STRUCT *ads = NULL;
1032 ADS_STATUS rc;
1034 DEBUG(3,("ads: fetch sequence_number for %s\n", domain->name));
1036 *seq = DOM_SEQUENCE_NONE;
1038 ads = ads_cached_connection(domain);
1040 if (!ads) {
1041 domain->last_status = NT_STATUS_SERVER_DISABLED;
1042 return NT_STATUS_UNSUCCESSFUL;
1045 rc = ads_USN(ads, seq);
1047 if (!ADS_ERR_OK(rc)) {
1049 /* its a dead connection ; don't destroy it
1050 through since ads_USN() has already done
1051 that indirectly */
1053 domain->private_data = NULL;
1055 return ads_ntstatus(rc);
1058 /* get a list of trusted domains */
1059 static NTSTATUS trusted_domains(struct winbindd_domain *domain,
1060 TALLOC_CTX *mem_ctx,
1061 uint32 *num_domains,
1062 char ***names,
1063 char ***alt_names,
1064 DOM_SID **dom_sids)
1066 NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
1067 struct ds_domain_trust *domains = NULL;
1068 int count = 0;
1069 int i;
1070 uint32 flags = DS_DOMAIN_IN_FOREST | DS_DOMAIN_DIRECT_OUTBOUND;
1071 struct rpc_pipe_client *cli;
1073 DEBUG(3,("ads: trusted_domains\n"));
1075 *num_domains = 0;
1076 *alt_names = NULL;
1077 *names = NULL;
1078 *dom_sids = NULL;
1080 result = cm_connect_netlogon(domain, &cli);
1082 if (!NT_STATUS_IS_OK(result)) {
1083 DEBUG(5, ("trusted_domains: Could not open a connection to %s "
1084 "for PIPE_NETLOGON (%s)\n",
1085 domain->name, nt_errstr(result)));
1086 return NT_STATUS_UNSUCCESSFUL;
1089 if ( NT_STATUS_IS_OK(result) ) {
1090 result = rpccli_ds_enum_domain_trusts(cli, mem_ctx,
1091 cli->cli->desthost,
1092 flags, &domains,
1093 (unsigned int *)&count);
1096 if ( NT_STATUS_IS_OK(result) && count) {
1098 /* Allocate memory for trusted domain names and sids */
1100 if ( !(*names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1101 DEBUG(0, ("trusted_domains: out of memory\n"));
1102 return NT_STATUS_NO_MEMORY;
1105 if ( !(*alt_names = TALLOC_ARRAY(mem_ctx, char *, count)) ) {
1106 DEBUG(0, ("trusted_domains: out of memory\n"));
1107 return NT_STATUS_NO_MEMORY;
1110 if ( !(*dom_sids = TALLOC_ARRAY(mem_ctx, DOM_SID, count)) ) {
1111 DEBUG(0, ("trusted_domains: out of memory\n"));
1112 return NT_STATUS_NO_MEMORY;
1115 /* Copy across names and sids */
1117 for (i = 0; i < count; i++) {
1118 (*names)[i] = domains[i].netbios_domain;
1119 (*alt_names)[i] = domains[i].dns_domain;
1121 sid_copy(&(*dom_sids)[i], &domains[i].sid);
1124 *num_domains = count;
1127 return result;
1130 /* the ADS backend methods are exposed via this structure */
1131 struct winbindd_methods ads_methods = {
1132 True,
1133 query_user_list,
1134 enum_dom_groups,
1135 enum_local_groups,
1136 msrpc_name_to_sid,
1137 msrpc_sid_to_name,
1138 msrpc_rids_to_names,
1139 query_user,
1140 lookup_usergroups,
1141 msrpc_lookup_useraliases,
1142 lookup_groupmem,
1143 sequence_number,
1144 msrpc_lockout_policy,
1145 msrpc_password_policy,
1146 trusted_domains,
1149 #endif