r17727: Start pulling in changes for 3.0.23c
[Samba/gbeck.git] / source / nsswitch / winbindd_group.c
blob5528b6c78e4dc208a95e7cb974e1b2300d499083
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;
45 fill_domain_username( full_group_name, dom_name, gr_name, True );
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 size_t *num_gr_mem, char **gr_mem, size_t *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 (%s)\n", the_name, sid_type_lookup(name_types[i])));
144 continue;
147 /* Append domain name */
149 fill_domain_username(name, domain->name, the_name, True);
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 = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
188 (unsigned int)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, tmp_sid;
205 uint32 grp_rid;
206 struct winbindd_domain *domain;
207 enum SID_NAME_USE name_type;
208 fstring name_domain, name_group;
209 char *tmp, *gr_mem;
210 size_t gr_mem_len;
211 size_t num_gr_mem;
212 gid_t gid;
213 union unid_t id;
214 NTSTATUS status;
216 /* Ensure null termination */
217 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
219 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
220 state->request.data.groupname));
222 /* Parse domain and groupname */
224 memset(name_group, 0, sizeof(fstring));
226 tmp = state->request.data.groupname;
228 parse_domain_user(tmp, name_domain, name_group);
230 /* if no domain or our local domain and no local tdb group, default to
231 * our local domain for aliases */
233 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
234 fstrcpy(name_domain, get_global_sam_name());
237 /* Get info for the domain */
239 if ((domain = find_domain_from_name(name_domain)) == NULL) {
240 DEBUG(3, ("could not get domain sid for domain %s\n",
241 name_domain));
242 request_error(state);
243 return;
245 /* should we deal with users for our domain? */
247 if ( lp_winbind_trusted_domains_only() && domain->primary) {
248 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
249 "getgrnam() for %s\\%s.\n", name_domain, name_group));
250 request_error(state);
251 return;
254 /* Get rid and name type from name */
256 if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name,
257 name_group, &group_sid, &name_type)) {
258 DEBUG(1, ("group %s in domain %s does not exist\n",
259 name_group, name_domain));
260 request_error(state);
261 return;
264 if ( !((name_type==SID_NAME_DOM_GRP) ||
265 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
266 ((name_type==SID_NAME_ALIAS) && domain->internal) ||
267 ((name_type==SID_NAME_WKN_GRP) && domain->internal)) )
269 DEBUG(1, ("name '%s' is not a local, domain or builtin "
270 "group: %d\n", name_group, name_type));
271 request_error(state);
272 return;
275 /* Make sure that the group SID is within the domain of the
276 original domain */
278 sid_copy( &tmp_sid, &group_sid );
279 sid_split_rid( &tmp_sid, &grp_rid );
280 if ( !sid_equal( &tmp_sid, &domain->sid ) ) {
281 DEBUG(3,("winbindd_getgrnam: group %s resolves to a SID in the wrong domain [%s]\n",
282 state->request.data.groupname, sid_string_static(&group_sid)));
283 request_error(state);
284 return;
289 /* Try to get the GID */
291 status = idmap_sid_to_gid(&group_sid, &gid, 0);
293 if (NT_STATUS_IS_OK(status)) {
294 goto got_gid;
297 /* Maybe it's one of our aliases in passdb */
299 if (pdb_sid_to_id(&group_sid, &id, &name_type) &&
300 ((name_type == SID_NAME_ALIAS) ||
301 (name_type == SID_NAME_WKN_GRP))) {
302 gid = id.gid;
303 goto got_gid;
306 DEBUG(1, ("error converting unix gid to sid\n"));
307 request_error(state);
308 return;
310 got_gid:
312 if (!fill_grent(&state->response.data.gr, name_domain,
313 name_group, gid) ||
314 !fill_grent_mem(domain, &group_sid, name_type,
315 &num_gr_mem,
316 &gr_mem, &gr_mem_len)) {
317 request_error(state);
318 return;
321 state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
323 /* Group membership lives at start of extra data */
325 state->response.data.gr.gr_mem_ofs = 0;
327 state->response.length += gr_mem_len;
328 state->response.extra_data.data = gr_mem;
329 request_ok(state);
332 /* Return a group structure from a gid number */
334 void winbindd_getgrgid(struct winbindd_cli_state *state)
336 struct winbindd_domain *domain;
337 DOM_SID group_sid;
338 enum SID_NAME_USE name_type;
339 fstring dom_name;
340 fstring group_name;
341 size_t gr_mem_len;
342 size_t num_gr_mem;
343 char *gr_mem;
344 NTSTATUS status;
346 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
347 (unsigned long)state->request.data.gid));
349 /* Bug out if the gid isn't in the winbind range */
351 if ((state->request.data.gid < server_state.gid_low) ||
352 (state->request.data.gid > server_state.gid_high)) {
353 request_error(state);
354 return;
357 /* Get sid from gid */
359 status = idmap_gid_to_sid(&group_sid, state->request.data.gid, 0);
360 if (NT_STATUS_IS_OK(status)) {
361 /* This is a remote one */
362 goto got_sid;
365 /* Ok, this might be "ours", i.e. an alias */
367 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
368 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
369 (name_type == SID_NAME_ALIAS)) {
370 /* Hey, got an alias */
371 goto got_sid;
374 DEBUG(1, ("could not convert gid %lu to sid\n",
375 (unsigned long)state->request.data.gid));
376 request_error(state);
377 return;
379 got_sid:
380 /* Get name from sid */
382 if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name,
383 group_name, &name_type)) {
384 DEBUG(1, ("could not lookup sid\n"));
385 request_error(state);
386 return;
389 /* Fill in group structure */
391 domain = find_domain_from_sid_noinit(&group_sid);
393 if (!domain) {
394 DEBUG(1,("Can't find domain from sid\n"));
395 request_error(state);
396 return;
399 if ( !((name_type==SID_NAME_DOM_GRP) ||
400 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
401 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
403 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
404 group_name, name_type));
405 request_error(state);
406 return;
409 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
410 state->request.data.gid) ||
411 !fill_grent_mem(domain, &group_sid, name_type,
412 &num_gr_mem,
413 &gr_mem, &gr_mem_len)) {
414 request_error(state);
415 return;
418 state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
420 /* Group membership lives at start of extra data */
422 state->response.data.gr.gr_mem_ofs = 0;
424 state->response.length += gr_mem_len;
425 state->response.extra_data.data = gr_mem;
426 request_ok(state);
430 * set/get/endgrent functions
433 /* "Rewind" file pointer for group database enumeration */
435 static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state)
437 struct winbindd_domain *domain;
439 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
441 /* Check user has enabled this */
443 if (!lp_winbind_enum_groups()) {
444 return False;
447 /* Free old static data if it exists */
449 if (state->getgrent_state != NULL) {
450 free_getent_state(state->getgrent_state);
451 state->getgrent_state = NULL;
454 /* Create sam pipes for each domain we know about */
456 for (domain = domain_list(); domain != NULL; domain = domain->next) {
457 struct getent_state *domain_state;
459 /* Create a state record for this domain */
461 /* don't add our domaina if we are a PDC or if we
462 are a member of a Samba domain */
464 if ( lp_winbind_trusted_domains_only() && domain->primary )
466 continue;
470 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
471 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
472 return False;
475 ZERO_STRUCTP(domain_state);
477 fstrcpy(domain_state->domain_name, domain->name);
479 /* Add to list of open domains */
481 DLIST_ADD(state->getgrent_state, domain_state);
484 state->getgrent_initialized = True;
485 return True;
488 void winbindd_setgrent(struct winbindd_cli_state *state)
490 if (winbindd_setgrent_internal(state)) {
491 request_ok(state);
492 } else {
493 request_error(state);
497 /* Close file pointer to ntdom group database */
499 void winbindd_endgrent(struct winbindd_cli_state *state)
501 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
503 free_getent_state(state->getgrent_state);
504 state->getgrent_initialized = False;
505 state->getgrent_state = NULL;
506 request_ok(state);
509 /* Get the list of domain groups and domain aliases for a domain. We fill in
510 the sam_entries and num_sam_entries fields with domain group information.
511 The dispinfo_ndx field is incremented to the index of the next group to
512 fetch. Return True if some groups were returned, False otherwise. */
514 static BOOL get_sam_group_entries(struct getent_state *ent)
516 NTSTATUS status;
517 uint32 num_entries;
518 struct acct_info *name_list = NULL;
519 TALLOC_CTX *mem_ctx;
520 BOOL result = False;
521 struct acct_info *sam_grp_entries = NULL;
522 struct winbindd_domain *domain;
524 if (ent->got_sam_entries)
525 return False;
527 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
528 ent->domain_name))) {
529 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
530 return False;
533 /* Free any existing group info */
535 SAFE_FREE(ent->sam_entries);
536 ent->num_sam_entries = 0;
537 ent->got_sam_entries = True;
539 /* Enumerate domain groups */
541 num_entries = 0;
543 if (!(domain = find_domain_from_name(ent->domain_name))) {
544 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
545 goto done;
548 /* always get the domain global groups */
550 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
552 if (!NT_STATUS_IS_OK(status)) {
553 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
554 result = False;
555 goto done;
558 /* Copy entries into return buffer */
560 if (num_entries) {
561 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
562 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
563 num_entries));
564 result = False;
565 goto done;
567 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
570 ent->num_sam_entries = num_entries;
572 /* get the domain local groups if we are a member of a native win2k domain
573 and are not using LDAP to get the groups */
575 if ( ( lp_security() != SEC_ADS && domain->native_mode
576 && domain->primary) || domain->internal )
578 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
579 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
581 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
583 if ( !NT_STATUS_IS_OK(status) ) {
584 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
585 num_entries = 0;
587 else
588 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
590 /* Copy entries into return buffer */
592 if ( num_entries ) {
593 if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
595 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
596 num_entries));
597 result = False;
598 goto done;
601 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
602 num_entries * sizeof(struct acct_info) );
605 ent->num_sam_entries += num_entries;
609 /* Fill in remaining fields */
611 ent->sam_entries = name_list;
612 ent->sam_entry_index = 0;
614 result = (ent->num_sam_entries > 0);
616 done:
617 talloc_destroy(mem_ctx);
619 return result;
622 /* Fetch next group entry from ntdom database */
624 #define MAX_GETGRENT_GROUPS 500
626 void winbindd_getgrent(struct winbindd_cli_state *state)
628 struct getent_state *ent;
629 struct winbindd_gr *group_list = NULL;
630 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
631 char *gr_mem_list = NULL;
633 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
635 /* Check user has enabled this */
637 if (!lp_winbind_enum_groups()) {
638 request_error(state);
639 return;
642 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
644 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
645 request_error(state);
646 return;
649 memset(state->response.extra_data.data, '\0',
650 num_groups * sizeof(struct winbindd_gr) );
652 state->response.data.num_entries = 0;
654 group_list = (struct winbindd_gr *)state->response.extra_data.data;
656 if (!state->getgrent_initialized)
657 winbindd_setgrent_internal(state);
659 if (!(ent = state->getgrent_state)) {
660 request_error(state);
661 return;
664 /* Start sending back groups */
666 for (i = 0; i < num_groups; i++) {
667 struct acct_info *name_list = NULL;
668 fstring domain_group_name;
669 uint32 result;
670 gid_t group_gid;
671 size_t gr_mem_len;
672 char *gr_mem;
673 DOM_SID group_sid;
674 struct winbindd_domain *domain;
676 /* Do we need to fetch another chunk of groups? */
678 tryagain:
680 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
681 ent->sam_entry_index, ent->num_sam_entries));
683 if (ent->num_sam_entries == ent->sam_entry_index) {
685 while(ent && !get_sam_group_entries(ent)) {
686 struct getent_state *next_ent;
688 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
690 /* Free state information for this domain */
692 SAFE_FREE(ent->sam_entries);
694 next_ent = ent->next;
695 DLIST_REMOVE(state->getgrent_state, ent);
697 SAFE_FREE(ent);
698 ent = next_ent;
701 /* No more domains */
703 if (!ent)
704 break;
707 name_list = ent->sam_entries;
709 if (!(domain =
710 find_domain_from_name(ent->domain_name))) {
711 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
712 result = False;
713 goto done;
716 /* Lookup group info */
718 sid_copy(&group_sid, &domain->sid);
719 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
721 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid,
722 &group_gid, 0))) {
723 union unid_t id;
724 enum SID_NAME_USE type;
726 DEBUG(10, ("SID %s not in idmap\n",
727 sid_string_static(&group_sid)));
729 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
730 DEBUG(1, ("could not look up gid for group "
731 "%s\n",
732 name_list[ent->sam_entry_index].acct_name));
733 ent->sam_entry_index++;
734 goto tryagain;
737 if ((type != SID_NAME_DOM_GRP) &&
738 (type != SID_NAME_ALIAS) &&
739 (type != SID_NAME_WKN_GRP)) {
740 DEBUG(1, ("Group %s is a %s, not a group\n",
741 sid_type_lookup(type),
742 name_list[ent->sam_entry_index].acct_name));
743 ent->sam_entry_index++;
744 goto tryagain;
746 group_gid = id.gid;
749 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
750 (unsigned long)name_list[ent->sam_entry_index].rid));
752 /* Fill in group entry */
754 fill_domain_username(domain_group_name, ent->domain_name,
755 name_list[ent->sam_entry_index].acct_name, True);
757 result = fill_grent(&group_list[group_list_ndx],
758 ent->domain_name,
759 name_list[ent->sam_entry_index].acct_name,
760 group_gid);
762 /* Fill in group membership entry */
764 if (result) {
765 size_t num_gr_mem = 0;
766 DOM_SID member_sid;
767 group_list[group_list_ndx].num_gr_mem = 0;
768 gr_mem = NULL;
769 gr_mem_len = 0;
771 /* Get group membership */
772 if (state->request.cmd == WINBINDD_GETGRLST) {
773 result = True;
774 } else {
775 sid_copy(&member_sid, &domain->sid);
776 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
777 result = fill_grent_mem(
778 domain,
779 &member_sid,
780 SID_NAME_DOM_GRP,
781 &num_gr_mem,
782 &gr_mem, &gr_mem_len);
784 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
788 if (result) {
789 /* Append to group membership list */
790 gr_mem_list = SMB_REALLOC( gr_mem_list, gr_mem_list_len + gr_mem_len);
792 if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
793 DEBUG(0, ("out of memory\n"));
794 gr_mem_list_len = 0;
795 break;
798 DEBUG(10, ("list_len = %d, mem_len = %u\n",
799 gr_mem_list_len, (unsigned int)gr_mem_len));
801 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
802 gr_mem_len);
804 SAFE_FREE(gr_mem);
806 group_list[group_list_ndx].gr_mem_ofs =
807 gr_mem_list_len;
809 gr_mem_list_len += gr_mem_len;
812 ent->sam_entry_index++;
814 /* Add group to return list */
816 if (result) {
818 DEBUG(10, ("adding group num_entries = %d\n",
819 state->response.data.num_entries));
821 group_list_ndx++;
822 state->response.data.num_entries++;
824 state->response.length +=
825 sizeof(struct winbindd_gr);
827 } else {
828 DEBUG(0, ("could not lookup domain group %s\n",
829 domain_group_name));
833 /* Copy the list of group memberships to the end of the extra data */
835 if (group_list_ndx == 0)
836 goto done;
838 state->response.extra_data.data = SMB_REALLOC(
839 state->response.extra_data.data,
840 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
842 if (!state->response.extra_data.data) {
843 DEBUG(0, ("out of memory\n"));
844 group_list_ndx = 0;
845 SAFE_FREE(gr_mem_list);
846 request_error(state);
847 return;
850 memcpy(&((char *)state->response.extra_data.data)
851 [group_list_ndx * sizeof(struct winbindd_gr)],
852 gr_mem_list, gr_mem_list_len);
854 state->response.length += gr_mem_list_len;
856 DEBUG(10, ("returning %d groups, length = %d\n",
857 group_list_ndx, gr_mem_list_len));
859 /* Out of domains */
861 done:
863 SAFE_FREE(gr_mem_list);
865 if (group_list_ndx > 0)
866 request_ok(state);
867 else
868 request_error(state);
871 /* List domain groups without mapping to unix ids */
873 void winbindd_list_groups(struct winbindd_cli_state *state)
875 uint32 total_entries = 0;
876 struct winbindd_domain *domain;
877 const char *which_domain;
878 char *extra_data = NULL;
879 unsigned int extra_data_len = 0, i;
881 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
883 /* Ensure null termination */
884 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
885 which_domain = state->request.domain_name;
887 /* Enumerate over trusted domains */
889 for (domain = domain_list(); domain; domain = domain->next) {
890 struct getent_state groups;
892 /* if we have a domain name restricting the request and this
893 one in the list doesn't match, then just bypass the remainder
894 of the loop */
896 if ( *which_domain && !strequal(which_domain, domain->name) )
897 continue;
899 ZERO_STRUCT(groups);
901 /* Get list of sam groups */
903 fstrcpy(groups.domain_name, domain->name);
905 get_sam_group_entries(&groups);
907 if (groups.num_sam_entries == 0) {
908 /* this domain is empty or in an error state */
909 continue;
912 /* keep track the of the total number of groups seen so
913 far over all domains */
914 total_entries += groups.num_sam_entries;
916 /* Allocate some memory for extra data. Note that we limit
917 account names to sizeof(fstring) = 128 characters. */
918 extra_data = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
920 if (!extra_data) {
921 DEBUG(0,("failed to enlarge buffer!\n"));
922 request_error(state);
923 return;
926 /* Pack group list into extra data fields */
927 for (i = 0; i < groups.num_sam_entries; i++) {
928 char *group_name = ((struct acct_info *)
929 groups.sam_entries)[i].acct_name;
930 fstring name;
932 fill_domain_username(name, domain->name, group_name, True);
933 /* Append to extra data */
934 memcpy(&extra_data[extra_data_len], name,
935 strlen(name));
936 extra_data_len += strlen(name);
937 extra_data[extra_data_len++] = ',';
940 SAFE_FREE(groups.sam_entries);
943 /* Assign extra_data fields in response structure */
944 if (extra_data) {
945 extra_data[extra_data_len - 1] = '\0';
946 state->response.extra_data.data = extra_data;
947 state->response.length += extra_data_len;
950 /* No domains may have responded but that's still OK so don't
951 return an error. */
953 request_ok(state);
956 /* Get user supplementary groups. This is much quicker than trying to
957 invert the groups database. We merge the groups from the gids and
958 other_sids info3 fields as trusted domain, universal group
959 memberships, and nested groups (win2k native mode only) are not
960 returned by the getgroups RPC call but are present in the info3. */
962 struct getgroups_state {
963 struct winbindd_cli_state *state;
964 struct winbindd_domain *domain;
965 char *domname;
966 char *username;
967 DOM_SID user_sid;
969 const DOM_SID *token_sids;
970 size_t i, num_token_sids;
972 gid_t *token_gids;
973 size_t num_token_gids;
976 static void getgroups_usersid_recv(void *private_data, BOOL success,
977 const DOM_SID *sid, enum SID_NAME_USE type);
978 static void getgroups_tokensids_recv(void *private_data, BOOL success,
979 DOM_SID *token_sids, size_t num_token_sids);
980 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
982 void winbindd_getgroups(struct winbindd_cli_state *state)
984 struct getgroups_state *s;
986 /* Ensure null termination */
987 state->request.data.username
988 [sizeof(state->request.data.username)-1]='\0';
990 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
991 state->request.data.username));
993 /* Parse domain and username */
995 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
996 if (s == NULL) {
997 DEBUG(0, ("talloc failed\n"));
998 request_error(state);
999 return;
1002 s->state = state;
1004 if (!parse_domain_user_talloc(state->mem_ctx,
1005 state->request.data.username,
1006 &s->domname, &s->username)) {
1007 DEBUG(5, ("Could not parse domain user: %s\n",
1008 state->request.data.username));
1010 /* error out if we do not have nested group support */
1012 if ( !lp_winbind_nested_groups() ) {
1013 request_error(state);
1014 return;
1017 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1018 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1021 /* Get info for the domain */
1023 s->domain = find_domain_from_name_noinit(s->domname);
1025 if (s->domain == NULL) {
1026 DEBUG(7, ("could not find domain entry for domain %s\n",
1027 s->domname));
1028 request_error(state);
1029 return;
1032 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1033 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting "
1034 "getgroups() for %s\\%s.\n", s->domname,
1035 s->username));
1036 request_error(state);
1037 return;
1040 /* Get rid and name type from name. The following costs 1 packet */
1042 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1043 getgroups_usersid_recv, s);
1046 static void getgroups_usersid_recv(void *private_data, BOOL success,
1047 const DOM_SID *sid, enum SID_NAME_USE type)
1049 struct getgroups_state *s = private_data;
1051 if ((!success) ||
1052 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1053 request_error(s->state);
1054 return;
1057 sid_copy(&s->user_sid, sid);
1059 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1060 getgroups_tokensids_recv, s);
1063 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1064 DOM_SID *token_sids, size_t num_token_sids)
1066 struct getgroups_state *s = private_data;
1068 /* We need at least the user sid and the primary group in the token,
1069 * otherwise it's an error */
1071 if ((!success) || (num_token_sids < 2)) {
1072 request_error(s->state);
1073 return;
1076 s->token_sids = token_sids;
1077 s->num_token_sids = num_token_sids;
1078 s->i = 0;
1080 s->token_gids = NULL;
1081 s->num_token_gids = 0;
1083 getgroups_sid2gid_recv(s, False, 0);
1086 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
1088 struct getgroups_state *s = private_data;
1090 if (success)
1091 add_gid_to_array_unique(NULL, gid,
1092 &s->token_gids,
1093 &s->num_token_gids);
1095 if (s->i < s->num_token_sids) {
1096 const DOM_SID *sid = &s->token_sids[s->i];
1097 s->i += 1;
1099 if (sid_equal(sid, &s->user_sid)) {
1100 getgroups_sid2gid_recv(s, False, 0);
1101 return;
1104 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1105 getgroups_sid2gid_recv, s);
1106 return;
1109 s->state->response.data.num_entries = s->num_token_gids;
1110 s->state->response.extra_data.data = s->token_gids;
1111 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1112 request_ok(s->state);
1115 /* Get user supplementary sids. This is equivalent to the
1116 winbindd_getgroups() function but it involves a SID->SIDs mapping
1117 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1118 idmap. This call is designed to be used with applications that need
1119 to do ACL evaluation themselves. Note that the cached info3 data is
1120 not used
1122 this function assumes that the SID that comes in is a user SID. If
1123 you pass in another type of SID then you may get unpredictable
1124 results.
1127 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1128 size_t num_sids);
1130 void winbindd_getusersids(struct winbindd_cli_state *state)
1132 DOM_SID *user_sid;
1134 /* Ensure null termination */
1135 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1137 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1138 if (user_sid == NULL) {
1139 DEBUG(1, ("talloc failed\n"));
1140 request_error(state);
1141 return;
1144 if (!string_to_sid(user_sid, state->request.data.sid)) {
1145 DEBUG(1, ("Could not get convert sid %s from string\n",
1146 state->request.data.sid));
1147 request_error(state);
1148 return;
1151 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1152 state);
1155 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1156 size_t num_sids)
1158 struct winbindd_cli_state *state = private_data;
1159 char *ret = NULL;
1160 unsigned ofs, ret_size = 0;
1161 size_t i;
1163 if (!success) {
1164 request_error(state);
1165 return;
1168 /* work out the response size */
1169 for (i = 0; i < num_sids; i++) {
1170 const char *s = sid_string_static(&sids[i]);
1171 ret_size += strlen(s) + 1;
1174 /* build the reply */
1175 ret = SMB_MALLOC(ret_size);
1176 if (!ret) {
1177 DEBUG(0, ("malloc failed\n"));
1178 request_error(state);
1179 return;
1181 ofs = 0;
1182 for (i = 0; i < num_sids; i++) {
1183 const char *s = sid_string_static(&sids[i]);
1184 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1185 ofs += strlen(ret+ofs) + 1;
1188 /* Send data back to client */
1189 state->response.data.num_entries = num_sids;
1190 state->response.extra_data.data = ret;
1191 state->response.length += ret_size;
1192 request_ok(state);
1195 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1197 DOM_SID user_sid;
1198 struct winbindd_domain *domain;
1200 /* Ensure null termination */
1201 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1203 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1204 DEBUG(1, ("Could not get convert sid %s from string\n",
1205 state->request.data.sid));
1206 request_error(state);
1207 return;
1210 /* Get info for the domain */
1211 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1212 DEBUG(0,("could not find domain entry for sid %s\n",
1213 sid_string_static(&user_sid)));
1214 request_error(state);
1215 return;
1218 sendto_domain(state, domain);
1221 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1222 struct winbindd_cli_state *state)
1224 DOM_SID user_sid;
1225 NTSTATUS status;
1227 char *sidstring;
1228 ssize_t len;
1229 DOM_SID *groups;
1230 uint32 num_groups;
1232 /* Ensure null termination */
1233 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1235 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1236 DEBUG(1, ("Could not get convert sid %s from string\n",
1237 state->request.data.sid));
1238 return WINBINDD_ERROR;
1241 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1242 &user_sid, &num_groups,
1243 &groups);
1244 if (!NT_STATUS_IS_OK(status))
1245 return WINBINDD_ERROR;
1247 if (num_groups == 0) {
1248 state->response.data.num_entries = 0;
1249 state->response.extra_data.data = NULL;
1250 return WINBINDD_OK;
1253 if (!print_sidlist(NULL, groups, num_groups, &sidstring, &len)) {
1254 DEBUG(0, ("malloc failed\n"));
1255 return WINBINDD_ERROR;
1258 state->response.extra_data.data = sidstring;
1259 state->response.length += len+1;
1260 state->response.data.num_entries = num_groups;
1262 return WINBINDD_OK;