r10400: commit merge patch from jra
[Samba.git] / source / nsswitch / winbindd_group.c
blob6261dfb6165c6df1af0ddf3551bc449fba40a146
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 void 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 request_error(state);
239 return;
241 /* should we deal with users for our domain? */
243 if ( lp_winbind_trusted_domains_only() && domain->primary) {
244 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
245 name_domain, name_group));
246 request_error(state);
247 return;
250 /* Get rid and name type from name */
252 if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name,
253 name_group, &group_sid, &name_type)) {
254 DEBUG(1, ("group %s in domain %s does not exist\n",
255 name_group, name_domain));
256 request_error(state);
257 return;
260 if ( !((name_type==SID_NAME_DOM_GRP) ||
261 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
262 ((name_type==SID_NAME_ALIAS) && domain->internal) ||
263 ((name_type==SID_NAME_WKN_GRP) && domain->internal)) )
265 DEBUG(1, ("name '%s' is not a local, domain or builtin group: %d\n",
266 name_group, name_type));
267 request_error(state);
268 return;
271 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
272 DEBUG(1, ("error converting unix gid to sid\n"));
273 request_error(state);
274 return;
277 if (!fill_grent(&state->response.data.gr, name_domain,
278 name_group, gid) ||
279 !fill_grent_mem(domain, &group_sid, name_type,
280 &state->response.data.gr.num_gr_mem,
281 &gr_mem, &gr_mem_len)) {
282 request_error(state);
283 return;
286 /* Group membership lives at start of extra data */
288 state->response.data.gr.gr_mem_ofs = 0;
290 state->response.length += gr_mem_len;
291 state->response.extra_data = gr_mem;
292 request_ok(state);
295 /* Return a group structure from a gid number */
297 void winbindd_getgrgid(struct winbindd_cli_state *state)
299 struct winbindd_domain *domain;
300 DOM_SID group_sid;
301 enum SID_NAME_USE name_type;
302 fstring dom_name;
303 fstring group_name;
304 int gr_mem_len;
305 char *gr_mem;
307 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
308 (unsigned long)state->request.data.gid));
310 /* Bug out if the gid isn't in the winbind range */
312 if ((state->request.data.gid < server_state.gid_low) ||
313 (state->request.data.gid > server_state.gid_high)) {
314 request_error(state);
315 return;
318 /* Get rid from gid */
319 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid, 0))) {
320 DEBUG(1, ("could not convert gid %lu to rid\n",
321 (unsigned long)state->request.data.gid));
322 request_error(state);
323 return;
326 /* Get name from sid */
328 if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name,
329 group_name, &name_type)) {
330 DEBUG(1, ("could not lookup sid\n"));
331 request_error(state);
332 return;
335 /* Fill in group structure */
337 domain = find_domain_from_sid_noinit(&group_sid);
339 if (!domain) {
340 DEBUG(1,("Can't find domain from sid\n"));
341 request_error(state);
342 return;
345 if ( !((name_type==SID_NAME_DOM_GRP) ||
346 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
347 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
349 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
350 group_name, name_type));
351 request_error(state);
352 return;
355 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
356 state->request.data.gid) ||
357 !fill_grent_mem(domain, &group_sid, name_type,
358 &state->response.data.gr.num_gr_mem,
359 &gr_mem, &gr_mem_len)) {
360 request_error(state);
361 return;
364 /* Group membership lives at start of extra data */
366 state->response.data.gr.gr_mem_ofs = 0;
368 state->response.length += gr_mem_len;
369 state->response.extra_data = gr_mem;
370 request_ok(state);
374 * set/get/endgrent functions
377 /* "Rewind" file pointer for group database enumeration */
379 static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state)
381 struct winbindd_domain *domain;
383 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
385 /* Check user has enabled this */
387 if (!lp_winbind_enum_groups()) {
388 return False;
391 /* Free old static data if it exists */
393 if (state->getgrent_state != NULL) {
394 free_getent_state(state->getgrent_state);
395 state->getgrent_state = NULL;
398 /* Create sam pipes for each domain we know about */
400 for (domain = domain_list(); domain != NULL; domain = domain->next) {
401 struct getent_state *domain_state;
403 /* Create a state record for this domain */
405 /* don't add our domaina if we are a PDC or if we
406 are a member of a Samba domain */
408 if ( lp_winbind_trusted_domains_only() && domain->primary )
410 continue;
414 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
415 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
416 return False;
419 ZERO_STRUCTP(domain_state);
421 fstrcpy(domain_state->domain_name, domain->name);
423 /* Add to list of open domains */
425 DLIST_ADD(state->getgrent_state, domain_state);
428 state->getgrent_initialized = True;
429 return True;
432 void winbindd_setgrent(struct winbindd_cli_state *state)
434 if (winbindd_setgrent_internal(state)) {
435 request_ok(state);
436 } else {
437 request_error(state);
441 /* Close file pointer to ntdom group database */
443 void winbindd_endgrent(struct winbindd_cli_state *state)
445 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
447 free_getent_state(state->getgrent_state);
448 state->getgrent_initialized = False;
449 state->getgrent_state = NULL;
450 request_ok(state);
453 /* Get the list of domain groups and domain aliases for a domain. We fill in
454 the sam_entries and num_sam_entries fields with domain group information.
455 The dispinfo_ndx field is incremented to the index of the next group to
456 fetch. Return True if some groups were returned, False otherwise. */
458 static BOOL get_sam_group_entries(struct getent_state *ent)
460 NTSTATUS status;
461 uint32 num_entries;
462 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
463 TALLOC_CTX *mem_ctx;
464 BOOL result = False;
465 struct acct_info *sam_grp_entries = NULL;
466 struct winbindd_domain *domain;
468 if (ent->got_sam_entries)
469 return False;
471 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
472 ent->domain_name))) {
473 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
474 return False;
477 /* Free any existing group info */
479 SAFE_FREE(ent->sam_entries);
480 ent->num_sam_entries = 0;
481 ent->got_sam_entries = True;
483 /* Enumerate domain groups */
485 num_entries = 0;
487 if (!(domain = find_domain_from_name(ent->domain_name))) {
488 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
489 goto done;
492 /* always get the domain global groups */
494 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
496 if (!NT_STATUS_IS_OK(status)) {
497 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
498 result = False;
499 goto done;
502 /* Copy entries into return buffer */
504 if (num_entries) {
505 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
506 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
507 num_entries));
508 result = False;
509 goto done;
511 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
514 ent->num_sam_entries = num_entries;
516 /* get the domain local groups if we are a member of a native win2k domain
517 and are not using LDAP to get the groups */
519 if ( ( lp_security() != SEC_ADS && domain->native_mode
520 && domain->primary) || domain->internal )
522 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
523 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
525 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
527 if ( !NT_STATUS_IS_OK(status) ) {
528 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
529 num_entries = 0;
531 else
532 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
534 /* Copy entries into return buffer */
536 if ( num_entries ) {
537 if ( !(tmp_name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
539 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
540 num_entries));
541 result = False;
542 SAFE_FREE( name_list );
543 goto done;
546 name_list = tmp_name_list;
548 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
549 num_entries * sizeof(struct acct_info) );
552 ent->num_sam_entries += num_entries;
556 /* Fill in remaining fields */
558 ent->sam_entries = name_list;
559 ent->sam_entry_index = 0;
561 result = (ent->num_sam_entries > 0);
563 done:
564 talloc_destroy(mem_ctx);
566 return result;
569 /* Fetch next group entry from ntdom database */
571 #define MAX_GETGRENT_GROUPS 500
573 void winbindd_getgrent(struct winbindd_cli_state *state)
575 struct getent_state *ent;
576 struct winbindd_gr *group_list = NULL;
577 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
578 char *new_extra_data, *gr_mem_list = NULL;
580 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
582 /* Check user has enabled this */
584 if (!lp_winbind_enum_groups()) {
585 request_error(state);
586 return;
589 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
591 if ((state->response.extra_data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
592 request_error(state);
593 return;
596 memset(state->response.extra_data, '\0',
597 num_groups * sizeof(struct winbindd_gr) );
599 state->response.data.num_entries = 0;
601 group_list = (struct winbindd_gr *)state->response.extra_data;
603 if (!state->getgrent_initialized)
604 winbindd_setgrent_internal(state);
606 if (!(ent = state->getgrent_state)) {
607 request_error(state);
608 return;
611 /* Start sending back groups */
613 for (i = 0; i < num_groups; i++) {
614 struct acct_info *name_list = NULL;
615 fstring domain_group_name;
616 uint32 result;
617 gid_t group_gid;
618 int gr_mem_len;
619 char *gr_mem, *new_gr_mem_list;
620 DOM_SID group_sid;
621 struct winbindd_domain *domain;
623 /* Do we need to fetch another chunk of groups? */
625 tryagain:
627 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
628 ent->sam_entry_index, ent->num_sam_entries));
630 if (ent->num_sam_entries == ent->sam_entry_index) {
632 while(ent && !get_sam_group_entries(ent)) {
633 struct getent_state *next_ent;
635 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
637 /* Free state information for this domain */
639 SAFE_FREE(ent->sam_entries);
641 next_ent = ent->next;
642 DLIST_REMOVE(state->getgrent_state, ent);
644 SAFE_FREE(ent);
645 ent = next_ent;
648 /* No more domains */
650 if (!ent)
651 break;
654 name_list = ent->sam_entries;
656 if (!(domain =
657 find_domain_from_name(ent->domain_name))) {
658 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
659 result = False;
660 goto done;
663 /* Lookup group info */
665 sid_copy(&group_sid, &domain->sid);
666 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
668 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
670 DEBUG(1, ("could not look up gid for group %s\n",
671 name_list[ent->sam_entry_index].acct_name));
673 ent->sam_entry_index++;
674 goto tryagain;
677 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
678 (unsigned long)name_list[ent->sam_entry_index].rid));
680 /* Fill in group entry */
682 fill_domain_username(domain_group_name, ent->domain_name,
683 name_list[ent->sam_entry_index].acct_name);
685 result = fill_grent(&group_list[group_list_ndx],
686 ent->domain_name,
687 name_list[ent->sam_entry_index].acct_name,
688 group_gid);
690 /* Fill in group membership entry */
692 if (result) {
693 DOM_SID member_sid;
694 group_list[group_list_ndx].num_gr_mem = 0;
695 gr_mem = NULL;
696 gr_mem_len = 0;
698 /* Get group membership */
699 if (state->request.cmd == WINBINDD_GETGRLST) {
700 result = True;
701 } else {
702 sid_copy(&member_sid, &domain->sid);
703 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
704 result = fill_grent_mem(
705 domain,
706 &member_sid,
707 SID_NAME_DOM_GRP,
708 &group_list[group_list_ndx].num_gr_mem,
709 &gr_mem, &gr_mem_len);
713 if (result) {
714 /* Append to group membership list */
715 new_gr_mem_list = SMB_REALLOC( gr_mem_list, gr_mem_list_len + gr_mem_len);
717 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
718 DEBUG(0, ("out of memory\n"));
719 SAFE_FREE(gr_mem_list);
720 gr_mem_list_len = 0;
721 break;
724 DEBUG(10, ("list_len = %d, mem_len = %d\n",
725 gr_mem_list_len, gr_mem_len));
727 gr_mem_list = new_gr_mem_list;
729 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
730 gr_mem_len);
732 SAFE_FREE(gr_mem);
734 group_list[group_list_ndx].gr_mem_ofs =
735 gr_mem_list_len;
737 gr_mem_list_len += gr_mem_len;
740 ent->sam_entry_index++;
742 /* Add group to return list */
744 if (result) {
746 DEBUG(10, ("adding group num_entries = %d\n",
747 state->response.data.num_entries));
749 group_list_ndx++;
750 state->response.data.num_entries++;
752 state->response.length +=
753 sizeof(struct winbindd_gr);
755 } else {
756 DEBUG(0, ("could not lookup domain group %s\n",
757 domain_group_name));
761 /* Copy the list of group memberships to the end of the extra data */
763 if (group_list_ndx == 0)
764 goto done;
766 new_extra_data = SMB_REALLOC(
767 state->response.extra_data,
768 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
770 if (!new_extra_data) {
771 DEBUG(0, ("out of memory\n"));
772 group_list_ndx = 0;
773 SAFE_FREE(state->response.extra_data);
774 SAFE_FREE(gr_mem_list);
775 request_error(state);
776 return;
779 state->response.extra_data = new_extra_data;
781 memcpy(&((char *)state->response.extra_data)
782 [group_list_ndx * sizeof(struct winbindd_gr)],
783 gr_mem_list, gr_mem_list_len);
785 SAFE_FREE(gr_mem_list);
787 state->response.length += gr_mem_list_len;
789 DEBUG(10, ("returning %d groups, length = %d\n",
790 group_list_ndx, gr_mem_list_len));
792 /* Out of domains */
794 done:
796 if (group_list_ndx > 0)
797 request_ok(state);
798 else
799 request_error(state);
802 /* List domain groups without mapping to unix ids */
804 void winbindd_list_groups(struct winbindd_cli_state *state)
806 uint32 total_entries = 0;
807 struct winbindd_domain *domain;
808 const char *which_domain;
809 char *extra_data = NULL;
810 char *ted = NULL;
811 unsigned int extra_data_len = 0, i;
813 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
815 /* Ensure null termination */
816 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
817 which_domain = state->request.domain_name;
819 /* Enumerate over trusted domains */
821 for (domain = domain_list(); domain; domain = domain->next) {
822 struct getent_state groups;
824 /* if we have a domain name restricting the request and this
825 one in the list doesn't match, then just bypass the remainder
826 of the loop */
828 if ( *which_domain && !strequal(which_domain, domain->name) )
829 continue;
831 ZERO_STRUCT(groups);
833 /* Get list of sam groups */
835 fstrcpy(groups.domain_name, domain->name);
837 get_sam_group_entries(&groups);
839 if (groups.num_sam_entries == 0) {
840 /* this domain is empty or in an error state */
841 continue;
844 /* keep track the of the total number of groups seen so
845 far over all domains */
846 total_entries += groups.num_sam_entries;
848 /* Allocate some memory for extra data. Note that we limit
849 account names to sizeof(fstring) = 128 characters. */
850 ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
852 if (!ted) {
853 DEBUG(0,("failed to enlarge buffer!\n"));
854 SAFE_FREE(extra_data);
855 request_error(state);
856 return;
857 } else
858 extra_data = ted;
860 /* Pack group list into extra data fields */
861 for (i = 0; i < groups.num_sam_entries; i++) {
862 char *group_name = ((struct acct_info *)
863 groups.sam_entries)[i].acct_name;
864 fstring name;
866 fill_domain_username(name, domain->name, group_name);
867 /* Append to extra data */
868 memcpy(&extra_data[extra_data_len], name,
869 strlen(name));
870 extra_data_len += strlen(name);
871 extra_data[extra_data_len++] = ',';
874 SAFE_FREE(groups.sam_entries);
877 /* Assign extra_data fields in response structure */
878 if (extra_data) {
879 extra_data[extra_data_len - 1] = '\0';
880 state->response.extra_data = extra_data;
881 state->response.length += extra_data_len;
884 /* No domains may have responded but that's still OK so don't
885 return an error. */
887 request_ok(state);
890 /* Get user supplementary groups. This is much quicker than trying to
891 invert the groups database. We merge the groups from the gids and
892 other_sids info3 fields as trusted domain, universal group
893 memberships, and nested groups (win2k native mode only) are not
894 returned by the getgroups RPC call but are present in the info3. */
896 struct getgroups_state {
897 struct winbindd_cli_state *state;
898 struct winbindd_domain *domain;
899 char *domname;
900 char *username;
901 DOM_SID user_sid;
903 const DOM_SID *token_sids;
904 int i, num_token_sids;
906 gid_t *token_gids;
907 int num_token_gids;
910 static void getgroups_usersid_recv(void *private_data, BOOL success,
911 const DOM_SID *sid, enum SID_NAME_USE type);
912 static void getgroups_tokensids_recv(void *private_data, BOOL success,
913 DOM_SID *token_sids, int num_token_sids);
914 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
916 void winbindd_getgroups(struct winbindd_cli_state *state)
918 struct getgroups_state *s;
920 /* Ensure null termination */
921 state->request.data.username
922 [sizeof(state->request.data.username)-1]='\0';
924 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
925 state->request.data.username));
927 /* Parse domain and username */
929 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
930 if (s == NULL) {
931 DEBUG(0, ("talloc failed\n"));
932 request_error(state);
933 return;
936 s->state = state;
938 if (!parse_domain_user_talloc(state->mem_ctx,
939 state->request.data.username,
940 &s->domname, &s->username)) {
941 DEBUG(0, ("Could not parse domain user: %s\n",
942 state->request.data.username));
943 request_error(state);
944 return;
947 /* Get info for the domain */
949 s->domain = find_domain_from_name_noinit(s->domname);
951 if (s->domain == NULL) {
952 DEBUG(7, ("could not find domain entry for domain %s\n",
953 s->domname));
954 request_error(state);
955 return;
958 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
959 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting "
960 "getgroups() for %s\\%s.\n", s->domname,
961 s->username));
962 request_error(state);
963 return;
966 /* Get rid and name type from name. The following costs 1 packet */
968 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
969 getgroups_usersid_recv, s);
972 static void getgroups_usersid_recv(void *private_data, BOOL success,
973 const DOM_SID *sid, enum SID_NAME_USE type)
975 struct getgroups_state *s = private_data;
977 if ((!success) ||
978 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
979 request_error(s->state);
980 return;
983 sid_copy(&s->user_sid, sid);
985 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
986 getgroups_tokensids_recv, s);
989 static void getgroups_tokensids_recv(void *private_data, BOOL success,
990 DOM_SID *token_sids, int num_token_sids)
992 struct getgroups_state *s = private_data;
994 /* We need at least the user sid and the primary group in the token,
995 * otherwise it's an error */
997 if ((!success) || (num_token_sids < 2)) {
998 request_error(s->state);
999 return;
1002 s->token_sids = token_sids;
1003 s->num_token_sids = num_token_sids;
1004 s->i = 0;
1006 s->token_gids = NULL;
1007 s->num_token_gids = 0;
1009 getgroups_sid2gid_recv(s, False, 0);
1012 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
1014 struct getgroups_state *s = private_data;
1016 if (success)
1017 add_gid_to_array_unique(NULL, gid,
1018 &s->token_gids,
1019 &s->num_token_gids);
1021 if (s->i < s->num_token_sids) {
1022 const DOM_SID *sid = &s->token_sids[s->i];
1023 s->i += 1;
1025 if (sid_equal(sid, &s->user_sid)) {
1026 getgroups_sid2gid_recv(s, False, 0);
1027 return;
1030 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1031 getgroups_sid2gid_recv, s);
1032 return;
1035 s->state->response.data.num_entries = s->num_token_gids;
1036 s->state->response.extra_data = s->token_gids;
1037 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1038 request_ok(s->state);
1041 /* Get user supplementary sids. This is equivalent to the
1042 winbindd_getgroups() function but it involves a SID->SIDs mapping
1043 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1044 idmap. This call is designed to be used with applications that need
1045 to do ACL evaluation themselves. Note that the cached info3 data is
1046 not used
1048 this function assumes that the SID that comes in is a user SID. If
1049 you pass in another type of SID then you may get unpredictable
1050 results.
1053 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1054 int num_sids);
1056 void winbindd_getusersids(struct winbindd_cli_state *state)
1058 DOM_SID *user_sid;
1060 /* Ensure null termination */
1061 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1063 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1064 if (user_sid == NULL) {
1065 DEBUG(1, ("talloc failed\n"));
1066 request_error(state);
1067 return;
1070 if (!string_to_sid(user_sid, state->request.data.sid)) {
1071 DEBUG(1, ("Could not get convert sid %s from string\n",
1072 state->request.data.sid));
1073 request_error(state);
1074 return;
1077 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1078 state);
1081 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1082 int num_sids)
1084 struct winbindd_cli_state *state = private_data;
1085 char *ret = NULL;
1086 unsigned ofs, ret_size = 0;
1087 int i;
1089 if (!success) {
1090 request_error(state);
1091 return;
1094 /* work out the response size */
1095 for (i = 0; i < num_sids; i++) {
1096 const char *s = sid_string_static(&sids[i]);
1097 ret_size += strlen(s) + 1;
1100 /* build the reply */
1101 ret = SMB_MALLOC(ret_size);
1102 if (!ret) {
1103 DEBUG(0, ("malloc failed\n"));
1104 request_error(state);
1105 return;
1107 ofs = 0;
1108 for (i = 0; i < num_sids; i++) {
1109 const char *s = sid_string_static(&sids[i]);
1110 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1111 ofs += strlen(ret+ofs) + 1;
1114 /* Send data back to client */
1115 state->response.data.num_entries = num_sids;
1116 state->response.extra_data = ret;
1117 state->response.length += ret_size;
1118 request_ok(state);
1121 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1123 DOM_SID user_sid;
1124 struct winbindd_domain *domain;
1126 /* Ensure null termination */
1127 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1129 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1130 DEBUG(1, ("Could not get convert sid %s from string\n",
1131 state->request.data.sid));
1132 request_error(state);
1133 return;
1136 /* Get info for the domain */
1137 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1138 DEBUG(0,("could not find domain entry for sid %s\n",
1139 sid_string_static(&user_sid)));
1140 request_error(state);
1141 return;
1144 sendto_domain(state, domain);
1147 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1148 struct winbindd_cli_state *state)
1150 DOM_SID user_sid;
1151 NTSTATUS status;
1153 int i, num_groups;
1154 size_t bufsize;
1155 ssize_t len;
1156 DOM_SID *groups;
1158 /* Ensure null termination */
1159 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1161 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1162 DEBUG(1, ("Could not get convert sid %s from string\n",
1163 state->request.data.sid));
1164 return WINBINDD_ERROR;
1167 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1168 &user_sid, &num_groups,
1169 &groups);
1170 if (!NT_STATUS_IS_OK(status))
1171 return WINBINDD_ERROR;
1173 if (num_groups == 0) {
1174 state->response.data.num_entries = 0;
1175 state->response.extra_data = NULL;
1176 return WINBINDD_OK;
1179 len=bufsize=0;
1180 state->response.extra_data = NULL;
1182 for (i=0; i<num_groups; i++) {
1183 sprintf_append(NULL, (char **)&state->response.extra_data,
1184 &len, &bufsize,
1185 "%s\n", sid_string_static(&groups[i]));
1188 if (state->response.extra_data == NULL) {
1189 /* Hmmm. Allocation failed somewhere */
1190 return WINBINDD_ERROR;
1193 state->response.data.num_entries = num_groups;
1194 state->response.length += len+1;
1196 return WINBINDD_OK;