Minor comment updates ...
[Samba/gebeck_regimport.git] / source3 / nsswitch / winbindd_group.c
blobfba427536c8dd833c24fb156fb4e1cbbb1e60967
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.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 /*********************************************************************
31 *********************************************************************/
33 static int gr_mem_buffer( char **buffer, char **members, int num_members )
35 int i;
36 int len = 0;
37 int idx = 0;
39 if ( num_members == 0 ) {
40 *buffer = NULL;
41 return 0;
44 for ( i=0; i<num_members; i++ )
45 len += strlen(members[i])+1;
47 *buffer = (char*)smb_xmalloc(len);
48 for ( i=0; i<num_members; i++ ) {
49 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
50 idx += strlen(members[i])+1;
52 /* terminate with NULL */
53 (*buffer)[len-1] = '\0';
55 return len;
58 /***************************************************************
59 Empty static struct for negative caching.
60 ****************************************************************/
62 /* Fill a grent structure from various other information */
64 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
65 const char *gr_name, gid_t unix_gid)
67 fstring full_group_name;
68 /* Fill in uid/gid */
69 fill_domain_username(full_group_name, dom_name, gr_name);
71 gr->gr_gid = unix_gid;
73 /* Group name and password */
75 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
76 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
78 return True;
81 /* Fill in the group membership field of a NT group given by group_sid */
83 static BOOL fill_grent_mem(struct winbindd_domain *domain,
84 DOM_SID *group_sid,
85 enum SID_NAME_USE group_name_type,
86 int *num_gr_mem, char **gr_mem, int *gr_mem_len)
88 DOM_SID **sid_mem = NULL;
89 uint32 num_names = 0;
90 uint32 *name_types = NULL;
91 unsigned int buf_len, buf_ndx, i;
92 char **names = NULL, *buf;
93 BOOL result = False;
94 TALLOC_CTX *mem_ctx;
95 NTSTATUS status;
96 fstring sid_string;
98 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
99 return False;
101 /* Initialise group membership information */
103 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
105 *num_gr_mem = 0;
107 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
108 ((group_name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
110 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
111 sid_to_string(sid_string, group_sid), domain->name,
112 group_name_type));
113 goto done;
116 /* Lookup group members */
117 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
118 &sid_mem, &names, &name_types);
119 if (!NT_STATUS_IS_OK(status)) {
120 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
121 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
123 goto done;
126 DEBUG(10, ("looked up %d names\n", num_names));
128 if (DEBUGLEVEL >= 10) {
129 for (i = 0; i < num_names; i++)
130 DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
131 name_types[i]));
134 /* Add members to list */
136 buf = NULL;
137 buf_len = buf_ndx = 0;
139 again:
141 for (i = 0; i < num_names; i++) {
142 char *the_name;
143 fstring name;
144 int len;
146 the_name = names[i];
148 DEBUG(10, ("processing name %s\n", the_name));
150 /* FIXME: need to cope with groups within groups. These
151 occur in Universal groups on a Windows 2000 native mode
152 server. */
154 if (name_types[i] != SID_NAME_USER) {
155 DEBUG(3, ("name %s isn't a domain user\n", the_name));
156 continue;
159 /* Don't bother with machine accounts */
161 if (the_name[strlen(the_name) - 1] == '$') {
162 DEBUG(10, ("%s is machine account\n", the_name));
163 continue;
166 /* Append domain name */
168 fill_domain_username(name, domain->name, the_name);
170 len = strlen(name);
172 /* Add to list or calculate buffer length */
174 if (!buf) {
175 buf_len += len + 1; /* List is comma separated */
176 (*num_gr_mem)++;
177 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
178 } else {
179 DEBUG(10, ("appending %s at ndx %d\n", name, len));
180 safe_strcpy(&buf[buf_ndx], name, len);
181 buf_ndx += len;
182 buf[buf_ndx] = ',';
183 buf_ndx++;
187 /* Allocate buffer */
189 if (!buf && buf_len != 0) {
190 if (!(buf = malloc(buf_len))) {
191 DEBUG(1, ("out of memory\n"));
192 result = False;
193 goto done;
195 memset(buf, 0, buf_len);
196 goto again;
199 if (buf && buf_ndx > 0) {
200 buf[buf_ndx - 1] = '\0';
203 *gr_mem = buf;
204 *gr_mem_len = buf_len;
206 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
207 buf_len, *num_gr_mem ? buf : "NULL"));
208 result = True;
210 done:
212 talloc_destroy(mem_ctx);
214 DEBUG(10, ("fill_grent_mem returning %d\n", result));
216 return result;
219 /* Return a group structure from a group name */
221 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
223 DOM_SID group_sid;
224 WINBINDD_GR *grp;
225 struct winbindd_domain *domain;
226 enum SID_NAME_USE name_type;
227 fstring name_domain, name_group;
228 char *tmp, *gr_mem;
229 int gr_mem_len;
230 gid_t gid;
232 /* Ensure null termination */
233 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
235 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
236 state->request.data.groupname));
238 /* Parse domain and groupname */
240 memset(name_group, 0, sizeof(fstring));
242 tmp = state->request.data.groupname;
244 parse_domain_user(tmp, name_domain, name_group);
246 /* if no domain or our local domain, then do a local tdb search */
248 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
249 char *buffer = NULL;
251 if ( !(grp=wb_getgrnam(name_group)) ) {
252 DEBUG(5,("winbindd_getgrnam: lookup for %s\\%s failed\n",
253 name_domain, name_group));
254 return WINBINDD_ERROR;
256 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
258 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
260 state->response.data.gr.gr_mem_ofs = 0;
261 state->response.length += gr_mem_len;
262 state->response.extra_data = buffer; /* give the memory away */
264 return WINBINDD_OK;
267 /* should we deal with users for our domain? */
269 if ( lp_winbind_trusted_domains_only() && strequal(name_domain, lp_workgroup())) {
270 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
271 name_domain, name_group));
272 return WINBINDD_ERROR;
276 /* Get info for the domain */
278 if ((domain = find_domain_from_name(name_domain)) == NULL) {
279 DEBUG(0, ("could not get domain sid for domain %s\n",
280 name_domain));
281 return WINBINDD_ERROR;
284 /* Get rid and name type from name */
286 if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
287 &name_type)) {
288 DEBUG(1, ("group %s in domain %s does not exist\n",
289 name_group, name_domain));
290 return WINBINDD_ERROR;
293 if ( !((name_type==SID_NAME_DOM_GRP) ||
294 ((name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
296 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
297 name_group, name_type));
298 return WINBINDD_ERROR;
301 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
302 DEBUG(1, ("error converting unix gid to sid\n"));
303 return WINBINDD_ERROR;
306 if (!fill_grent(&state->response.data.gr, name_domain,
307 name_group, gid) ||
308 !fill_grent_mem(domain, &group_sid, name_type,
309 &state->response.data.gr.num_gr_mem,
310 &gr_mem, &gr_mem_len)) {
311 return WINBINDD_ERROR;
314 /* Group membership lives at start of extra data */
316 state->response.data.gr.gr_mem_ofs = 0;
318 state->response.length += gr_mem_len;
319 state->response.extra_data = gr_mem;
321 return WINBINDD_OK;
324 /* Return a group structure from a gid number */
326 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
328 struct winbindd_domain *domain;
329 WINBINDD_GR *grp;
330 DOM_SID group_sid;
331 enum SID_NAME_USE name_type;
332 fstring dom_name;
333 fstring group_name;
334 int gr_mem_len;
335 char *gr_mem;
337 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
338 (unsigned long)state->request.data.gid));
340 /* Bug out if the gid isn't in the winbind range */
342 if ((state->request.data.gid < server_state.gid_low) ||
343 (state->request.data.gid > server_state.gid_high))
344 return WINBINDD_ERROR;
346 /* alway try local tdb lookup first */
347 if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
348 char *buffer = NULL;
350 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
352 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
354 state->response.data.gr.gr_mem_ofs = 0;
355 state->response.length += gr_mem_len;
356 state->response.extra_data = buffer; /* give away the memory */
358 return WINBINDD_OK;
361 /* Get rid from gid */
362 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
363 DEBUG(1, ("could not convert gid %lu to rid\n",
364 (unsigned long)state->request.data.gid));
365 return WINBINDD_ERROR;
368 /* Get name from sid */
370 if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
371 DEBUG(1, ("could not lookup sid\n"));
372 return WINBINDD_ERROR;
375 /* Fill in group structure */
377 domain = find_domain_from_sid(&group_sid);
379 if (!domain) {
380 DEBUG(1,("Can't find domain from sid\n"));
381 return WINBINDD_ERROR;
384 if ( !((name_type==SID_NAME_DOM_GRP) ||
385 ((name_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
387 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
388 group_name, name_type));
389 return WINBINDD_ERROR;
392 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
393 state->request.data.gid) ||
394 !fill_grent_mem(domain, &group_sid, name_type,
395 &state->response.data.gr.num_gr_mem,
396 &gr_mem, &gr_mem_len))
397 return WINBINDD_ERROR;
399 /* Group membership lives at start of extra data */
401 state->response.data.gr.gr_mem_ofs = 0;
403 state->response.length += gr_mem_len;
404 state->response.extra_data = gr_mem;
406 return WINBINDD_OK;
410 * set/get/endgrent functions
413 /* "Rewind" file pointer for group database enumeration */
415 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
417 struct winbindd_domain *domain;
419 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
421 /* Check user has enabled this */
423 if (!lp_winbind_enum_groups())
424 return WINBINDD_ERROR;
426 /* Free old static data if it exists */
428 if (state->getgrent_state != NULL) {
429 free_getent_state(state->getgrent_state);
430 state->getgrent_state = NULL;
433 /* Create sam pipes for each domain we know about */
435 for (domain = domain_list(); domain != NULL; domain = domain->next) {
436 struct getent_state *domain_state;
439 /* don't add our domaina if we are a PDC or if we
440 are a member of a Samba domain */
442 if ( (IS_DC || lp_winbind_trusted_domains_only())
443 && strequal(domain->name, lp_workgroup()) )
445 continue;
448 /* Create a state record for this domain */
450 if ((domain_state = (struct getent_state *)
451 malloc(sizeof(struct getent_state))) == NULL) {
452 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
453 return WINBINDD_ERROR;
456 ZERO_STRUCTP(domain_state);
458 fstrcpy(domain_state->domain_name, domain->name);
460 /* Add to list of open domains */
462 DLIST_ADD(state->getgrent_state, domain_state);
465 return WINBINDD_OK;
468 /* Close file pointer to ntdom group database */
470 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
472 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
474 free_getent_state(state->getgrent_state);
475 state->getgrent_state = NULL;
477 return WINBINDD_OK;
480 /* Get the list of domain groups and domain aliases for a domain. We fill in
481 the sam_entries and num_sam_entries fields with domain group information.
482 The dispinfo_ndx field is incremented to the index of the next group to
483 fetch. Return True if some groups were returned, False otherwise. */
485 #define MAX_FETCH_SAM_ENTRIES 100
487 static BOOL get_sam_group_entries(struct getent_state *ent)
489 NTSTATUS status;
490 uint32 num_entries;
491 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
492 TALLOC_CTX *mem_ctx;
493 BOOL result = False;
494 struct acct_info *sam_grp_entries = NULL;
495 struct winbindd_domain *domain;
497 if (ent->got_sam_entries)
498 return False;
500 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
501 ent->domain_name))) {
502 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
503 return False;
506 /* Free any existing group info */
508 SAFE_FREE(ent->sam_entries);
509 ent->num_sam_entries = 0;
510 ent->got_sam_entries = True;
512 /* Enumerate domain groups */
514 num_entries = 0;
516 if (!(domain = find_domain_from_name(ent->domain_name))) {
517 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
518 goto done;
521 /* always get the domain global groups */
523 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
525 if (!NT_STATUS_IS_OK(status)) {
526 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
527 result = False;
528 goto done;
531 /* Copy entries into return buffer */
533 if (num_entries) {
534 if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
535 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
536 num_entries));
537 result = False;
538 goto done;
540 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
543 ent->num_sam_entries = num_entries;
545 /* get the domain local groups if we are a member of a native win2k domain
546 and are not using LDAP to get the groups */
548 if ( lp_security() != SEC_ADS && domain->native_mode
549 && strequal(lp_workgroup(), domain->name) )
551 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
553 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
555 if ( !NT_STATUS_IS_OK(status) ) {
556 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
557 num_entries = 0;
559 else
560 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
562 /* Copy entries into return buffer */
564 if ( num_entries ) {
565 if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
567 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
568 num_entries));
569 result = False;
570 SAFE_FREE( name_list );
571 goto done;
574 name_list = tmp_name_list;
576 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
577 num_entries * sizeof(struct acct_info) );
580 ent->num_sam_entries += num_entries;
584 /* Fill in remaining fields */
586 ent->sam_entries = name_list;
587 ent->sam_entry_index = 0;
589 result = (ent->num_sam_entries > 0);
591 done:
592 talloc_destroy(mem_ctx);
594 return result;
597 /* Fetch next group entry from ntdom database */
599 #define MAX_GETGRENT_GROUPS 500
601 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
603 struct getent_state *ent;
604 struct winbindd_gr *group_list = NULL;
605 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
606 char *new_extra_data, *gr_mem_list = NULL;
608 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
610 /* Check user has enabled this */
612 if (!lp_winbind_enum_groups())
613 return WINBINDD_ERROR;
615 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
617 if ((state->response.extra_data =
618 malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
619 return WINBINDD_ERROR;
621 state->response.data.num_entries = 0;
623 group_list = (struct winbindd_gr *)state->response.extra_data;
625 if (!(ent = state->getgrent_state))
626 return WINBINDD_ERROR;
628 /* Start sending back groups */
630 for (i = 0; i < num_groups; i++) {
631 struct acct_info *name_list = NULL;
632 fstring domain_group_name;
633 uint32 result;
634 gid_t group_gid;
635 int gr_mem_len;
636 char *gr_mem, *new_gr_mem_list;
637 DOM_SID group_sid;
638 struct winbindd_domain *domain;
640 /* Do we need to fetch another chunk of groups? */
642 tryagain:
644 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
645 ent->sam_entry_index, ent->num_sam_entries));
647 if (ent->num_sam_entries == ent->sam_entry_index) {
649 while(ent && !get_sam_group_entries(ent)) {
650 struct getent_state *next_ent;
652 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
654 /* Free state information for this domain */
656 SAFE_FREE(ent->sam_entries);
658 next_ent = ent->next;
659 DLIST_REMOVE(state->getgrent_state, ent);
661 SAFE_FREE(ent);
662 ent = next_ent;
665 /* No more domains */
667 if (!ent)
668 break;
671 name_list = ent->sam_entries;
673 if (!(domain =
674 find_domain_from_name(ent->domain_name))) {
675 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
676 result = False;
677 goto done;
680 /* Lookup group info */
682 sid_copy(&group_sid, &domain->sid);
683 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
685 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
687 DEBUG(1, ("could not look up gid for group %s\n",
688 name_list[ent->sam_entry_index].acct_name));
690 ent->sam_entry_index++;
691 goto tryagain;
694 DEBUG(10, ("got gid %lu for group %x\n", (unsigned long)group_gid,
695 name_list[ent->sam_entry_index].rid));
697 /* Fill in group entry */
699 fill_domain_username(domain_group_name, ent->domain_name,
700 name_list[ent->sam_entry_index].acct_name);
702 result = fill_grent(&group_list[group_list_ndx],
703 ent->domain_name,
704 name_list[ent->sam_entry_index].acct_name,
705 group_gid);
707 /* Fill in group membership entry */
709 if (result) {
710 DOM_SID member_sid;
711 group_list[group_list_ndx].num_gr_mem = 0;
712 gr_mem = NULL;
713 gr_mem_len = 0;
715 /* Get group membership */
716 if (state->request.cmd == WINBINDD_GETGRLST) {
717 result = True;
718 } else {
719 sid_copy(&member_sid, &domain->sid);
720 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
721 result = fill_grent_mem(
722 domain,
723 &member_sid,
724 SID_NAME_DOM_GRP,
725 &group_list[group_list_ndx].num_gr_mem,
726 &gr_mem, &gr_mem_len);
730 if (result) {
731 /* Append to group membership list */
732 new_gr_mem_list = Realloc(
733 gr_mem_list,
734 gr_mem_list_len + gr_mem_len);
736 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
737 DEBUG(0, ("out of memory\n"));
738 SAFE_FREE(gr_mem_list);
739 gr_mem_list_len = 0;
740 break;
743 DEBUG(10, ("list_len = %d, mem_len = %d\n",
744 gr_mem_list_len, gr_mem_len));
746 gr_mem_list = new_gr_mem_list;
748 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
749 gr_mem_len);
751 SAFE_FREE(gr_mem);
753 group_list[group_list_ndx].gr_mem_ofs =
754 gr_mem_list_len;
756 gr_mem_list_len += gr_mem_len;
759 ent->sam_entry_index++;
761 /* Add group to return list */
763 if (result) {
765 DEBUG(10, ("adding group num_entries = %d\n",
766 state->response.data.num_entries));
768 group_list_ndx++;
769 state->response.data.num_entries++;
771 state->response.length +=
772 sizeof(struct winbindd_gr);
774 } else {
775 DEBUG(0, ("could not lookup domain group %s\n",
776 domain_group_name));
780 /* Copy the list of group memberships to the end of the extra data */
782 if (group_list_ndx == 0)
783 goto done;
785 new_extra_data = Realloc(
786 state->response.extra_data,
787 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
789 if (!new_extra_data) {
790 DEBUG(0, ("out of memory\n"));
791 group_list_ndx = 0;
792 SAFE_FREE(state->response.extra_data);
793 SAFE_FREE(gr_mem_list);
795 return WINBINDD_ERROR;
798 state->response.extra_data = new_extra_data;
800 memcpy(&((char *)state->response.extra_data)
801 [group_list_ndx * sizeof(struct winbindd_gr)],
802 gr_mem_list, gr_mem_list_len);
804 SAFE_FREE(gr_mem_list);
806 state->response.length += gr_mem_list_len;
808 DEBUG(10, ("returning %d groups, length = %d\n",
809 group_list_ndx, gr_mem_list_len));
811 /* Out of domains */
813 done:
815 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
818 /* List domain groups without mapping to unix ids */
820 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
822 uint32 total_entries = 0;
823 struct winbindd_domain *domain;
824 const char *which_domain;
825 char *extra_data = NULL;
826 char *ted = NULL;
827 unsigned int extra_data_len = 0, i;
829 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
831 /* Ensure null termination */
832 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
833 which_domain = state->request.domain_name;
835 /* Enumerate over trusted domains */
837 for (domain = domain_list(); domain; domain = domain->next) {
838 struct getent_state groups;
840 /* if we have a domain name restricting the request and this
841 one in the list doesn't match, then just bypass the remainder
842 of the loop */
844 if ( *which_domain && !strequal(which_domain, domain->name) )
845 continue;
847 ZERO_STRUCT(groups);
849 /* Get list of sam groups */
851 fstrcpy(groups.domain_name, domain->name);
853 get_sam_group_entries(&groups);
855 if (groups.num_sam_entries == 0) {
856 /* this domain is empty or in an error state */
857 continue;
860 /* keep track the of the total number of groups seen so
861 far over all domains */
862 total_entries += groups.num_sam_entries;
864 /* Allocate some memory for extra data. Note that we limit
865 account names to sizeof(fstring) = 128 characters. */
866 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
868 if (!ted) {
869 DEBUG(0,("failed to enlarge buffer!\n"));
870 SAFE_FREE(extra_data);
871 return WINBINDD_ERROR;
872 } else
873 extra_data = ted;
875 /* Pack group list into extra data fields */
876 for (i = 0; i < groups.num_sam_entries; i++) {
877 char *group_name = ((struct acct_info *)
878 groups.sam_entries)[i].acct_name;
879 fstring name;
881 fill_domain_username(name, domain->name, group_name);
882 /* Append to extra data */
883 memcpy(&extra_data[extra_data_len], name,
884 strlen(name));
885 extra_data_len += strlen(name);
886 extra_data[extra_data_len++] = ',';
889 free(groups.sam_entries);
892 /* Assign extra_data fields in response structure */
893 if (extra_data) {
894 extra_data[extra_data_len - 1] = '\0';
895 state->response.extra_data = extra_data;
896 state->response.length += extra_data_len;
899 /* No domains may have responded but that's still OK so don't
900 return an error. */
902 return WINBINDD_OK;
905 /* Get user supplementary groups. This is much quicker than trying to
906 invert the groups database. We merge the groups from the gids and
907 other_sids info3 fields as trusted domain, universal group
908 memberships, and nested groups (win2k native mode only) are not
909 returned by the getgroups RPC call but are present in the info3. */
911 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
913 fstring name_domain, name_user;
914 DOM_SID user_sid, group_sid;
915 enum SID_NAME_USE name_type;
916 uint32 num_groups = 0;
917 uint32 num_gids = 0;
918 NTSTATUS status;
919 DOM_SID **user_grpsids;
920 struct winbindd_domain *domain;
921 enum winbindd_result result = WINBINDD_ERROR;
922 gid_t *gid_list;
923 unsigned int i;
924 TALLOC_CTX *mem_ctx;
925 NET_USER_INFO_3 *info3 = NULL;
927 /* Ensure null termination */
928 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
930 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
931 state->request.data.username));
933 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
934 state->request.data.username)))
935 return WINBINDD_ERROR;
937 /* Parse domain and username */
939 parse_domain_user(state->request.data.username,
940 name_domain, name_user);
942 /* bail if there is no domain */
944 if ( !*name_domain )
945 goto done;
947 /* Get info for the domain */
949 if ((domain = find_domain_from_name(name_domain)) == NULL) {
950 DEBUG(0, ("could not find domain entry for domain %s\n",
951 name_domain));
952 goto done;
955 /* Get rid and name type from name. The following costs 1 packet */
957 if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
958 &name_type)) {
959 DEBUG(1, ("user '%s' does not exist\n", name_user));
960 goto done;
963 if (name_type != SID_NAME_USER) {
964 DEBUG(1, ("name '%s' is not a user name: %d\n",
965 name_user, name_type));
966 goto done;
969 /* Treat the info3 cache as authoritative as the
970 lookup_usergroups() function may return cached data. */
972 if ((info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
974 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
975 info3->num_groups2, info3->num_other_sids));
977 num_groups = info3->num_other_sids + info3->num_groups2;
978 gid_list = calloc(sizeof(gid_t), num_groups);
980 /* Go through each other sid and convert it to a gid */
982 for (i = 0; i < info3->num_other_sids; i++) {
983 fstring name;
984 fstring dom_name;
985 enum SID_NAME_USE sid_type;
987 /* Is this sid known to us? It can either be
988 a trusted domain sid or a foreign sid. */
990 if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
991 dom_name, name, &sid_type))
993 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
994 sid_string_static(&info3->other_sids[i].sid)));
995 continue;
998 /* Check it is a domain group or an alias (domain local group)
999 in a win2k native mode domain. */
1001 if ( !((sid_type==SID_NAME_DOM_GRP) ||
1002 ((sid_type==SID_NAME_ALIAS) && strequal(lp_workgroup(), domain->name))) )
1004 DEBUG(10, ("winbindd_getgroups: sid type %d "
1005 "for %s is not a domain group\n",
1006 sid_type,
1007 sid_string_static(
1008 &info3->other_sids[i].sid)));
1009 continue;
1012 /* Map to a gid */
1014 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&info3->other_sids[i].sid, &gid_list[num_gids], 0)) )
1016 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1017 sid_string_static(&info3->other_sids[i].sid)));
1018 continue;
1021 /* We've jumped through a lot of hoops to get here */
1023 DEBUG(10, ("winbindd_getgroups: mapped other sid %s to "
1024 "gid %lu\n", sid_string_static(
1025 &info3->other_sids[i].sid),
1026 (unsigned long)gid_list[num_gids]));
1028 num_gids++;
1031 for (i = 0; i < info3->num_groups2; i++) {
1033 /* create the group SID */
1035 sid_copy( &group_sid, &domain->sid );
1036 sid_append_rid( &group_sid, info3->gids[i].g_rid );
1038 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid_list[num_gids], 0)) ) {
1039 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1040 sid_string_static(&group_sid)));
1043 num_gids++;
1046 SAFE_FREE(info3);
1048 } else {
1049 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1050 &user_sid, &num_groups,
1051 &user_grpsids);
1052 if (!NT_STATUS_IS_OK(status))
1053 goto done;
1055 gid_list = malloc(sizeof(gid_t) * num_groups);
1057 if (state->response.extra_data)
1058 goto done;
1060 for (i = 0; i < num_groups; i++) {
1061 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_grpsids[i], &gid_list[num_gids], 0))) {
1062 DEBUG(1, ("unable to convert group sid %s to gid\n",
1063 sid_string_static(user_grpsids[i])));
1064 continue;
1066 num_gids++;
1070 /* Send data back to client */
1072 state->response.data.num_entries = num_gids;
1073 state->response.extra_data = gid_list;
1074 state->response.length += num_gids * sizeof(gid_t);
1076 result = WINBINDD_OK;
1078 done:
1080 talloc_destroy(mem_ctx);
1082 return result;