r7415: * big change -- volker's new async winbindd from trunk
[Samba/gbeck.git] / source / nsswitch / winbindd_group.c
blob1138762cc4045da8dcd60ac72428a55def94a3ed
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
9 Copyright (C) Volker Lendecke 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "includes.h"
27 #include "winbindd.h"
29 extern BOOL opt_nocache;
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
34 /***************************************************************
35 Empty static struct for negative caching.
36 ****************************************************************/
38 /* Fill a grent structure from various other information */
40 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
41 const char *gr_name, gid_t unix_gid)
43 fstring full_group_name;
44 /* Fill in uid/gid */
45 fill_domain_username(full_group_name, dom_name, gr_name);
47 gr->gr_gid = unix_gid;
49 /* Group name and password */
51 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
52 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
54 return True;
57 /* Fill in the group membership field of a NT group given by group_sid */
59 static BOOL fill_grent_mem(struct winbindd_domain *domain,
60 DOM_SID *group_sid,
61 enum SID_NAME_USE group_name_type,
62 int *num_gr_mem, char **gr_mem, int *gr_mem_len)
64 DOM_SID *sid_mem = NULL;
65 uint32 num_names = 0;
66 uint32 *name_types = NULL;
67 unsigned int buf_len, buf_ndx, i;
68 char **names = NULL, *buf;
69 BOOL result = False;
70 TALLOC_CTX *mem_ctx;
71 NTSTATUS status;
72 fstring sid_string;
74 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
75 return False;
77 /* Initialise group membership information */
79 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
81 *num_gr_mem = 0;
83 /* HACK ALERT!! This whole routine does not cope with group members
84 * from more than one domain, ie aliases. Thus we have to work it out
85 * ourselves in a special routine. */
87 if (domain->internal)
88 return fill_passdb_alias_grmem(domain, group_sid,
89 num_gr_mem,
90 gr_mem, gr_mem_len);
92 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
93 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
95 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
96 sid_to_string(sid_string, group_sid), domain->name,
97 group_name_type));
98 goto done;
101 /* Lookup group members */
102 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
103 &sid_mem, &names, &name_types);
104 if (!NT_STATUS_IS_OK(status)) {
105 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
106 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
108 goto done;
111 DEBUG(10, ("looked up %d names\n", num_names));
113 if (DEBUGLEVEL >= 10) {
114 for (i = 0; i < num_names; i++)
115 DEBUG(10, ("\t%20s %s %d\n", names[i],
116 sid_string_static(&sid_mem[i]),
117 name_types[i]));
120 /* Add members to list */
122 buf = NULL;
123 buf_len = buf_ndx = 0;
125 again:
127 for (i = 0; i < num_names; i++) {
128 char *the_name;
129 fstring name;
130 int len;
132 the_name = names[i];
134 DEBUG(10, ("processing name %s\n", the_name));
136 /* FIXME: need to cope with groups within groups. These
137 occur in Universal groups on a Windows 2000 native mode
138 server. */
140 /* make sure to allow machine accounts */
142 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
143 DEBUG(3, ("name %s isn't a domain user\n", the_name));
144 continue;
147 /* Append domain name */
149 fill_domain_username(name, domain->name, the_name);
151 len = strlen(name);
153 /* Add to list or calculate buffer length */
155 if (!buf) {
156 buf_len += len + 1; /* List is comma separated */
157 (*num_gr_mem)++;
158 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
159 } else {
160 DEBUG(10, ("appending %s at ndx %d\n", name, len));
161 safe_strcpy(&buf[buf_ndx], name, len);
162 buf_ndx += len;
163 buf[buf_ndx] = ',';
164 buf_ndx++;
168 /* Allocate buffer */
170 if (!buf && buf_len != 0) {
171 if (!(buf = SMB_MALLOC(buf_len))) {
172 DEBUG(1, ("out of memory\n"));
173 result = False;
174 goto done;
176 memset(buf, 0, buf_len);
177 goto again;
180 if (buf && buf_ndx > 0) {
181 buf[buf_ndx - 1] = '\0';
184 *gr_mem = buf;
185 *gr_mem_len = buf_len;
187 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
188 buf_len, *num_gr_mem ? buf : "NULL"));
189 result = True;
191 done:
193 talloc_destroy(mem_ctx);
195 DEBUG(10, ("fill_grent_mem returning %d\n", result));
197 return result;
200 /* Return a group structure from a group name */
202 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
204 DOM_SID group_sid;
205 struct winbindd_domain *domain;
206 enum SID_NAME_USE name_type;
207 fstring name_domain, name_group;
208 char *tmp, *gr_mem;
209 int gr_mem_len;
210 gid_t gid;
212 /* Ensure null termination */
213 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
215 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
216 state->request.data.groupname));
218 /* Parse domain and groupname */
220 memset(name_group, 0, sizeof(fstring));
222 tmp = state->request.data.groupname;
224 parse_domain_user(tmp, name_domain, name_group);
226 /* if no domain or our local domain and no local tdb group, default to
227 * our local domain for aliases */
229 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
230 fstrcpy(name_domain, get_global_sam_name());
233 /* Get info for the domain */
235 if ((domain = find_domain_from_name(name_domain)) == NULL) {
236 DEBUG(3, ("could not get domain sid for domain %s\n",
237 name_domain));
238 return WINBINDD_ERROR;
240 /* should we deal with users for our domain? */
242 if ( lp_winbind_trusted_domains_only() && domain->primary) {
243 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
244 name_domain, name_group));
245 return WINBINDD_ERROR;
248 /* Get rid and name type from name */
250 if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name,
251 name_group, &group_sid, &name_type)) {
252 DEBUG(1, ("group %s in domain %s does not exist\n",
253 name_group, name_domain));
254 return WINBINDD_ERROR;
257 if ( !((name_type==SID_NAME_DOM_GRP) ||
258 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
259 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
261 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
262 name_group, name_type));
263 return WINBINDD_ERROR;
266 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
267 DEBUG(1, ("error converting unix gid to sid\n"));
268 return WINBINDD_ERROR;
271 if (!fill_grent(&state->response.data.gr, name_domain,
272 name_group, gid) ||
273 !fill_grent_mem(domain, &group_sid, name_type,
274 &state->response.data.gr.num_gr_mem,
275 &gr_mem, &gr_mem_len)) {
276 return WINBINDD_ERROR;
279 /* Group membership lives at start of extra data */
281 state->response.data.gr.gr_mem_ofs = 0;
283 state->response.length += gr_mem_len;
284 state->response.extra_data = gr_mem;
286 return WINBINDD_OK;
289 /* Return a group structure from a gid number */
291 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
293 struct winbindd_domain *domain;
294 DOM_SID group_sid;
295 enum SID_NAME_USE name_type;
296 fstring dom_name;
297 fstring group_name;
298 int gr_mem_len;
299 char *gr_mem;
301 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
302 (unsigned long)state->request.data.gid));
304 /* Bug out if the gid isn't in the winbind range */
306 if ((state->request.data.gid < server_state.gid_low) ||
307 (state->request.data.gid > server_state.gid_high))
308 return WINBINDD_ERROR;
310 /* Get rid from gid */
311 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid, 0))) {
312 DEBUG(1, ("could not convert gid %lu to rid\n",
313 (unsigned long)state->request.data.gid));
314 return WINBINDD_ERROR;
317 /* Get name from sid */
319 if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name,
320 group_name, &name_type)) {
321 DEBUG(1, ("could not lookup sid\n"));
322 return WINBINDD_ERROR;
325 /* Fill in group structure */
327 domain = find_domain_from_sid_noinit(&group_sid);
329 if (!domain) {
330 DEBUG(1,("Can't find domain from sid\n"));
331 return WINBINDD_ERROR;
334 if ( !((name_type==SID_NAME_DOM_GRP) ||
335 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
336 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
338 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
339 group_name, name_type));
340 return WINBINDD_ERROR;
343 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
344 state->request.data.gid) ||
345 !fill_grent_mem(domain, &group_sid, name_type,
346 &state->response.data.gr.num_gr_mem,
347 &gr_mem, &gr_mem_len))
348 return WINBINDD_ERROR;
350 /* Group membership lives at start of extra data */
352 state->response.data.gr.gr_mem_ofs = 0;
354 state->response.length += gr_mem_len;
355 state->response.extra_data = gr_mem;
357 return WINBINDD_OK;
361 * set/get/endgrent functions
364 /* "Rewind" file pointer for group database enumeration */
366 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
368 struct winbindd_domain *domain;
370 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
372 /* Check user has enabled this */
374 if (!lp_winbind_enum_groups())
375 return WINBINDD_ERROR;
377 /* Free old static data if it exists */
379 if (state->getgrent_state != NULL) {
380 free_getent_state(state->getgrent_state);
381 state->getgrent_state = NULL;
384 /* Create sam pipes for each domain we know about */
386 for (domain = domain_list(); domain != NULL; domain = domain->next) {
387 struct getent_state *domain_state;
389 /* Create a state record for this domain */
391 /* don't add our domaina if we are a PDC or if we
392 are a member of a Samba domain */
394 if ( lp_winbind_trusted_domains_only() && domain->primary )
396 continue;
400 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
401 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
402 return WINBINDD_ERROR;
405 ZERO_STRUCTP(domain_state);
407 fstrcpy(domain_state->domain_name, domain->name);
409 /* Add to list of open domains */
411 DLIST_ADD(state->getgrent_state, domain_state);
414 state->getgrent_initialized = True;
416 return WINBINDD_OK;
419 /* Close file pointer to ntdom group database */
421 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
423 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
425 free_getent_state(state->getgrent_state);
426 state->getgrent_initialized = False;
427 state->getgrent_state = NULL;
429 return WINBINDD_OK;
432 /* Get the list of domain groups and domain aliases for a domain. We fill in
433 the sam_entries and num_sam_entries fields with domain group information.
434 The dispinfo_ndx field is incremented to the index of the next group to
435 fetch. Return True if some groups were returned, False otherwise. */
437 static BOOL get_sam_group_entries(struct getent_state *ent)
439 NTSTATUS status;
440 uint32 num_entries;
441 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
442 TALLOC_CTX *mem_ctx;
443 BOOL result = False;
444 struct acct_info *sam_grp_entries = NULL;
445 struct winbindd_domain *domain;
447 if (ent->got_sam_entries)
448 return False;
450 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
451 ent->domain_name))) {
452 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
453 return False;
456 /* Free any existing group info */
458 SAFE_FREE(ent->sam_entries);
459 ent->num_sam_entries = 0;
460 ent->got_sam_entries = True;
462 /* Enumerate domain groups */
464 num_entries = 0;
466 if (!(domain = find_domain_from_name(ent->domain_name))) {
467 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
468 goto done;
471 /* always get the domain global groups */
473 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
475 if (!NT_STATUS_IS_OK(status)) {
476 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
477 result = False;
478 goto done;
481 /* Copy entries into return buffer */
483 if (num_entries) {
484 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
485 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
486 num_entries));
487 result = False;
488 goto done;
490 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
493 ent->num_sam_entries = num_entries;
495 /* get the domain local groups if we are a member of a native win2k domain
496 and are not using LDAP to get the groups */
498 if ( ( lp_security() != SEC_ADS && domain->native_mode
499 && domain->primary) || domain->internal )
501 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
503 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
505 if ( !NT_STATUS_IS_OK(status) ) {
506 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
507 num_entries = 0;
509 else
510 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
512 /* Copy entries into return buffer */
514 if ( num_entries ) {
515 if ( !(tmp_name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
517 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
518 num_entries));
519 result = False;
520 SAFE_FREE( name_list );
521 goto done;
524 name_list = tmp_name_list;
526 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
527 num_entries * sizeof(struct acct_info) );
530 ent->num_sam_entries += num_entries;
534 /* Fill in remaining fields */
536 ent->sam_entries = name_list;
537 ent->sam_entry_index = 0;
539 result = (ent->num_sam_entries > 0);
541 done:
542 talloc_destroy(mem_ctx);
544 return result;
547 /* Fetch next group entry from ntdom database */
549 #define MAX_GETGRENT_GROUPS 500
551 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
553 struct getent_state *ent;
554 struct winbindd_gr *group_list = NULL;
555 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
556 char *new_extra_data, *gr_mem_list = NULL;
558 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
560 /* Check user has enabled this */
562 if (!lp_winbind_enum_groups())
563 return WINBINDD_ERROR;
565 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
567 if ((state->response.extra_data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL)
568 return WINBINDD_ERROR;
570 memset(state->response.extra_data, '\0',
571 num_groups * sizeof(struct winbindd_gr) );
573 state->response.data.num_entries = 0;
575 group_list = (struct winbindd_gr *)state->response.extra_data;
577 if (!state->getgrent_initialized)
578 winbindd_setgrent(state);
580 if (!(ent = state->getgrent_state))
581 return WINBINDD_ERROR;
583 /* Start sending back groups */
585 for (i = 0; i < num_groups; i++) {
586 struct acct_info *name_list = NULL;
587 fstring domain_group_name;
588 uint32 result;
589 gid_t group_gid;
590 int gr_mem_len;
591 char *gr_mem, *new_gr_mem_list;
592 DOM_SID group_sid;
593 struct winbindd_domain *domain;
595 /* Do we need to fetch another chunk of groups? */
597 tryagain:
599 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
600 ent->sam_entry_index, ent->num_sam_entries));
602 if (ent->num_sam_entries == ent->sam_entry_index) {
604 while(ent && !get_sam_group_entries(ent)) {
605 struct getent_state *next_ent;
607 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
609 /* Free state information for this domain */
611 SAFE_FREE(ent->sam_entries);
613 next_ent = ent->next;
614 DLIST_REMOVE(state->getgrent_state, ent);
616 SAFE_FREE(ent);
617 ent = next_ent;
620 /* No more domains */
622 if (!ent)
623 break;
626 name_list = ent->sam_entries;
628 if (!(domain =
629 find_domain_from_name(ent->domain_name))) {
630 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
631 result = False;
632 goto done;
635 /* Lookup group info */
637 sid_copy(&group_sid, &domain->sid);
638 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
640 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
642 DEBUG(1, ("could not look up gid for group %s\n",
643 name_list[ent->sam_entry_index].acct_name));
645 ent->sam_entry_index++;
646 goto tryagain;
649 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
650 (unsigned long)name_list[ent->sam_entry_index].rid));
652 /* Fill in group entry */
654 fill_domain_username(domain_group_name, ent->domain_name,
655 name_list[ent->sam_entry_index].acct_name);
657 result = fill_grent(&group_list[group_list_ndx],
658 ent->domain_name,
659 name_list[ent->sam_entry_index].acct_name,
660 group_gid);
662 /* Fill in group membership entry */
664 if (result) {
665 DOM_SID member_sid;
666 group_list[group_list_ndx].num_gr_mem = 0;
667 gr_mem = NULL;
668 gr_mem_len = 0;
670 /* Get group membership */
671 if (state->request.cmd == WINBINDD_GETGRLST) {
672 result = True;
673 } else {
674 sid_copy(&member_sid, &domain->sid);
675 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
676 result = fill_grent_mem(
677 domain,
678 &member_sid,
679 SID_NAME_DOM_GRP,
680 &group_list[group_list_ndx].num_gr_mem,
681 &gr_mem, &gr_mem_len);
685 if (result) {
686 /* Append to group membership list */
687 new_gr_mem_list = SMB_REALLOC( gr_mem_list, gr_mem_list_len + gr_mem_len);
689 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
690 DEBUG(0, ("out of memory\n"));
691 SAFE_FREE(gr_mem_list);
692 gr_mem_list_len = 0;
693 break;
696 DEBUG(10, ("list_len = %d, mem_len = %d\n",
697 gr_mem_list_len, gr_mem_len));
699 gr_mem_list = new_gr_mem_list;
701 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
702 gr_mem_len);
704 SAFE_FREE(gr_mem);
706 group_list[group_list_ndx].gr_mem_ofs =
707 gr_mem_list_len;
709 gr_mem_list_len += gr_mem_len;
712 ent->sam_entry_index++;
714 /* Add group to return list */
716 if (result) {
718 DEBUG(10, ("adding group num_entries = %d\n",
719 state->response.data.num_entries));
721 group_list_ndx++;
722 state->response.data.num_entries++;
724 state->response.length +=
725 sizeof(struct winbindd_gr);
727 } else {
728 DEBUG(0, ("could not lookup domain group %s\n",
729 domain_group_name));
733 /* Copy the list of group memberships to the end of the extra data */
735 if (group_list_ndx == 0)
736 goto done;
738 new_extra_data = SMB_REALLOC(
739 state->response.extra_data,
740 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
742 if (!new_extra_data) {
743 DEBUG(0, ("out of memory\n"));
744 group_list_ndx = 0;
745 SAFE_FREE(state->response.extra_data);
746 SAFE_FREE(gr_mem_list);
748 return WINBINDD_ERROR;
751 state->response.extra_data = new_extra_data;
753 memcpy(&((char *)state->response.extra_data)
754 [group_list_ndx * sizeof(struct winbindd_gr)],
755 gr_mem_list, gr_mem_list_len);
757 SAFE_FREE(gr_mem_list);
759 state->response.length += gr_mem_list_len;
761 DEBUG(10, ("returning %d groups, length = %d\n",
762 group_list_ndx, gr_mem_list_len));
764 /* Out of domains */
766 done:
768 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
771 /* List domain groups without mapping to unix ids */
773 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
775 uint32 total_entries = 0;
776 struct winbindd_domain *domain;
777 const char *which_domain;
778 char *extra_data = NULL;
779 char *ted = NULL;
780 unsigned int extra_data_len = 0, i;
782 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
784 /* Ensure null termination */
785 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
786 which_domain = state->request.domain_name;
788 /* Enumerate over trusted domains */
790 for (domain = domain_list(); domain; domain = domain->next) {
791 struct getent_state groups;
793 /* if we have a domain name restricting the request and this
794 one in the list doesn't match, then just bypass the remainder
795 of the loop */
797 if ( *which_domain && !strequal(which_domain, domain->name) )
798 continue;
800 ZERO_STRUCT(groups);
802 /* Get list of sam groups */
804 fstrcpy(groups.domain_name, domain->name);
806 get_sam_group_entries(&groups);
808 if (groups.num_sam_entries == 0) {
809 /* this domain is empty or in an error state */
810 continue;
813 /* keep track the of the total number of groups seen so
814 far over all domains */
815 total_entries += groups.num_sam_entries;
817 /* Allocate some memory for extra data. Note that we limit
818 account names to sizeof(fstring) = 128 characters. */
819 ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
821 if (!ted) {
822 DEBUG(0,("failed to enlarge buffer!\n"));
823 SAFE_FREE(extra_data);
824 return WINBINDD_ERROR;
825 } else
826 extra_data = ted;
828 /* Pack group list into extra data fields */
829 for (i = 0; i < groups.num_sam_entries; i++) {
830 char *group_name = ((struct acct_info *)
831 groups.sam_entries)[i].acct_name;
832 fstring name;
834 fill_domain_username(name, domain->name, group_name);
835 /* Append to extra data */
836 memcpy(&extra_data[extra_data_len], name,
837 strlen(name));
838 extra_data_len += strlen(name);
839 extra_data[extra_data_len++] = ',';
842 SAFE_FREE(groups.sam_entries);
845 /* Assign extra_data fields in response structure */
846 if (extra_data) {
847 extra_data[extra_data_len - 1] = '\0';
848 state->response.extra_data = extra_data;
849 state->response.length += extra_data_len;
852 /* No domains may have responded but that's still OK so don't
853 return an error. */
855 return WINBINDD_OK;
858 /* Get user supplementary groups. This is much quicker than trying to
859 invert the groups database. We merge the groups from the gids and
860 other_sids info3 fields as trusted domain, universal group
861 memberships, and nested groups (win2k native mode only) are not
862 returned by the getgroups RPC call but are present in the info3. */
864 struct getgroups_state {
865 struct winbindd_cli_state *state;
866 struct winbindd_domain *domain;
867 char *domname;
868 char *username;
869 DOM_SID user_sid;
871 const DOM_SID *token_sids;
872 int i, num_token_sids;
874 gid_t *token_gids;
875 int num_token_gids;
878 static void getgroups_usersid_recv(void *private, BOOL success,
879 const DOM_SID *sid, enum SID_NAME_USE type);
880 static void getgroups_tokensids_recv(void *private, BOOL success,
881 DOM_SID *token_sids, int num_token_sids);
882 static void getgroups_sid2gid_recv(void *private, BOOL success, gid_t gid);
884 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
886 struct getgroups_state *s;
888 /* Ensure null termination */
889 state->request.data.username
890 [sizeof(state->request.data.username)-1]='\0';
892 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
893 state->request.data.username));
895 /* Parse domain and username */
897 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
898 if (s == NULL) {
899 DEBUG(0, ("talloc failed\n"));
900 return WINBINDD_ERROR;
903 s->state = state;
905 if (!parse_domain_user_talloc(state->mem_ctx,
906 state->request.data.username,
907 &s->domname, &s->username)) {
908 DEBUG(0, ("Could not parse domain user: %s\n",
909 state->request.data.username));
910 return WINBINDD_ERROR;
913 /* Get info for the domain */
915 s->domain = find_domain_from_name_noinit(s->domname);
917 if (s->domain == NULL) {
918 DEBUG(7, ("could not find domain entry for domain %s\n",
919 s->domname));
920 return WINBINDD_ERROR;
923 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
924 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting "
925 "getgroups() for %s\\%s.\n", s->domname,
926 s->username));
927 return WINBINDD_ERROR;
930 /* Get rid and name type from name. The following costs 1 packet */
932 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
933 getgroups_usersid_recv, s);
934 return WINBINDD_PENDING;
937 static void getgroups_usersid_recv(void *private, BOOL success,
938 const DOM_SID *sid, enum SID_NAME_USE type)
940 struct getgroups_state *s = private;
942 if ((!success) ||
943 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
944 s->state->response.result = WINBINDD_ERROR;
945 request_finished(s->state);
946 return;
949 sid_copy(&s->user_sid, sid);
951 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
952 getgroups_tokensids_recv, s);
955 static void getgroups_tokensids_recv(void *private, BOOL success,
956 DOM_SID *token_sids, int num_token_sids)
958 struct getgroups_state *s = private;
960 /* We need at least the user sid and the primary group in the token,
961 * otherwise it's an error */
963 if ((!success) || (num_token_sids < 2)) {
964 s->state->response.result = WINBINDD_ERROR;
965 request_finished(s->state);
966 return;
969 s->token_sids = token_sids;
970 s->num_token_sids = num_token_sids;
971 s->i = 0;
973 s->token_gids = NULL;
974 s->num_token_gids = 0;
976 getgroups_sid2gid_recv(s, False, 0);
979 static void getgroups_sid2gid_recv(void *private, BOOL success, gid_t gid)
981 struct getgroups_state *s = private;
983 if (success)
984 add_gid_to_array_unique(NULL, gid,
985 &s->token_gids,
986 &s->num_token_gids);
988 if (s->i < s->num_token_sids) {
989 const DOM_SID *sid = &s->token_sids[s->i];
990 s->i += 1;
992 if (sid_equal(sid, &s->user_sid)) {
993 getgroups_sid2gid_recv(s, False, 0);
994 return;
997 winbindd_sid2gid_async(s->state->mem_ctx, sid,
998 getgroups_sid2gid_recv, s);
999 return;
1002 s->state->response.data.num_entries = s->num_token_gids;
1003 s->state->response.extra_data = s->token_gids;
1004 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1005 s->state->response.result = WINBINDD_OK;
1006 request_finished(s->state);
1009 /* Get user supplementary sids. This is equivalent to the
1010 winbindd_getgroups() function but it involves a SID->SIDs mapping
1011 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1012 idmap. This call is designed to be used with applications that need
1013 to do ACL evaluation themselves. Note that the cached info3 data is
1014 not used
1016 this function assumes that the SID that comes in is a user SID. If
1017 you pass in another type of SID then you may get unpredictable
1018 results.
1021 static void getusersids_recv(void *private, BOOL success, DOM_SID *sids,
1022 int num_sids);
1024 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1026 DOM_SID *user_sid;
1028 /* Ensure null termination */
1029 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1031 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1032 if (user_sid == NULL) {
1033 DEBUG(1, ("talloc failed\n"));
1034 return WINBINDD_ERROR;
1037 if (!string_to_sid(user_sid, state->request.data.sid)) {
1038 DEBUG(1, ("Could not get convert sid %s from string\n",
1039 state->request.data.sid));
1040 return WINBINDD_ERROR;
1043 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1044 state);
1045 return WINBINDD_PENDING;
1048 static void getusersids_recv(void *private, BOOL success, DOM_SID *sids,
1049 int num_sids)
1051 struct winbindd_cli_state *state = private;
1052 char *ret = NULL;
1053 unsigned ofs, ret_size = 0;
1054 int i;
1056 state->response.result = WINBINDD_ERROR;
1058 if (!success)
1059 goto done;
1061 /* work out the response size */
1062 for (i = 0; i < num_sids; i++) {
1063 const char *s = sid_string_static(&sids[i]);
1064 ret_size += strlen(s) + 1;
1067 /* build the reply */
1068 ret = SMB_MALLOC(ret_size);
1069 if (!ret) goto done;
1070 ofs = 0;
1071 for (i = 0; i < num_sids; i++) {
1072 const char *s = sid_string_static(&sids[i]);
1073 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1074 ofs += strlen(ret+ofs) + 1;
1077 /* Send data back to client */
1078 state->response.data.num_entries = num_sids;
1079 state->response.extra_data = ret;
1080 state->response.length += ret_size;
1081 state->response.result = WINBINDD_OK;
1083 done:
1084 request_finished(state);
1087 enum winbindd_result winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1089 DOM_SID user_sid;
1090 struct winbindd_domain *domain;
1092 /* Ensure null termination */
1093 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1095 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1096 DEBUG(1, ("Could not get convert sid %s from string\n",
1097 state->request.data.sid));
1098 return WINBINDD_ERROR;
1101 /* Get info for the domain */
1102 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1103 DEBUG(0,("could not find domain entry for sid %s\n",
1104 sid_string_static(&user_sid)));
1105 return WINBINDD_ERROR;
1108 async_domain_request(state->mem_ctx, domain, &state->request,
1109 &state->response, request_finished_cont, state);
1110 return WINBINDD_PENDING;
1113 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1114 struct winbindd_cli_state *state)
1116 DOM_SID user_sid;
1117 NTSTATUS status;
1119 int i, num_groups, len, bufsize;
1120 DOM_SID *groups;
1122 /* Ensure null termination */
1123 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1125 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1126 DEBUG(1, ("Could not get convert sid %s from string\n",
1127 state->request.data.sid));
1128 return WINBINDD_ERROR;
1131 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1132 &user_sid, &num_groups,
1133 &groups);
1134 if (!NT_STATUS_IS_OK(status))
1135 return WINBINDD_ERROR;
1137 if (num_groups == 0) {
1138 state->response.data.num_entries = 0;
1139 state->response.extra_data = NULL;
1140 return WINBINDD_OK;
1143 len=bufsize=0;
1144 state->response.extra_data = NULL;
1146 for (i=0; i<num_groups; i++) {
1147 sprintf_append(NULL, (char **)&state->response.extra_data,
1148 &len, &bufsize,
1149 "%s\n", sid_string_static(&groups[i]));
1152 if (state->response.extra_data == NULL) {
1153 /* Hmmm. Allocation failed somewhere */
1154 return WINBINDD_ERROR;
1157 state->response.data.num_entries = num_groups;
1158 state->response.length += len+1;
1160 return WINBINDD_OK;