This adds winbind-generated groups showing up in 'getent group'. It is not
[Samba/gebeck_regimport.git] / source / nsswitch / winbindd_group.c
blobd09b4ec6f91e87811b10714dff049b49301d4a2b
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 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
31 /*********************************************************************
32 *********************************************************************/
34 static int gr_mem_buffer( char **buffer, char **members, int num_members )
36 int i;
37 int len = 0;
38 int idx = 0;
40 if ( num_members == 0 ) {
41 *buffer = NULL;
42 return 0;
45 for ( i=0; i<num_members; i++ )
46 len += strlen(members[i])+1;
48 *buffer = (char*)smb_xmalloc(len);
49 for ( i=0; i<num_members; i++ ) {
50 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
51 idx += strlen(members[i])+1;
53 /* terminate with NULL */
54 (*buffer)[len-1] = '\0';
56 return len;
59 /***************************************************************
60 Empty static struct for negative caching.
61 ****************************************************************/
63 /* Fill a grent structure from various other information */
65 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
66 const char *gr_name, gid_t unix_gid)
68 fstring full_group_name;
69 /* Fill in uid/gid */
70 fill_domain_username(full_group_name, dom_name, gr_name);
72 gr->gr_gid = unix_gid;
74 /* Group name and password */
76 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
77 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
79 return True;
82 /* Fill in the group membership field of a NT group given by group_sid */
84 static BOOL fill_grent_mem(struct winbindd_domain *domain,
85 DOM_SID *group_sid,
86 enum SID_NAME_USE group_name_type,
87 int *num_gr_mem, char **gr_mem, int *gr_mem_len)
89 DOM_SID **sid_mem = NULL;
90 uint32 num_names = 0;
91 uint32 *name_types = NULL;
92 unsigned int buf_len, buf_ndx, i;
93 char **names = NULL, *buf;
94 BOOL result = False;
95 TALLOC_CTX *mem_ctx;
96 NTSTATUS status;
97 fstring sid_string;
99 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
100 return False;
102 /* Initialise group membership information */
104 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
106 *num_gr_mem = 0;
108 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
109 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
111 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
112 sid_to_string(sid_string, group_sid), domain->name,
113 group_name_type));
114 goto done;
117 /* Lookup group members */
118 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
119 &sid_mem, &names, &name_types);
120 if (!NT_STATUS_IS_OK(status)) {
121 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
122 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
124 goto done;
127 DEBUG(10, ("looked up %d names\n", num_names));
129 if (DEBUGLEVEL >= 10) {
130 for (i = 0; i < num_names; i++)
131 DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
132 name_types[i]));
135 /* Add members to list */
137 buf = NULL;
138 buf_len = buf_ndx = 0;
140 again:
142 for (i = 0; i < num_names; i++) {
143 char *the_name;
144 fstring name;
145 int len;
147 the_name = names[i];
149 DEBUG(10, ("processing name %s\n", the_name));
151 /* FIXME: need to cope with groups within groups. These
152 occur in Universal groups on a Windows 2000 native mode
153 server. */
155 /* make sure to allow machine accounts */
157 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
158 DEBUG(3, ("name %s isn't a domain user\n", the_name));
159 continue;
162 /* Append domain name */
164 fill_domain_username(name, domain->name, the_name);
166 len = strlen(name);
168 /* Add to list or calculate buffer length */
170 if (!buf) {
171 buf_len += len + 1; /* List is comma separated */
172 (*num_gr_mem)++;
173 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
174 } else {
175 DEBUG(10, ("appending %s at ndx %d\n", name, len));
176 safe_strcpy(&buf[buf_ndx], name, len);
177 buf_ndx += len;
178 buf[buf_ndx] = ',';
179 buf_ndx++;
183 /* Allocate buffer */
185 if (!buf && buf_len != 0) {
186 if (!(buf = malloc(buf_len))) {
187 DEBUG(1, ("out of memory\n"));
188 result = False;
189 goto done;
191 memset(buf, 0, buf_len);
192 goto again;
195 if (buf && buf_ndx > 0) {
196 buf[buf_ndx - 1] = '\0';
199 *gr_mem = buf;
200 *gr_mem_len = buf_len;
202 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
203 buf_len, *num_gr_mem ? buf : "NULL"));
204 result = True;
206 done:
208 talloc_destroy(mem_ctx);
210 DEBUG(10, ("fill_grent_mem returning %d\n", result));
212 return result;
215 /* Return a group structure from a group name */
217 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
219 DOM_SID group_sid;
220 WINBINDD_GR *grp;
221 struct winbindd_domain *domain;
222 enum SID_NAME_USE name_type;
223 fstring name_domain, name_group;
224 char *tmp, *gr_mem;
225 int gr_mem_len;
226 gid_t gid;
228 /* Ensure null termination */
229 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
231 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
232 state->request.data.groupname));
234 /* Parse domain and groupname */
236 memset(name_group, 0, sizeof(fstring));
238 tmp = state->request.data.groupname;
240 parse_domain_user(tmp, name_domain, name_group);
242 /* if no domain or our local domain, then do a local tdb search */
244 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
245 char *buffer = NULL;
247 if ( !(grp=wb_getgrnam(name_group)) ) {
248 DEBUG(5,("winbindd_getgrnam: lookup for %s\\%s failed\n",
249 name_domain, name_group));
250 return WINBINDD_ERROR;
252 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
254 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
256 state->response.data.gr.gr_mem_ofs = 0;
257 state->response.length += gr_mem_len;
258 state->response.extra_data = buffer; /* give the memory away */
260 return WINBINDD_OK;
263 /* Get info for the domain */
265 if ((domain = find_domain_from_name(name_domain)) == NULL) {
266 DEBUG(3, ("could not get domain sid for domain %s\n",
267 name_domain));
268 return WINBINDD_ERROR;
270 /* should we deal with users for our domain? */
272 if ( lp_winbind_trusted_domains_only() && domain->primary) {
273 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
274 name_domain, name_group));
275 return WINBINDD_ERROR;
278 /* Get rid and name type from name */
280 if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
281 &name_type)) {
282 DEBUG(1, ("group %s in domain %s does not exist\n",
283 name_group, name_domain));
284 return WINBINDD_ERROR;
287 if ( !((name_type==SID_NAME_DOM_GRP) ||
288 ((name_type==SID_NAME_ALIAS) && domain->primary)) )
290 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
291 name_group, name_type));
292 return WINBINDD_ERROR;
295 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
296 DEBUG(1, ("error converting unix gid to sid\n"));
297 return WINBINDD_ERROR;
300 if (!fill_grent(&state->response.data.gr, name_domain,
301 name_group, gid) ||
302 !fill_grent_mem(domain, &group_sid, name_type,
303 &state->response.data.gr.num_gr_mem,
304 &gr_mem, &gr_mem_len)) {
305 return WINBINDD_ERROR;
308 /* Group membership lives at start of extra data */
310 state->response.data.gr.gr_mem_ofs = 0;
312 state->response.length += gr_mem_len;
313 state->response.extra_data = gr_mem;
315 return WINBINDD_OK;
318 /* Return a group structure from a gid number */
320 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
322 struct winbindd_domain *domain;
323 WINBINDD_GR *grp;
324 DOM_SID group_sid;
325 enum SID_NAME_USE name_type;
326 fstring dom_name;
327 fstring group_name;
328 int gr_mem_len;
329 char *gr_mem;
331 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
332 (unsigned long)state->request.data.gid));
334 /* Bug out if the gid isn't in the winbind range */
336 if ((state->request.data.gid < server_state.gid_low) ||
337 (state->request.data.gid > server_state.gid_high))
338 return WINBINDD_ERROR;
340 /* alway try local tdb lookup first */
341 if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
342 char *buffer = NULL;
344 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
346 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
348 state->response.data.gr.gr_mem_ofs = 0;
349 state->response.length += gr_mem_len;
350 state->response.extra_data = buffer; /* give away the memory */
352 return WINBINDD_OK;
355 /* Get rid from gid */
356 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
357 DEBUG(1, ("could not convert gid %lu to rid\n",
358 (unsigned long)state->request.data.gid));
359 return WINBINDD_ERROR;
362 /* Get name from sid */
364 if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
365 DEBUG(1, ("could not lookup sid\n"));
366 return WINBINDD_ERROR;
369 /* Fill in group structure */
371 domain = find_domain_from_sid(&group_sid);
373 if (!domain) {
374 DEBUG(1,("Can't find domain from sid\n"));
375 return WINBINDD_ERROR;
378 if ( !((name_type==SID_NAME_DOM_GRP) ||
379 ((name_type==SID_NAME_ALIAS) && domain->primary) ))
381 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
382 group_name, name_type));
383 return WINBINDD_ERROR;
386 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
387 state->request.data.gid) ||
388 !fill_grent_mem(domain, &group_sid, name_type,
389 &state->response.data.gr.num_gr_mem,
390 &gr_mem, &gr_mem_len))
391 return WINBINDD_ERROR;
393 /* Group membership lives at start of extra data */
395 state->response.data.gr.gr_mem_ofs = 0;
397 state->response.length += gr_mem_len;
398 state->response.extra_data = gr_mem;
400 return WINBINDD_OK;
404 * set/get/endgrent functions
407 /* "Rewind" file pointer for group database enumeration */
409 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
411 struct winbindd_domain *domain;
413 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
415 /* Check user has enabled this */
417 if (!lp_winbind_enum_groups())
418 return WINBINDD_ERROR;
420 /* Free old static data if it exists */
422 if (state->getgrent_state != NULL) {
423 free_getent_state(state->getgrent_state);
424 state->getgrent_state = NULL;
427 /* Add our locally defined groups */
429 state->local_group_names = NULL;
430 state->num_local_group_names = 0;
431 state->local_group_ndx = 0;
433 wb_list_group_names(&state->local_group_names,
434 &state->num_local_group_names);
436 /* Create sam pipes for each domain we know about */
438 for (domain = domain_list(); domain != NULL; domain = domain->next) {
439 struct getent_state *domain_state;
442 /* don't add our domaina if we are a PDC or if we
443 are a member of a Samba domain */
445 if ( (IS_DC || lp_winbind_trusted_domains_only())
446 && domain->primary )
448 continue;
451 /* Create a state record for this domain */
453 if ((domain_state = (struct getent_state *)
454 malloc(sizeof(struct getent_state))) == NULL) {
455 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
456 return WINBINDD_ERROR;
459 ZERO_STRUCTP(domain_state);
461 fstrcpy(domain_state->domain_name, domain->name);
463 /* Add to list of open domains */
465 DLIST_ADD(state->getgrent_state, domain_state);
468 return WINBINDD_OK;
471 /* Close file pointer to ntdom group database */
473 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
475 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
477 free_getent_state(state->getgrent_state);
478 state->getgrent_state = NULL;
480 return WINBINDD_OK;
483 /* Fetch group entries from local faked database */
485 static BOOL return_local_winbind_groups(struct winbindd_cli_state *state)
487 WINBINDD_GR *grp;
488 char *buffer = NULL;
489 char *name;
490 int gr_mem_list_len = 0;
491 struct winbindd_gr *group_list;
492 struct winbindd_gr *gr;
494 if (state->local_group_names == NULL)
495 return False;
497 name = state->local_group_names[state->local_group_ndx];
498 grp = wb_getgrnam(name);
500 if (grp == NULL) {
501 DEBUG(3, ("Group %s vanished\n", name));
503 /* Stop that stuff.. */
504 state->local_group_ndx = state->num_local_group_names;
506 return False;
509 gr_mem_list_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
511 state->response.extra_data = malloc(sizeof(struct winbindd_gr) +
512 gr_mem_list_len);
513 state->response.length += sizeof(struct winbindd_gr) + gr_mem_list_len;
515 group_list = (struct winbindd_gr *)state->response.extra_data;
517 if (group_list == NULL) {
518 DEBUG(0, ("Could not malloc group_list\n"));
519 return False;
522 gr = &group_list[0];
524 ZERO_STRUCTP(gr);
526 gr->gr_gid = grp->gr_gid;
527 safe_strcpy(gr->gr_name, name, sizeof(gr->gr_name) - 1);
528 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
529 gr->num_gr_mem = grp->num_gr_mem;
530 gr->gr_mem_ofs = 0;
532 memcpy(&((char *)state->response.extra_data)
533 [sizeof(struct winbindd_gr)],
534 buffer, gr_mem_list_len);
536 SAFE_FREE(buffer);
537 SAFE_FREE(grp->gr_mem);
539 state->response.data.num_entries = 1;
541 state->local_group_ndx += 1;
543 if (state->local_group_ndx >= state->num_local_group_names) {
544 int i;
546 for (i=0; i<state->num_local_group_names; i++) {
547 free(state->local_group_names[i]);
549 free(state->local_group_names);
550 state->local_group_names = NULL;
553 return True;
557 /* Get the list of domain groups and domain aliases for a domain. We fill in
558 the sam_entries and num_sam_entries fields with domain group information.
559 The dispinfo_ndx field is incremented to the index of the next group to
560 fetch. Return True if some groups were returned, False otherwise. */
562 #define MAX_FETCH_SAM_ENTRIES 100
564 static BOOL get_sam_group_entries(struct getent_state *ent)
566 NTSTATUS status;
567 uint32 num_entries;
568 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
569 TALLOC_CTX *mem_ctx;
570 BOOL result = False;
571 struct acct_info *sam_grp_entries = NULL;
572 struct winbindd_domain *domain;
574 if (ent->got_sam_entries)
575 return False;
577 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
578 ent->domain_name))) {
579 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
580 return False;
583 /* Free any existing group info */
585 SAFE_FREE(ent->sam_entries);
586 ent->num_sam_entries = 0;
587 ent->got_sam_entries = True;
589 /* Enumerate domain groups */
591 num_entries = 0;
593 if (!(domain = find_domain_from_name(ent->domain_name))) {
594 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
595 goto done;
598 /* always get the domain global groups */
600 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
602 if (!NT_STATUS_IS_OK(status)) {
603 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
604 result = False;
605 goto done;
608 /* Copy entries into return buffer */
610 if (num_entries) {
611 if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
612 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
613 num_entries));
614 result = False;
615 goto done;
617 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
620 ent->num_sam_entries = num_entries;
622 /* get the domain local groups if we are a member of a native win2k domain
623 and are not using LDAP to get the groups */
625 if ( lp_security() != SEC_ADS && domain->native_mode
626 && domain->primary )
628 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
630 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
632 if ( !NT_STATUS_IS_OK(status) ) {
633 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
634 num_entries = 0;
636 else
637 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
639 /* Copy entries into return buffer */
641 if ( num_entries ) {
642 if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
644 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
645 num_entries));
646 result = False;
647 SAFE_FREE( name_list );
648 goto done;
651 name_list = tmp_name_list;
653 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
654 num_entries * sizeof(struct acct_info) );
657 ent->num_sam_entries += num_entries;
661 /* Fill in remaining fields */
663 ent->sam_entries = name_list;
664 ent->sam_entry_index = 0;
666 result = (ent->num_sam_entries > 0);
668 done:
669 talloc_destroy(mem_ctx);
671 return result;
674 /* Fetch next group entry from ntdom database */
676 #define MAX_GETGRENT_GROUPS 500
678 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
680 struct getent_state *ent;
681 struct winbindd_gr *group_list = NULL;
682 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
683 char *new_extra_data, *gr_mem_list = NULL;
685 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
687 /* Check user has enabled this */
689 if (!lp_winbind_enum_groups())
690 return WINBINDD_ERROR;
692 if (return_local_winbind_groups(state))
693 return WINBINDD_OK;
695 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
697 if ((state->response.extra_data =
698 malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
699 return WINBINDD_ERROR;
701 state->response.data.num_entries = 0;
703 group_list = (struct winbindd_gr *)state->response.extra_data;
705 if (!(ent = state->getgrent_state))
706 return WINBINDD_ERROR;
708 /* Start sending back groups */
710 for (i = 0; i < num_groups; i++) {
711 struct acct_info *name_list = NULL;
712 fstring domain_group_name;
713 uint32 result;
714 gid_t group_gid;
715 int gr_mem_len;
716 char *gr_mem, *new_gr_mem_list;
717 DOM_SID group_sid;
718 struct winbindd_domain *domain;
720 /* Do we need to fetch another chunk of groups? */
722 tryagain:
724 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
725 ent->sam_entry_index, ent->num_sam_entries));
727 if (ent->num_sam_entries == ent->sam_entry_index) {
729 while(ent && !get_sam_group_entries(ent)) {
730 struct getent_state *next_ent;
732 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
734 /* Free state information for this domain */
736 SAFE_FREE(ent->sam_entries);
738 next_ent = ent->next;
739 DLIST_REMOVE(state->getgrent_state, ent);
741 SAFE_FREE(ent);
742 ent = next_ent;
745 /* No more domains */
747 if (!ent)
748 break;
751 name_list = ent->sam_entries;
753 if (!(domain =
754 find_domain_from_name(ent->domain_name))) {
755 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
756 result = False;
757 goto done;
760 /* Lookup group info */
762 sid_copy(&group_sid, &domain->sid);
763 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
765 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
767 DEBUG(1, ("could not look up gid for group %s\n",
768 name_list[ent->sam_entry_index].acct_name));
770 ent->sam_entry_index++;
771 goto tryagain;
774 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
775 (unsigned long)name_list[ent->sam_entry_index].rid));
777 /* Fill in group entry */
779 fill_domain_username(domain_group_name, ent->domain_name,
780 name_list[ent->sam_entry_index].acct_name);
782 result = fill_grent(&group_list[group_list_ndx],
783 ent->domain_name,
784 name_list[ent->sam_entry_index].acct_name,
785 group_gid);
787 /* Fill in group membership entry */
789 if (result) {
790 DOM_SID member_sid;
791 group_list[group_list_ndx].num_gr_mem = 0;
792 gr_mem = NULL;
793 gr_mem_len = 0;
795 /* Get group membership */
796 if (state->request.cmd == WINBINDD_GETGRLST) {
797 result = True;
798 } else {
799 sid_copy(&member_sid, &domain->sid);
800 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
801 result = fill_grent_mem(
802 domain,
803 &member_sid,
804 SID_NAME_DOM_GRP,
805 &group_list[group_list_ndx].num_gr_mem,
806 &gr_mem, &gr_mem_len);
810 if (result) {
811 /* Append to group membership list */
812 new_gr_mem_list = Realloc(
813 gr_mem_list,
814 gr_mem_list_len + gr_mem_len);
816 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
817 DEBUG(0, ("out of memory\n"));
818 SAFE_FREE(gr_mem_list);
819 gr_mem_list_len = 0;
820 break;
823 DEBUG(10, ("list_len = %d, mem_len = %d\n",
824 gr_mem_list_len, gr_mem_len));
826 gr_mem_list = new_gr_mem_list;
828 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
829 gr_mem_len);
831 SAFE_FREE(gr_mem);
833 group_list[group_list_ndx].gr_mem_ofs =
834 gr_mem_list_len;
836 gr_mem_list_len += gr_mem_len;
839 ent->sam_entry_index++;
841 /* Add group to return list */
843 if (result) {
845 DEBUG(10, ("adding group num_entries = %d\n",
846 state->response.data.num_entries));
848 group_list_ndx++;
849 state->response.data.num_entries++;
851 state->response.length +=
852 sizeof(struct winbindd_gr);
854 } else {
855 DEBUG(0, ("could not lookup domain group %s\n",
856 domain_group_name));
860 /* Copy the list of group memberships to the end of the extra data */
862 if (group_list_ndx == 0)
863 goto done;
865 new_extra_data = Realloc(
866 state->response.extra_data,
867 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
869 if (!new_extra_data) {
870 DEBUG(0, ("out of memory\n"));
871 group_list_ndx = 0;
872 SAFE_FREE(state->response.extra_data);
873 SAFE_FREE(gr_mem_list);
875 return WINBINDD_ERROR;
878 state->response.extra_data = new_extra_data;
880 memcpy(&((char *)state->response.extra_data)
881 [group_list_ndx * sizeof(struct winbindd_gr)],
882 gr_mem_list, gr_mem_list_len);
884 SAFE_FREE(gr_mem_list);
886 state->response.length += gr_mem_list_len;
888 DEBUG(10, ("returning %d groups, length = %d\n",
889 group_list_ndx, gr_mem_list_len));
891 /* Out of domains */
893 done:
895 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
898 /* List domain groups without mapping to unix ids */
900 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
902 uint32 total_entries = 0;
903 struct winbindd_domain *domain;
904 const char *which_domain;
905 char *extra_data = NULL;
906 char *ted = NULL;
907 unsigned int extra_data_len = 0, i;
909 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
911 /* Ensure null termination */
912 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
913 which_domain = state->request.domain_name;
915 /* Enumerate over trusted domains */
917 for (domain = domain_list(); domain; domain = domain->next) {
918 struct getent_state groups;
920 /* if we have a domain name restricting the request and this
921 one in the list doesn't match, then just bypass the remainder
922 of the loop */
924 if ( *which_domain && !strequal(which_domain, domain->name) )
925 continue;
927 ZERO_STRUCT(groups);
929 /* Get list of sam groups */
931 fstrcpy(groups.domain_name, domain->name);
933 get_sam_group_entries(&groups);
935 if (groups.num_sam_entries == 0) {
936 /* this domain is empty or in an error state */
937 continue;
940 /* keep track the of the total number of groups seen so
941 far over all domains */
942 total_entries += groups.num_sam_entries;
944 /* Allocate some memory for extra data. Note that we limit
945 account names to sizeof(fstring) = 128 characters. */
946 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
948 if (!ted) {
949 DEBUG(0,("failed to enlarge buffer!\n"));
950 SAFE_FREE(extra_data);
951 return WINBINDD_ERROR;
952 } else
953 extra_data = ted;
955 /* Pack group list into extra data fields */
956 for (i = 0; i < groups.num_sam_entries; i++) {
957 char *group_name = ((struct acct_info *)
958 groups.sam_entries)[i].acct_name;
959 fstring name;
961 fill_domain_username(name, domain->name, group_name);
962 /* Append to extra data */
963 memcpy(&extra_data[extra_data_len], name,
964 strlen(name));
965 extra_data_len += strlen(name);
966 extra_data[extra_data_len++] = ',';
969 SAFE_FREE(groups.sam_entries);
972 /* Assign extra_data fields in response structure */
973 if (extra_data) {
974 extra_data[extra_data_len - 1] = '\0';
975 state->response.extra_data = extra_data;
976 state->response.length += extra_data_len;
979 /* No domains may have responded but that's still OK so don't
980 return an error. */
982 return WINBINDD_OK;
985 static void add_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
987 gid_t gid;
989 DEBUG(10, ("Adding gids from SID: %s\n", sid_string_static(sid)));
991 if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
992 add_gid_to_array_unique(gid, gids, num);
994 /* Add nested group memberships */
996 add_foreign_gids_from_sid(sid, gids, num);
999 /* Get user supplementary groups. This is much quicker than trying to
1000 invert the groups database. We merge the groups from the gids and
1001 other_sids info3 fields as trusted domain, universal group
1002 memberships, and nested groups (win2k native mode only) are not
1003 returned by the getgroups RPC call but are present in the info3. */
1005 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
1007 fstring name_domain, name_user;
1008 DOM_SID user_sid, group_sid;
1009 enum SID_NAME_USE name_type;
1010 uint32 num_groups = 0;
1011 uint32 num_gids = 0;
1012 NTSTATUS status;
1013 DOM_SID **user_grpsids;
1014 struct winbindd_domain *domain;
1015 enum winbindd_result result = WINBINDD_ERROR;
1016 gid_t *gid_list = NULL;
1017 unsigned int i;
1018 TALLOC_CTX *mem_ctx;
1019 NET_USER_INFO_3 *info3 = NULL;
1021 /* Ensure null termination */
1022 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
1024 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1025 state->request.data.username));
1027 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1028 state->request.data.username)))
1029 return WINBINDD_ERROR;
1031 /* Parse domain and username */
1033 parse_domain_user(state->request.data.username,
1034 name_domain, name_user);
1036 /* Get info for the domain */
1038 if ((domain = find_domain_from_name(name_domain)) == NULL) {
1039 DEBUG(7, ("could not find domain entry for domain %s\n",
1040 name_domain));
1041 goto done;
1044 if ( domain->primary && lp_winbind_trusted_domains_only()) {
1045 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
1046 name_domain, name_user));
1047 return WINBINDD_ERROR;
1050 /* Get rid and name type from name. The following costs 1 packet */
1052 if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
1053 &name_type)) {
1054 DEBUG(1, ("user '%s' does not exist\n", name_user));
1055 goto done;
1058 if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1059 DEBUG(1, ("name '%s' is not a user name: %d\n",
1060 name_user, name_type));
1061 goto done;
1064 add_gids_from_sid(&user_sid, &gid_list, &num_gids);
1066 /* Treat the info3 cache as authoritative as the
1067 lookup_usergroups() function may return cached data. */
1069 if ((info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1071 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1072 info3->num_groups2, info3->num_other_sids));
1074 num_groups = info3->num_other_sids + info3->num_groups2;
1076 /* Go through each other sid and convert it to a gid */
1078 for (i = 0; i < info3->num_other_sids; i++) {
1079 fstring name;
1080 fstring dom_name;
1081 enum SID_NAME_USE sid_type;
1083 /* Is this sid known to us? It can either be
1084 a trusted domain sid or a foreign sid. */
1086 if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
1087 dom_name, name, &sid_type))
1089 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
1090 sid_string_static(&info3->other_sids[i].sid)));
1091 continue;
1094 /* Check it is a domain group or an alias (domain local group)
1095 in a win2k native mode domain. */
1097 if ( !((sid_type==SID_NAME_DOM_GRP) ||
1098 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1100 DEBUG(10, ("winbindd_getgroups: sid type %d "
1101 "for %s is not a domain group\n",
1102 sid_type,
1103 sid_string_static(
1104 &info3->other_sids[i].sid)));
1105 continue;
1108 add_gids_from_sid(&info3->other_sids[i].sid,
1109 &gid_list, &num_gids);
1111 if (gid_list == NULL)
1112 goto done;
1115 for (i = 0; i < info3->num_groups2; i++) {
1117 /* create the group SID */
1119 sid_copy( &group_sid, &domain->sid );
1120 sid_append_rid( &group_sid, info3->gids[i].g_rid );
1122 add_gids_from_sid(&group_sid, &gid_list, &num_gids);
1124 if (gid_list == NULL)
1125 goto done;
1128 SAFE_FREE(info3);
1130 } else {
1131 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1132 &user_sid, &num_groups,
1133 &user_grpsids);
1134 if (!NT_STATUS_IS_OK(status))
1135 goto done;
1137 gid_list = malloc(sizeof(gid_t) * num_groups);
1139 if (state->response.extra_data)
1140 goto done;
1142 for (i = 0; i < num_groups; i++) {
1143 add_gids_from_sid(user_grpsids[i],
1144 &gid_list, &num_gids);
1146 if (gid_list == NULL)
1147 goto done;
1151 /* Send data back to client */
1153 state->response.data.num_entries = num_gids;
1154 state->response.extra_data = gid_list;
1155 state->response.length += num_gids * sizeof(gid_t);
1157 result = WINBINDD_OK;
1159 done:
1161 talloc_destroy(mem_ctx);
1163 return result;
1167 /* Get user supplementary sids. This is equivalent to the
1168 winbindd_getgroups() function but it involves a SID->SIDs mapping
1169 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1170 idmap. This call is designed to be used with applications that need
1171 to do ACL evaluation themselves. Note that the cached info3 data is
1172 not used
1174 this function assumes that the SID that comes in is a user SID. If
1175 you pass in another type of SID then you may get unpredictable
1176 results.
1178 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1180 DOM_SID user_sid;
1181 NTSTATUS status;
1182 DOM_SID **user_grpsids;
1183 struct winbindd_domain *domain;
1184 enum winbindd_result result = WINBINDD_ERROR;
1185 unsigned int i;
1186 TALLOC_CTX *mem_ctx;
1187 char *ret = NULL;
1188 uint32 num_groups;
1189 unsigned ofs, ret_size = 0;
1191 /* Ensure null termination */
1192 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1194 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1195 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1196 return WINBINDD_ERROR;
1199 if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1200 state->request.data.username))) {
1201 return WINBINDD_ERROR;
1204 /* Get info for the domain */
1205 if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1206 DEBUG(0,("could not find domain entry for sid %s\n",
1207 sid_string_static(&user_sid)));
1208 goto done;
1211 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1212 &user_sid, &num_groups,
1213 &user_grpsids);
1214 if (!NT_STATUS_IS_OK(status))
1215 goto done;
1217 if (num_groups == 0) {
1218 goto no_groups;
1221 /* work out the response size */
1222 for (i = 0; i < num_groups; i++) {
1223 const char *s = sid_string_static(user_grpsids[i]);
1224 ret_size += strlen(s) + 1;
1227 /* build the reply */
1228 ret = malloc(ret_size);
1229 if (!ret) goto done;
1230 ofs = 0;
1231 for (i = 0; i < num_groups; i++) {
1232 const char *s = sid_string_static(user_grpsids[i]);
1233 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1234 ofs += strlen(ret+ofs) + 1;
1237 no_groups:
1238 /* Send data back to client */
1239 state->response.data.num_entries = num_groups;
1240 state->response.extra_data = ret;
1241 state->response.length += ret_size;
1242 result = WINBINDD_OK;
1244 done:
1245 talloc_destroy(mem_ctx);
1247 return result;