r290: combination of BUG 1081 and patch from J. Klinger -- added remove_duplicate_gid...
[Samba.git] / source / nsswitch / winbindd_group.c
blob8f5306321accd5af84253c4a59962466d154c274
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 "includes.h"
26 #include "winbindd.h"
28 extern BOOL opt_nocache;
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 /*********************************************************************
34 *********************************************************************/
36 static int gr_mem_buffer( char **buffer, char **members, int num_members )
38 int i;
39 int len = 0;
40 int idx = 0;
42 if ( num_members == 0 ) {
43 *buffer = NULL;
44 return 0;
47 for ( i=0; i<num_members; i++ )
48 len += strlen(members[i])+1;
50 *buffer = (char*)smb_xmalloc(len);
51 for ( i=0; i<num_members; i++ ) {
52 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
53 idx += strlen(members[i])+1;
55 /* terminate with NULL */
56 (*buffer)[len-1] = '\0';
58 return len;
61 /***************************************************************
62 Empty static struct for negative caching.
63 ****************************************************************/
65 /* Fill a grent structure from various other information */
67 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
68 const char *gr_name, gid_t unix_gid)
70 fstring full_group_name;
71 /* Fill in uid/gid */
72 fill_domain_username(full_group_name, dom_name, gr_name);
74 gr->gr_gid = unix_gid;
76 /* Group name and password */
78 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
79 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
81 return True;
84 /* Fill in the group membership field of a NT group given by group_sid */
86 static BOOL fill_grent_mem(struct winbindd_domain *domain,
87 DOM_SID *group_sid,
88 enum SID_NAME_USE group_name_type,
89 int *num_gr_mem, char **gr_mem, int *gr_mem_len)
91 DOM_SID **sid_mem = NULL;
92 uint32 num_names = 0;
93 uint32 *name_types = NULL;
94 unsigned int buf_len, buf_ndx, i;
95 char **names = NULL, *buf;
96 BOOL result = False;
97 TALLOC_CTX *mem_ctx;
98 NTSTATUS status;
99 fstring sid_string;
101 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
102 return False;
104 /* Initialise group membership information */
106 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
108 *num_gr_mem = 0;
110 /* HACK ALERT!! This whole routine does not cope with group members
111 * from more than one domain, ie aliases. Thus we have to work it out
112 * ourselves in a special routine. */
114 if (domain->internal)
115 return fill_passdb_alias_grmem(domain, group_sid,
116 num_gr_mem,
117 gr_mem, gr_mem_len);
119 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
120 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
122 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
123 sid_to_string(sid_string, group_sid), domain->name,
124 group_name_type));
125 goto done;
128 /* Lookup group members */
129 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
130 &sid_mem, &names, &name_types);
131 if (!NT_STATUS_IS_OK(status)) {
132 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
133 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
135 goto done;
138 DEBUG(10, ("looked up %d names\n", num_names));
140 if (DEBUGLEVEL >= 10) {
141 for (i = 0; i < num_names; i++)
142 DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
143 name_types[i]));
146 /* Add members to list */
148 buf = NULL;
149 buf_len = buf_ndx = 0;
151 again:
153 for (i = 0; i < num_names; i++) {
154 char *the_name;
155 fstring name;
156 int len;
158 the_name = names[i];
160 DEBUG(10, ("processing name %s\n", the_name));
162 /* FIXME: need to cope with groups within groups. These
163 occur in Universal groups on a Windows 2000 native mode
164 server. */
166 /* make sure to allow machine accounts */
168 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
169 DEBUG(3, ("name %s isn't a domain user\n", the_name));
170 continue;
173 /* Append domain name */
175 fill_domain_username(name, domain->name, the_name);
177 len = strlen(name);
179 /* Add to list or calculate buffer length */
181 if (!buf) {
182 buf_len += len + 1; /* List is comma separated */
183 (*num_gr_mem)++;
184 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
185 } else {
186 DEBUG(10, ("appending %s at ndx %d\n", name, len));
187 safe_strcpy(&buf[buf_ndx], name, len);
188 buf_ndx += len;
189 buf[buf_ndx] = ',';
190 buf_ndx++;
194 /* Allocate buffer */
196 if (!buf && buf_len != 0) {
197 if (!(buf = malloc(buf_len))) {
198 DEBUG(1, ("out of memory\n"));
199 result = False;
200 goto done;
202 memset(buf, 0, buf_len);
203 goto again;
206 if (buf && buf_ndx > 0) {
207 buf[buf_ndx - 1] = '\0';
210 *gr_mem = buf;
211 *gr_mem_len = buf_len;
213 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
214 buf_len, *num_gr_mem ? buf : "NULL"));
215 result = True;
217 done:
219 talloc_destroy(mem_ctx);
221 DEBUG(10, ("fill_grent_mem returning %d\n", result));
223 return result;
226 /* Return a group structure from a group name */
228 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
230 DOM_SID group_sid;
231 WINBINDD_GR *grp;
232 struct winbindd_domain *domain;
233 enum SID_NAME_USE name_type;
234 fstring name_domain, name_group;
235 char *tmp, *gr_mem;
236 int gr_mem_len;
237 gid_t gid;
239 /* Ensure null termination */
240 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
242 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
243 state->request.data.groupname));
245 /* Parse domain and groupname */
247 memset(name_group, 0, sizeof(fstring));
249 tmp = state->request.data.groupname;
251 parse_domain_user(tmp, name_domain, name_group);
253 /* if no domain or our local domain, then do a local tdb search */
255 if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) &&
256 ((grp = wb_getgrnam(name_group)) != NULL) ) {
258 char *buffer = NULL;
260 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
262 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
264 state->response.data.gr.gr_mem_ofs = 0;
265 state->response.length += gr_mem_len;
266 state->response.extra_data = buffer; /* give the memory away */
268 return WINBINDD_OK;
271 /* if no domain or our local domain and no local tdb group, default to
272 * our local domain for aliases */
274 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
275 fstrcpy(name_domain, get_global_sam_name());
278 /* Get info for the domain */
280 if ((domain = find_domain_from_name(name_domain)) == NULL) {
281 DEBUG(3, ("could not get domain sid for domain %s\n",
282 name_domain));
283 return WINBINDD_ERROR;
285 /* should we deal with users for our domain? */
287 if ( lp_winbind_trusted_domains_only() && domain->primary) {
288 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
289 name_domain, name_group));
290 return WINBINDD_ERROR;
293 /* Get rid and name type from name */
295 if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
296 &name_type)) {
297 DEBUG(1, ("group %s in domain %s does not exist\n",
298 name_group, name_domain));
299 return WINBINDD_ERROR;
302 if ( !((name_type==SID_NAME_DOM_GRP) ||
303 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
304 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
306 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
307 name_group, name_type));
308 return WINBINDD_ERROR;
311 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
312 DEBUG(1, ("error converting unix gid to sid\n"));
313 return WINBINDD_ERROR;
316 if (!fill_grent(&state->response.data.gr, name_domain,
317 name_group, gid) ||
318 !fill_grent_mem(domain, &group_sid, name_type,
319 &state->response.data.gr.num_gr_mem,
320 &gr_mem, &gr_mem_len)) {
321 return WINBINDD_ERROR;
324 /* Group membership lives at start of extra data */
326 state->response.data.gr.gr_mem_ofs = 0;
328 state->response.length += gr_mem_len;
329 state->response.extra_data = gr_mem;
331 return WINBINDD_OK;
334 /* Return a group structure from a gid number */
336 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
338 struct winbindd_domain *domain;
339 WINBINDD_GR *grp;
340 DOM_SID group_sid;
341 enum SID_NAME_USE name_type;
342 fstring dom_name;
343 fstring group_name;
344 int gr_mem_len;
345 char *gr_mem;
347 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
348 (unsigned long)state->request.data.gid));
350 /* Bug out if the gid isn't in the winbind range */
352 if ((state->request.data.gid < server_state.gid_low) ||
353 (state->request.data.gid > server_state.gid_high))
354 return WINBINDD_ERROR;
356 /* alway try local tdb lookup first */
357 if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
358 char *buffer = NULL;
360 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
362 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
364 state->response.data.gr.gr_mem_ofs = 0;
365 state->response.length += gr_mem_len;
366 state->response.extra_data = buffer; /* give away the memory */
368 return WINBINDD_OK;
371 /* Get rid from gid */
372 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
373 DEBUG(1, ("could not convert gid %lu to rid\n",
374 (unsigned long)state->request.data.gid));
375 return WINBINDD_ERROR;
378 /* Get name from sid */
380 if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
381 DEBUG(1, ("could not lookup sid\n"));
382 return WINBINDD_ERROR;
385 /* Fill in group structure */
387 domain = find_domain_from_sid(&group_sid);
389 if (!domain) {
390 DEBUG(1,("Can't find domain from sid\n"));
391 return WINBINDD_ERROR;
394 if ( !((name_type==SID_NAME_DOM_GRP) ||
395 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
396 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
398 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
399 group_name, name_type));
400 return WINBINDD_ERROR;
403 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
404 state->request.data.gid) ||
405 !fill_grent_mem(domain, &group_sid, name_type,
406 &state->response.data.gr.num_gr_mem,
407 &gr_mem, &gr_mem_len))
408 return WINBINDD_ERROR;
410 /* Group membership lives at start of extra data */
412 state->response.data.gr.gr_mem_ofs = 0;
414 state->response.length += gr_mem_len;
415 state->response.extra_data = gr_mem;
417 return WINBINDD_OK;
421 * set/get/endgrent functions
424 /* "Rewind" file pointer for group database enumeration */
426 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
428 struct winbindd_domain *domain;
430 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
432 /* Check user has enabled this */
434 if (!lp_winbind_enum_groups())
435 return WINBINDD_ERROR;
437 /* Free old static data if it exists */
439 if (state->getgrent_state != NULL) {
440 free_getent_state(state->getgrent_state);
441 state->getgrent_state = NULL;
444 /* Create sam pipes for each domain we know about */
446 for (domain = domain_list(); domain != NULL; domain = domain->next) {
447 struct getent_state *domain_state;
450 /* don't add our domaina if we are a PDC or if we
451 are a member of a Samba domain */
453 if ( (IS_DC || lp_winbind_trusted_domains_only())
454 && domain->primary )
456 continue;
459 /* Create a state record for this domain */
461 if ((domain_state = (struct getent_state *)
462 malloc(sizeof(struct getent_state))) == NULL) {
463 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
464 return WINBINDD_ERROR;
467 ZERO_STRUCTP(domain_state);
469 fstrcpy(domain_state->domain_name, domain->name);
471 /* Add to list of open domains */
473 DLIST_ADD(state->getgrent_state, domain_state);
476 return WINBINDD_OK;
479 /* Close file pointer to ntdom group database */
481 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
483 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
485 free_getent_state(state->getgrent_state);
486 state->getgrent_state = NULL;
488 return WINBINDD_OK;
491 /* Get the list of domain groups and domain aliases for a domain. We fill in
492 the sam_entries and num_sam_entries fields with domain group information.
493 The dispinfo_ndx field is incremented to the index of the next group to
494 fetch. Return True if some groups were returned, False otherwise. */
496 #define MAX_FETCH_SAM_ENTRIES 100
498 static BOOL get_sam_group_entries(struct getent_state *ent)
500 NTSTATUS status;
501 uint32 num_entries;
502 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
503 TALLOC_CTX *mem_ctx;
504 BOOL result = False;
505 struct acct_info *sam_grp_entries = NULL;
506 struct winbindd_domain *domain;
508 if (ent->got_sam_entries)
509 return False;
511 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
512 ent->domain_name))) {
513 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
514 return False;
517 /* Free any existing group info */
519 SAFE_FREE(ent->sam_entries);
520 ent->num_sam_entries = 0;
521 ent->got_sam_entries = True;
523 /* Enumerate domain groups */
525 num_entries = 0;
527 if (!(domain = find_domain_from_name(ent->domain_name))) {
528 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
529 goto done;
532 /* always get the domain global groups */
534 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
536 if (!NT_STATUS_IS_OK(status)) {
537 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
538 result = False;
539 goto done;
542 /* Copy entries into return buffer */
544 if (num_entries) {
545 if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
546 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
547 num_entries));
548 result = False;
549 goto done;
551 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
554 ent->num_sam_entries = num_entries;
556 /* get the domain local groups if we are a member of a native win2k domain
557 and are not using LDAP to get the groups */
559 if ( ( lp_security() != SEC_ADS && domain->native_mode
560 && domain->primary) || domain->internal )
562 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
564 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
566 if ( !NT_STATUS_IS_OK(status) ) {
567 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
568 num_entries = 0;
570 else
571 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
573 /* Copy entries into return buffer */
575 if ( num_entries ) {
576 if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
578 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
579 num_entries));
580 result = False;
581 SAFE_FREE( name_list );
582 goto done;
585 name_list = tmp_name_list;
587 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
588 num_entries * sizeof(struct acct_info) );
591 ent->num_sam_entries += num_entries;
595 /* Fill in remaining fields */
597 ent->sam_entries = name_list;
598 ent->sam_entry_index = 0;
600 result = (ent->num_sam_entries > 0);
602 done:
603 talloc_destroy(mem_ctx);
605 return result;
608 /* Fetch next group entry from ntdom database */
610 #define MAX_GETGRENT_GROUPS 500
612 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
614 struct getent_state *ent;
615 struct winbindd_gr *group_list = NULL;
616 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
617 char *new_extra_data, *gr_mem_list = NULL;
619 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
621 /* Check user has enabled this */
623 if (!lp_winbind_enum_groups())
624 return WINBINDD_ERROR;
626 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
628 if ((state->response.extra_data =
629 malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
630 return WINBINDD_ERROR;
632 state->response.data.num_entries = 0;
634 group_list = (struct winbindd_gr *)state->response.extra_data;
636 if (!(ent = state->getgrent_state))
637 return WINBINDD_ERROR;
639 /* Start sending back groups */
641 for (i = 0; i < num_groups; i++) {
642 struct acct_info *name_list = NULL;
643 fstring domain_group_name;
644 uint32 result;
645 gid_t group_gid;
646 int gr_mem_len;
647 char *gr_mem, *new_gr_mem_list;
648 DOM_SID group_sid;
649 struct winbindd_domain *domain;
651 /* Do we need to fetch another chunk of groups? */
653 tryagain:
655 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
656 ent->sam_entry_index, ent->num_sam_entries));
658 if (ent->num_sam_entries == ent->sam_entry_index) {
660 while(ent && !get_sam_group_entries(ent)) {
661 struct getent_state *next_ent;
663 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
665 /* Free state information for this domain */
667 SAFE_FREE(ent->sam_entries);
669 next_ent = ent->next;
670 DLIST_REMOVE(state->getgrent_state, ent);
672 SAFE_FREE(ent);
673 ent = next_ent;
676 /* No more domains */
678 if (!ent)
679 break;
682 name_list = ent->sam_entries;
684 if (!(domain =
685 find_domain_from_name(ent->domain_name))) {
686 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
687 result = False;
688 goto done;
691 /* Lookup group info */
693 sid_copy(&group_sid, &domain->sid);
694 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
696 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
698 DEBUG(1, ("could not look up gid for group %s\n",
699 name_list[ent->sam_entry_index].acct_name));
701 ent->sam_entry_index++;
702 goto tryagain;
705 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
706 (unsigned long)name_list[ent->sam_entry_index].rid));
708 /* Fill in group entry */
710 fill_domain_username(domain_group_name, ent->domain_name,
711 name_list[ent->sam_entry_index].acct_name);
713 result = fill_grent(&group_list[group_list_ndx],
714 ent->domain_name,
715 name_list[ent->sam_entry_index].acct_name,
716 group_gid);
718 /* Fill in group membership entry */
720 if (result) {
721 DOM_SID member_sid;
722 group_list[group_list_ndx].num_gr_mem = 0;
723 gr_mem = NULL;
724 gr_mem_len = 0;
726 /* Get group membership */
727 if (state->request.cmd == WINBINDD_GETGRLST) {
728 result = True;
729 } else {
730 sid_copy(&member_sid, &domain->sid);
731 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
732 result = fill_grent_mem(
733 domain,
734 &member_sid,
735 SID_NAME_DOM_GRP,
736 &group_list[group_list_ndx].num_gr_mem,
737 &gr_mem, &gr_mem_len);
741 if (result) {
742 /* Append to group membership list */
743 new_gr_mem_list = Realloc(
744 gr_mem_list,
745 gr_mem_list_len + gr_mem_len);
747 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
748 DEBUG(0, ("out of memory\n"));
749 SAFE_FREE(gr_mem_list);
750 gr_mem_list_len = 0;
751 break;
754 DEBUG(10, ("list_len = %d, mem_len = %d\n",
755 gr_mem_list_len, gr_mem_len));
757 gr_mem_list = new_gr_mem_list;
759 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
760 gr_mem_len);
762 SAFE_FREE(gr_mem);
764 group_list[group_list_ndx].gr_mem_ofs =
765 gr_mem_list_len;
767 gr_mem_list_len += gr_mem_len;
770 ent->sam_entry_index++;
772 /* Add group to return list */
774 if (result) {
776 DEBUG(10, ("adding group num_entries = %d\n",
777 state->response.data.num_entries));
779 group_list_ndx++;
780 state->response.data.num_entries++;
782 state->response.length +=
783 sizeof(struct winbindd_gr);
785 } else {
786 DEBUG(0, ("could not lookup domain group %s\n",
787 domain_group_name));
791 /* Copy the list of group memberships to the end of the extra data */
793 if (group_list_ndx == 0)
794 goto done;
796 new_extra_data = Realloc(
797 state->response.extra_data,
798 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
800 if (!new_extra_data) {
801 DEBUG(0, ("out of memory\n"));
802 group_list_ndx = 0;
803 SAFE_FREE(state->response.extra_data);
804 SAFE_FREE(gr_mem_list);
806 return WINBINDD_ERROR;
809 state->response.extra_data = new_extra_data;
811 memcpy(&((char *)state->response.extra_data)
812 [group_list_ndx * sizeof(struct winbindd_gr)],
813 gr_mem_list, gr_mem_list_len);
815 SAFE_FREE(gr_mem_list);
817 state->response.length += gr_mem_list_len;
819 DEBUG(10, ("returning %d groups, length = %d\n",
820 group_list_ndx, gr_mem_list_len));
822 /* Out of domains */
824 done:
826 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
829 /* List domain groups without mapping to unix ids */
831 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
833 uint32 total_entries = 0;
834 struct winbindd_domain *domain;
835 const char *which_domain;
836 char *extra_data = NULL;
837 char *ted = NULL;
838 unsigned int extra_data_len = 0, i;
840 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
842 /* Ensure null termination */
843 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
844 which_domain = state->request.domain_name;
846 /* Enumerate over trusted domains */
848 for (domain = domain_list(); domain; domain = domain->next) {
849 struct getent_state groups;
851 /* if we have a domain name restricting the request and this
852 one in the list doesn't match, then just bypass the remainder
853 of the loop */
855 if ( *which_domain && !strequal(which_domain, domain->name) )
856 continue;
858 ZERO_STRUCT(groups);
860 /* Get list of sam groups */
862 fstrcpy(groups.domain_name, domain->name);
864 get_sam_group_entries(&groups);
866 if (groups.num_sam_entries == 0) {
867 /* this domain is empty or in an error state */
868 continue;
871 /* keep track the of the total number of groups seen so
872 far over all domains */
873 total_entries += groups.num_sam_entries;
875 /* Allocate some memory for extra data. Note that we limit
876 account names to sizeof(fstring) = 128 characters. */
877 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
879 if (!ted) {
880 DEBUG(0,("failed to enlarge buffer!\n"));
881 SAFE_FREE(extra_data);
882 return WINBINDD_ERROR;
883 } else
884 extra_data = ted;
886 /* Pack group list into extra data fields */
887 for (i = 0; i < groups.num_sam_entries; i++) {
888 char *group_name = ((struct acct_info *)
889 groups.sam_entries)[i].acct_name;
890 fstring name;
892 fill_domain_username(name, domain->name, group_name);
893 /* Append to extra data */
894 memcpy(&extra_data[extra_data_len], name,
895 strlen(name));
896 extra_data_len += strlen(name);
897 extra_data[extra_data_len++] = ',';
900 SAFE_FREE(groups.sam_entries);
903 /* Assign extra_data fields in response structure */
904 if (extra_data) {
905 extra_data[extra_data_len - 1] = '\0';
906 state->response.extra_data = extra_data;
907 state->response.length += extra_data_len;
910 /* No domains may have responded but that's still OK so don't
911 return an error. */
913 return WINBINDD_OK;
916 static void add_gid_to_array_unique(gid_t gid, gid_t **gids, int *num)
918 int i;
920 if ((*num) >= groups_max())
921 return;
923 for (i=0; i<*num; i++) {
924 if ((*gids)[i] == gid)
925 return;
928 *gids = Realloc(*gids, (*num+1) * sizeof(gid_t));
930 if (*gids == NULL)
931 return;
933 (*gids)[*num] = gid;
934 *num += 1;
937 static void add_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
939 gid_t gid;
940 DOM_SID *aliases;
941 int j, num_aliases;
943 DEBUG(10, ("Adding gids from SID: %s\n", sid_string_static(sid)));
945 if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
946 add_gid_to_array_unique(gid, gids, num);
948 /* Don't expand aliases if not explicitly activated -- for now */
949 /* we don't support windows local nested groups if we are a DC.
950 refer to to sid_to_gid() in the smbd server code to see why
951 -- jerry */
953 if (!lp_winbind_nested_groups() || IS_DC)
954 return;
956 /* Add nested group memberships */
958 if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases))
959 return;
961 for (j=0; j<num_aliases; j++) {
963 if (!NT_STATUS_IS_OK(sid_to_gid(&aliases[j], &gid)))
964 continue;
966 add_gid_to_array_unique(gid, gids, num);
968 SAFE_FREE(aliases);
971 /* Get user supplementary groups. This is much quicker than trying to
972 invert the groups database. We merge the groups from the gids and
973 other_sids info3 fields as trusted domain, universal group
974 memberships, and nested groups (win2k native mode only) are not
975 returned by the getgroups RPC call but are present in the info3. */
977 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
979 fstring name_domain, name_user;
980 DOM_SID user_sid, group_sid;
981 enum SID_NAME_USE name_type;
982 uint32 num_groups = 0;
983 uint32 num_gids = 0;
984 NTSTATUS status;
985 DOM_SID **user_grpsids;
986 struct winbindd_domain *domain;
987 enum winbindd_result result = WINBINDD_ERROR;
988 gid_t *gid_list = NULL;
989 unsigned int i;
990 TALLOC_CTX *mem_ctx;
991 NET_USER_INFO_3 *info3 = NULL;
993 /* Ensure null termination */
994 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
996 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
997 state->request.data.username));
999 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1000 state->request.data.username)))
1001 return WINBINDD_ERROR;
1003 /* Parse domain and username */
1005 parse_domain_user(state->request.data.username,
1006 name_domain, name_user);
1008 /* Get info for the domain */
1010 if ((domain = find_domain_from_name(name_domain)) == NULL) {
1011 DEBUG(7, ("could not find domain entry for domain %s\n",
1012 name_domain));
1013 goto done;
1016 if ( domain->primary && lp_winbind_trusted_domains_only()) {
1017 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
1018 name_domain, name_user));
1019 return WINBINDD_ERROR;
1022 /* Get rid and name type from name. The following costs 1 packet */
1024 if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
1025 &name_type)) {
1026 DEBUG(1, ("user '%s' does not exist\n", name_user));
1027 goto done;
1030 if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1031 DEBUG(1, ("name '%s' is not a user name: %d\n",
1032 name_user, name_type));
1033 goto done;
1036 add_gids_from_sid(&user_sid, &gid_list, &num_gids);
1038 /* Treat the info3 cache as authoritative as the
1039 lookup_usergroups() function may return cached data. */
1041 if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1043 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1044 info3->num_groups2, info3->num_other_sids));
1046 num_groups = info3->num_other_sids + info3->num_groups2;
1048 /* Go through each other sid and convert it to a gid */
1050 for (i = 0; i < info3->num_other_sids; i++) {
1051 fstring name;
1052 fstring dom_name;
1053 enum SID_NAME_USE sid_type;
1055 /* Is this sid known to us? It can either be
1056 a trusted domain sid or a foreign sid. */
1058 if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
1059 dom_name, name, &sid_type))
1061 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
1062 sid_string_static(&info3->other_sids[i].sid)));
1063 continue;
1066 /* Check it is a domain group or an alias (domain local group)
1067 in a win2k native mode domain. */
1069 if ( !((sid_type==SID_NAME_DOM_GRP) ||
1070 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1072 DEBUG(10, ("winbindd_getgroups: sid type %d "
1073 "for %s is not a domain group\n",
1074 sid_type,
1075 sid_string_static(
1076 &info3->other_sids[i].sid)));
1077 continue;
1080 add_gids_from_sid(&info3->other_sids[i].sid,
1081 &gid_list, &num_gids);
1083 if (gid_list == NULL)
1084 goto done;
1087 for (i = 0; i < info3->num_groups2; i++) {
1089 /* create the group SID */
1091 sid_copy( &group_sid, &domain->sid );
1092 sid_append_rid( &group_sid, info3->gids[i].g_rid );
1094 add_gids_from_sid(&group_sid, &gid_list, &num_gids);
1096 if (gid_list == NULL)
1097 goto done;
1100 SAFE_FREE(info3);
1102 } else {
1103 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1104 &user_sid, &num_groups,
1105 &user_grpsids);
1106 if (!NT_STATUS_IS_OK(status))
1107 goto done;
1109 if (state->response.extra_data)
1110 goto done;
1112 for (i = 0; i < num_groups; i++) {
1113 add_gids_from_sid(user_grpsids[i],
1114 &gid_list, &num_gids);
1116 if (gid_list == NULL)
1117 goto done;
1121 remove_duplicate_gids( &num_gids, gid_list );
1123 /* Send data back to client */
1125 state->response.data.num_entries = num_gids;
1126 state->response.extra_data = gid_list;
1127 state->response.length += num_gids * sizeof(gid_t);
1129 result = WINBINDD_OK;
1131 done:
1133 talloc_destroy(mem_ctx);
1135 return result;
1139 /* Get user supplementary sids. This is equivalent to the
1140 winbindd_getgroups() function but it involves a SID->SIDs mapping
1141 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1142 idmap. This call is designed to be used with applications that need
1143 to do ACL evaluation themselves. Note that the cached info3 data is
1144 not used
1146 this function assumes that the SID that comes in is a user SID. If
1147 you pass in another type of SID then you may get unpredictable
1148 results.
1150 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1152 DOM_SID user_sid;
1153 NTSTATUS status;
1154 DOM_SID **user_grpsids;
1155 struct winbindd_domain *domain;
1156 enum winbindd_result result = WINBINDD_ERROR;
1157 unsigned int i;
1158 TALLOC_CTX *mem_ctx;
1159 char *ret = NULL;
1160 uint32 num_groups;
1161 unsigned ofs, ret_size = 0;
1163 /* Ensure null termination */
1164 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1166 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1167 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1168 return WINBINDD_ERROR;
1171 if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1172 state->request.data.username))) {
1173 return WINBINDD_ERROR;
1176 /* Get info for the domain */
1177 if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1178 DEBUG(0,("could not find domain entry for sid %s\n",
1179 sid_string_static(&user_sid)));
1180 goto done;
1183 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1184 &user_sid, &num_groups,
1185 &user_grpsids);
1186 if (!NT_STATUS_IS_OK(status))
1187 goto done;
1189 if (num_groups == 0) {
1190 goto no_groups;
1193 /* work out the response size */
1194 for (i = 0; i < num_groups; i++) {
1195 const char *s = sid_string_static(user_grpsids[i]);
1196 ret_size += strlen(s) + 1;
1199 /* build the reply */
1200 ret = malloc(ret_size);
1201 if (!ret) goto done;
1202 ofs = 0;
1203 for (i = 0; i < num_groups; i++) {
1204 const char *s = sid_string_static(user_grpsids[i]);
1205 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1206 ofs += strlen(ret+ofs) + 1;
1209 no_groups:
1210 /* Send data back to client */
1211 state->response.data.num_entries = num_groups;
1212 state->response.extra_data = ret;
1213 state->response.length += ret_size;
1214 result = WINBINDD_OK;
1216 done:
1217 talloc_destroy(mem_ctx);
1219 return result;