r6273: Remove some unused code, minor cleanup
[Samba/gebeck_regimport.git] / source3 / nsswitch / winbindd_group.c
blobf0d3bc43ea6867ad065f9ca3ea4c34a20e102b1b
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
9 Copyright (C) Volker Lendecke 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "includes.h"
27 #include "winbindd.h"
29 extern BOOL opt_nocache;
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
34 /*********************************************************************
35 *********************************************************************/
37 static int gr_mem_buffer( char **buffer, char **members, int num_members )
39 int i;
40 int len = 0;
41 int idx = 0;
43 if ( num_members == 0 ) {
44 *buffer = NULL;
45 return 0;
48 for ( i=0; i<num_members; i++ )
49 len += strlen(members[i])+1;
51 *buffer = SMB_XMALLOC_ARRAY(char, len);
52 for ( i=0; i<num_members; i++ ) {
53 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
54 idx += strlen(members[i])+1;
56 /* terminate with NULL */
57 (*buffer)[len-1] = '\0';
59 return len;
62 /***************************************************************
63 Empty static struct for negative caching.
64 ****************************************************************/
66 /* Fill a grent structure from various other information */
68 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
69 const char *gr_name, gid_t unix_gid)
71 fstring full_group_name;
72 /* Fill in uid/gid */
73 fill_domain_username(full_group_name, dom_name, gr_name);
75 gr->gr_gid = unix_gid;
77 /* Group name and password */
79 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
80 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
82 return True;
85 /* Fill in the group membership field of a NT group given by group_sid */
87 static BOOL fill_grent_mem(struct winbindd_domain *domain,
88 DOM_SID *group_sid,
89 enum SID_NAME_USE group_name_type,
90 int *num_gr_mem, char **gr_mem, int *gr_mem_len)
92 DOM_SID **sid_mem = NULL;
93 uint32 num_names = 0;
94 uint32 *name_types = NULL;
95 unsigned int buf_len, buf_ndx, i;
96 char **names = NULL, *buf;
97 BOOL result = False;
98 TALLOC_CTX *mem_ctx;
99 NTSTATUS status;
100 fstring sid_string;
102 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
103 return False;
105 /* Initialise group membership information */
107 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
109 *num_gr_mem = 0;
111 /* HACK ALERT!! This whole routine does not cope with group members
112 * from more than one domain, ie aliases. Thus we have to work it out
113 * ourselves in a special routine. */
115 if (domain->internal)
116 return fill_passdb_alias_grmem(domain, group_sid,
117 num_gr_mem,
118 gr_mem, gr_mem_len);
120 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
121 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
123 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
124 sid_to_string(sid_string, group_sid), domain->name,
125 group_name_type));
126 goto done;
129 /* Lookup group members */
130 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
131 &sid_mem, &names, &name_types);
132 if (!NT_STATUS_IS_OK(status)) {
133 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
134 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
136 goto done;
139 DEBUG(10, ("looked up %d names\n", num_names));
141 if (DEBUGLEVEL >= 10) {
142 for (i = 0; i < num_names; i++)
143 DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
144 name_types[i]));
147 /* Add members to list */
149 buf = NULL;
150 buf_len = buf_ndx = 0;
152 again:
154 for (i = 0; i < num_names; i++) {
155 char *the_name;
156 fstring name;
157 int len;
159 the_name = names[i];
161 DEBUG(10, ("processing name %s\n", the_name));
163 /* FIXME: need to cope with groups within groups. These
164 occur in Universal groups on a Windows 2000 native mode
165 server. */
167 /* make sure to allow machine accounts */
169 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
170 DEBUG(3, ("name %s isn't a domain user\n", the_name));
171 continue;
174 /* Append domain name */
176 fill_domain_username(name, domain->name, the_name);
178 len = strlen(name);
180 /* Add to list or calculate buffer length */
182 if (!buf) {
183 buf_len += len + 1; /* List is comma separated */
184 (*num_gr_mem)++;
185 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
186 } else {
187 DEBUG(10, ("appending %s at ndx %d\n", name, len));
188 safe_strcpy(&buf[buf_ndx], name, len);
189 buf_ndx += len;
190 buf[buf_ndx] = ',';
191 buf_ndx++;
195 /* Allocate buffer */
197 if (!buf && buf_len != 0) {
198 if (!(buf = SMB_MALLOC(buf_len))) {
199 DEBUG(1, ("out of memory\n"));
200 result = False;
201 goto done;
203 memset(buf, 0, buf_len);
204 goto again;
207 if (buf && buf_ndx > 0) {
208 buf[buf_ndx - 1] = '\0';
211 *gr_mem = buf;
212 *gr_mem_len = buf_len;
214 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
215 buf_len, *num_gr_mem ? buf : "NULL"));
216 result = True;
218 done:
220 talloc_destroy(mem_ctx);
222 DEBUG(10, ("fill_grent_mem returning %d\n", result));
224 return result;
227 /* Return a group structure from a group name */
229 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
231 DOM_SID group_sid;
232 WINBINDD_GR *grp;
233 struct winbindd_domain *domain;
234 enum SID_NAME_USE name_type;
235 fstring name_domain, name_group;
236 char *tmp, *gr_mem;
237 int gr_mem_len;
238 gid_t gid;
240 /* Ensure null termination */
241 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
243 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
244 state->request.data.groupname));
246 /* Parse domain and groupname */
248 memset(name_group, 0, sizeof(fstring));
250 tmp = state->request.data.groupname;
252 parse_domain_user(tmp, name_domain, name_group);
254 /* if no domain or our local domain, then do a local tdb search */
256 if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) &&
257 ((grp = wb_getgrnam(name_group)) != NULL) ) {
259 char *buffer = NULL;
261 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
263 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
265 state->response.data.gr.gr_mem_ofs = 0;
266 state->response.length += gr_mem_len;
267 state->response.extra_data = buffer; /* give the memory away */
269 return WINBINDD_OK;
272 /* if no domain or our local domain and no local tdb group, default to
273 * our local domain for aliases */
275 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
276 fstrcpy(name_domain, get_global_sam_name());
279 /* Get info for the domain */
281 if ((domain = find_domain_from_name(name_domain)) == NULL) {
282 DEBUG(3, ("could not get domain sid for domain %s\n",
283 name_domain));
284 return WINBINDD_ERROR;
286 /* should we deal with users for our domain? */
288 if ( lp_winbind_trusted_domains_only() && domain->primary) {
289 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
290 name_domain, name_group));
291 return WINBINDD_ERROR;
294 /* Get rid and name type from name */
296 if (!winbindd_lookup_sid_by_name(domain, domain->name, name_group, &group_sid,
297 &name_type)) {
298 DEBUG(1, ("group %s in domain %s does not exist\n",
299 name_group, name_domain));
300 return WINBINDD_ERROR;
303 if ( !((name_type==SID_NAME_DOM_GRP) ||
304 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
305 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
307 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
308 name_group, name_type));
309 return WINBINDD_ERROR;
312 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
313 DEBUG(1, ("error converting unix gid to sid\n"));
314 return WINBINDD_ERROR;
317 if (!fill_grent(&state->response.data.gr, name_domain,
318 name_group, gid) ||
319 !fill_grent_mem(domain, &group_sid, name_type,
320 &state->response.data.gr.num_gr_mem,
321 &gr_mem, &gr_mem_len)) {
322 return WINBINDD_ERROR;
325 /* Group membership lives at start of extra data */
327 state->response.data.gr.gr_mem_ofs = 0;
329 state->response.length += gr_mem_len;
330 state->response.extra_data = gr_mem;
332 return WINBINDD_OK;
335 /* Return a group structure from a gid number */
337 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
339 struct winbindd_domain *domain;
340 WINBINDD_GR *grp;
341 DOM_SID group_sid;
342 enum SID_NAME_USE name_type;
343 fstring dom_name;
344 fstring group_name;
345 int gr_mem_len;
346 char *gr_mem;
348 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
349 (unsigned long)state->request.data.gid));
351 /* Bug out if the gid isn't in the winbind range */
353 if ((state->request.data.gid < server_state.gid_low) ||
354 (state->request.data.gid > server_state.gid_high))
355 return WINBINDD_ERROR;
357 /* alway try local tdb lookup first */
358 if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
359 char *buffer = NULL;
361 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
363 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
365 state->response.data.gr.gr_mem_ofs = 0;
366 state->response.length += gr_mem_len;
367 state->response.extra_data = buffer; /* give away the memory */
369 return WINBINDD_OK;
372 /* Get rid from gid */
373 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
374 DEBUG(1, ("could not convert gid %lu to rid\n",
375 (unsigned long)state->request.data.gid));
376 return WINBINDD_ERROR;
379 /* Get name from sid */
381 if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
382 DEBUG(1, ("could not lookup sid\n"));
383 return WINBINDD_ERROR;
386 /* Fill in group structure */
388 domain = find_domain_from_sid(&group_sid);
390 if (!domain) {
391 DEBUG(1,("Can't find domain from sid\n"));
392 return WINBINDD_ERROR;
395 if ( !((name_type==SID_NAME_DOM_GRP) ||
396 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
397 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
399 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
400 group_name, name_type));
401 return WINBINDD_ERROR;
404 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
405 state->request.data.gid) ||
406 !fill_grent_mem(domain, &group_sid, name_type,
407 &state->response.data.gr.num_gr_mem,
408 &gr_mem, &gr_mem_len))
409 return WINBINDD_ERROR;
411 /* Group membership lives at start of extra data */
413 state->response.data.gr.gr_mem_ofs = 0;
415 state->response.length += gr_mem_len;
416 state->response.extra_data = gr_mem;
418 return WINBINDD_OK;
422 * set/get/endgrent functions
425 /* "Rewind" file pointer for group database enumeration */
427 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
429 struct winbindd_domain *domain;
431 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
433 /* Check user has enabled this */
435 if (!lp_winbind_enum_groups())
436 return WINBINDD_ERROR;
438 /* Free old static data if it exists */
440 if (state->getgrent_state != NULL) {
441 free_getent_state(state->getgrent_state);
442 state->getgrent_state = NULL;
445 /* Create sam pipes for each domain we know about */
447 for (domain = domain_list(); domain != NULL; domain = domain->next) {
448 struct getent_state *domain_state;
450 /* Create a state record for this domain */
452 /* don't add our domaina if we are a PDC or if we
453 are a member of a Samba domain */
455 if ( lp_winbind_trusted_domains_only() && domain->primary )
457 continue;
461 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
462 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
463 return WINBINDD_ERROR;
466 ZERO_STRUCTP(domain_state);
468 fstrcpy(domain_state->domain_name, domain->name);
470 /* Add to list of open domains */
472 DLIST_ADD(state->getgrent_state, domain_state);
475 state->getgrent_initialized = True;
477 return WINBINDD_OK;
480 /* Close file pointer to ntdom group database */
482 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
484 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
486 free_getent_state(state->getgrent_state);
487 state->getgrent_initialized = False;
488 state->getgrent_state = NULL;
490 return WINBINDD_OK;
493 /* Get the list of domain groups and domain aliases for a domain. We fill in
494 the sam_entries and num_sam_entries fields with domain group information.
495 The dispinfo_ndx field is incremented to the index of the next group to
496 fetch. Return True if some groups were returned, False otherwise. */
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 = SMB_MALLOC_ARRAY(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 = SMB_REALLOC_ARRAY( name_list, 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 = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL)
629 return WINBINDD_ERROR;
631 memset(state->response.extra_data, '\0',
632 num_groups * sizeof(struct winbindd_gr) );
634 state->response.data.num_entries = 0;
636 group_list = (struct winbindd_gr *)state->response.extra_data;
638 if (!state->getgrent_initialized)
639 winbindd_setgrent(state);
641 if (!(ent = state->getgrent_state))
642 return WINBINDD_ERROR;
644 /* Start sending back groups */
646 for (i = 0; i < num_groups; i++) {
647 struct acct_info *name_list = NULL;
648 fstring domain_group_name;
649 uint32 result;
650 gid_t group_gid;
651 int gr_mem_len;
652 char *gr_mem, *new_gr_mem_list;
653 DOM_SID group_sid;
654 struct winbindd_domain *domain;
656 /* Do we need to fetch another chunk of groups? */
658 tryagain:
660 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
661 ent->sam_entry_index, ent->num_sam_entries));
663 if (ent->num_sam_entries == ent->sam_entry_index) {
665 while(ent && !get_sam_group_entries(ent)) {
666 struct getent_state *next_ent;
668 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
670 /* Free state information for this domain */
672 SAFE_FREE(ent->sam_entries);
674 next_ent = ent->next;
675 DLIST_REMOVE(state->getgrent_state, ent);
677 SAFE_FREE(ent);
678 ent = next_ent;
681 /* No more domains */
683 if (!ent)
684 break;
687 name_list = ent->sam_entries;
689 if (!(domain =
690 find_domain_from_name(ent->domain_name))) {
691 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
692 result = False;
693 goto done;
696 /* Lookup group info */
698 sid_copy(&group_sid, &domain->sid);
699 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
701 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
703 DEBUG(1, ("could not look up gid for group %s\n",
704 name_list[ent->sam_entry_index].acct_name));
706 ent->sam_entry_index++;
707 goto tryagain;
710 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
711 (unsigned long)name_list[ent->sam_entry_index].rid));
713 /* Fill in group entry */
715 fill_domain_username(domain_group_name, ent->domain_name,
716 name_list[ent->sam_entry_index].acct_name);
718 result = fill_grent(&group_list[group_list_ndx],
719 ent->domain_name,
720 name_list[ent->sam_entry_index].acct_name,
721 group_gid);
723 /* Fill in group membership entry */
725 if (result) {
726 DOM_SID member_sid;
727 group_list[group_list_ndx].num_gr_mem = 0;
728 gr_mem = NULL;
729 gr_mem_len = 0;
731 /* Get group membership */
732 if (state->request.cmd == WINBINDD_GETGRLST) {
733 result = True;
734 } else {
735 sid_copy(&member_sid, &domain->sid);
736 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
737 result = fill_grent_mem(
738 domain,
739 &member_sid,
740 SID_NAME_DOM_GRP,
741 &group_list[group_list_ndx].num_gr_mem,
742 &gr_mem, &gr_mem_len);
746 if (result) {
747 /* Append to group membership list */
748 new_gr_mem_list = SMB_REALLOC( gr_mem_list, gr_mem_list_len + gr_mem_len);
750 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
751 DEBUG(0, ("out of memory\n"));
752 SAFE_FREE(gr_mem_list);
753 gr_mem_list_len = 0;
754 break;
757 DEBUG(10, ("list_len = %d, mem_len = %d\n",
758 gr_mem_list_len, gr_mem_len));
760 gr_mem_list = new_gr_mem_list;
762 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
763 gr_mem_len);
765 SAFE_FREE(gr_mem);
767 group_list[group_list_ndx].gr_mem_ofs =
768 gr_mem_list_len;
770 gr_mem_list_len += gr_mem_len;
773 ent->sam_entry_index++;
775 /* Add group to return list */
777 if (result) {
779 DEBUG(10, ("adding group num_entries = %d\n",
780 state->response.data.num_entries));
782 group_list_ndx++;
783 state->response.data.num_entries++;
785 state->response.length +=
786 sizeof(struct winbindd_gr);
788 } else {
789 DEBUG(0, ("could not lookup domain group %s\n",
790 domain_group_name));
794 /* Copy the list of group memberships to the end of the extra data */
796 if (group_list_ndx == 0)
797 goto done;
799 new_extra_data = SMB_REALLOC(
800 state->response.extra_data,
801 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
803 if (!new_extra_data) {
804 DEBUG(0, ("out of memory\n"));
805 group_list_ndx = 0;
806 SAFE_FREE(state->response.extra_data);
807 SAFE_FREE(gr_mem_list);
809 return WINBINDD_ERROR;
812 state->response.extra_data = new_extra_data;
814 memcpy(&((char *)state->response.extra_data)
815 [group_list_ndx * sizeof(struct winbindd_gr)],
816 gr_mem_list, gr_mem_list_len);
818 SAFE_FREE(gr_mem_list);
820 state->response.length += gr_mem_list_len;
822 DEBUG(10, ("returning %d groups, length = %d\n",
823 group_list_ndx, gr_mem_list_len));
825 /* Out of domains */
827 done:
829 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
832 /* List domain groups without mapping to unix ids */
834 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
836 uint32 total_entries = 0;
837 struct winbindd_domain *domain;
838 const char *which_domain;
839 char *extra_data = NULL;
840 char *ted = NULL;
841 unsigned int extra_data_len = 0, i;
843 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
845 /* Ensure null termination */
846 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
847 which_domain = state->request.domain_name;
849 /* Enumerate over trusted domains */
851 for (domain = domain_list(); domain; domain = domain->next) {
852 struct getent_state groups;
854 /* if we have a domain name restricting the request and this
855 one in the list doesn't match, then just bypass the remainder
856 of the loop */
858 if ( *which_domain && !strequal(which_domain, domain->name) )
859 continue;
861 if ( !domain->initialized )
862 set_dc_type_and_flags( domain );
865 ZERO_STRUCT(groups);
867 /* Get list of sam groups */
869 fstrcpy(groups.domain_name, domain->name);
871 get_sam_group_entries(&groups);
873 if (groups.num_sam_entries == 0) {
874 /* this domain is empty or in an error state */
875 continue;
878 /* keep track the of the total number of groups seen so
879 far over all domains */
880 total_entries += groups.num_sam_entries;
882 /* Allocate some memory for extra data. Note that we limit
883 account names to sizeof(fstring) = 128 characters. */
884 ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
886 if (!ted) {
887 DEBUG(0,("failed to enlarge buffer!\n"));
888 SAFE_FREE(extra_data);
889 return WINBINDD_ERROR;
890 } else
891 extra_data = ted;
893 /* Pack group list into extra data fields */
894 for (i = 0; i < groups.num_sam_entries; i++) {
895 char *group_name = ((struct acct_info *)
896 groups.sam_entries)[i].acct_name;
897 fstring name;
899 fill_domain_username(name, domain->name, group_name);
900 /* Append to extra data */
901 memcpy(&extra_data[extra_data_len], name,
902 strlen(name));
903 extra_data_len += strlen(name);
904 extra_data[extra_data_len++] = ',';
907 SAFE_FREE(groups.sam_entries);
910 /* Assign extra_data fields in response structure */
911 if (extra_data) {
912 extra_data[extra_data_len - 1] = '\0';
913 state->response.extra_data = extra_data;
914 state->response.length += extra_data_len;
917 /* No domains may have responded but that's still OK so don't
918 return an error. */
920 return WINBINDD_OK;
923 static BOOL enum_alias_memberships(const DOM_SID *member_sid,
924 DOM_SID **aliases, int *num_aliases)
926 TALLOC_CTX *mem_ctx = talloc_init("enum_alias_memberships");
928 uint32 *rids = NULL;
929 int i, num_rids = 0;
931 BOOL result = False;
933 if (mem_ctx == NULL)
934 return False;
936 *aliases = NULL;
937 *num_aliases = 0;
939 if (!pdb_enum_alias_memberships(mem_ctx, get_global_sam_sid(),
940 member_sid, 1, &rids, &num_rids))
941 goto done;
943 for (i=0; i<num_rids; i++) {
944 DOM_SID alias_sid;
945 sid_copy(&alias_sid, get_global_sam_sid());
946 sid_append_rid(&alias_sid, rids[i]);
947 add_sid_to_array(NULL, &alias_sid, aliases, num_aliases);
950 if (!pdb_enum_alias_memberships(mem_ctx, &global_sid_Builtin,
951 member_sid, 1, &rids, &num_rids))
952 goto done;
954 for (i=0; i<num_rids; i++) {
955 DOM_SID alias_sid;
956 sid_copy(&alias_sid, &global_sid_Builtin);
957 sid_append_rid(&alias_sid, rids[i]);
958 add_sid_to_array(NULL, &alias_sid, aliases, num_aliases);
961 result = True;
962 done:
963 if (mem_ctx != NULL)
964 talloc_destroy(mem_ctx);
966 return result;
969 static void add_local_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
971 gid_t gid;
972 DOM_SID *aliases;
973 int j, num_aliases;
975 DEBUG(10, ("Adding local gids from SID: %s\n",
976 sid_string_static(sid)));
978 /* Don't expand aliases if not explicitly activated -- for now
979 -- jerry */
981 if (!lp_winbind_nested_groups())
982 return;
984 /* Add nested group memberships */
986 if (!enum_alias_memberships(sid, &aliases, &num_aliases))
987 return;
989 for (j=0; j<num_aliases; j++) {
990 enum SID_NAME_USE type;
992 if (!local_sid_to_gid(&gid, &aliases[j], &type)) {
993 DEBUG(1, ("Got an alias membership with no alias\n"));
994 continue;
997 if ((type != SID_NAME_ALIAS) && (type != SID_NAME_WKN_GRP)) {
998 DEBUG(1, ("Got an alias membership in a non-alias\n"));
999 continue;
1002 add_gid_to_array_unique(NULL, gid, gids, num);
1004 SAFE_FREE(aliases);
1007 static void add_gids_from_user_sid(DOM_SID *sid, gid_t **gids, int *num)
1009 DEBUG(10, ("Adding gids from user SID: %s\n",
1010 sid_string_static(sid)));
1012 add_local_gids_from_sid(sid, gids, num);
1015 static void add_gids_from_group_sid(DOM_SID *sid, gid_t **gids, int *num)
1017 gid_t gid;
1019 DEBUG(10, ("Adding gids from group SID: %s\n",
1020 sid_string_static(sid)));
1022 if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
1023 add_gid_to_array_unique(NULL, gid, gids, num);
1025 add_local_gids_from_sid(sid, gids, num);
1028 /* Get user supplementary groups. This is much quicker than trying to
1029 invert the groups database. We merge the groups from the gids and
1030 other_sids info3 fields as trusted domain, universal group
1031 memberships, and nested groups (win2k native mode only) are not
1032 returned by the getgroups RPC call but are present in the info3. */
1034 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
1036 fstring name_domain, name_user;
1037 DOM_SID user_sid, group_sid;
1038 enum SID_NAME_USE name_type;
1039 uint32 num_groups = 0;
1040 uint32 num_gids = 0;
1041 NTSTATUS status;
1042 DOM_SID **user_grpsids;
1043 struct winbindd_domain *domain;
1044 enum winbindd_result result = WINBINDD_ERROR;
1045 gid_t *gid_list = NULL;
1046 unsigned int i;
1047 TALLOC_CTX *mem_ctx;
1048 NET_USER_INFO_3 *info3 = NULL;
1050 /* Ensure null termination */
1051 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
1053 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1054 state->request.data.username));
1056 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1057 state->request.data.username)))
1058 return WINBINDD_ERROR;
1060 /* Parse domain and username */
1062 parse_domain_user(state->request.data.username,
1063 name_domain, name_user);
1065 /* Get info for the domain */
1067 if ((domain = find_domain_from_name(name_domain)) == NULL) {
1068 DEBUG(7, ("could not find domain entry for domain %s\n",
1069 name_domain));
1070 goto done;
1073 if ( domain->primary && lp_winbind_trusted_domains_only()) {
1074 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
1075 name_domain, name_user));
1076 return WINBINDD_ERROR;
1079 /* Get rid and name type from name. The following costs 1 packet */
1081 if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid,
1082 &name_type)) {
1083 DEBUG(4, ("user '%s' does not exist\n", name_user));
1084 goto done;
1087 if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1088 DEBUG(1, ("name '%s' is not a user name: %d\n",
1089 name_user, name_type));
1090 goto done;
1093 add_gids_from_user_sid(&user_sid, &gid_list, &num_gids);
1095 /* Treat the info3 cache as authoritative as the
1096 lookup_usergroups() function may return cached data. */
1098 if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1100 struct winbindd_domain *our_domain = find_our_domain();
1102 if (our_domain == NULL) {
1103 DEBUG(0, ("Could not find our domain\n"));
1104 goto done;
1107 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1108 info3->num_groups2, info3->num_other_sids));
1110 num_groups = info3->num_other_sids + info3->num_groups2;
1112 /* Go through each other sid and convert it to a gid */
1114 for (i = 0; i < info3->num_other_sids; i++) {
1115 DOM_SID *sid = &info3->other_sids[i].sid;
1116 fstring name;
1117 fstring dom_name;
1118 enum SID_NAME_USE sid_type;
1120 /* Is this sid known to us? It can either be
1121 a trusted domain sid or a foreign sid. */
1123 if (!winbindd_lookup_name_by_sid( sid, dom_name,
1124 name, &sid_type)) {
1125 DEBUG(10, ("winbindd_getgroups: could not "
1126 "lookup name for %s\n",
1127 sid_string_static(sid)));
1128 continue;
1131 /* Check it is a domain group or an alias (domain
1132 local group) in a win2k native mode domain. */
1134 if (!((sid_type==SID_NAME_DOM_GRP) ||
1135 ((sid_type==SID_NAME_ALIAS) &&
1136 (our_domain->active_directory) &&
1137 (our_domain->native_mode) &&
1138 (sid_compare_domain(sid, &our_domain->sid)
1139 == 0)))) {
1140 DEBUG(10, ("winbindd_getgroups: sid type %d "
1141 "for %s is not a domain group\n",
1142 sid_type, sid_string_static(sid)));
1143 continue;
1146 add_gids_from_group_sid(sid, &gid_list, &num_gids);
1149 for (i = 0; i < info3->num_groups2; i++) {
1151 /* create the group SID */
1153 sid_copy( &group_sid, &domain->sid );
1154 sid_append_rid( &group_sid, info3->gids[i].g_rid );
1156 add_gids_from_group_sid(&group_sid, &gid_list,
1157 &num_gids);
1160 SAFE_FREE(info3);
1162 } else {
1163 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1164 &user_sid, &num_groups,
1165 &user_grpsids);
1166 if (!NT_STATUS_IS_OK(status))
1167 goto done;
1169 if (state->response.extra_data)
1170 goto done;
1172 for (i = 0; i < num_groups; i++) {
1173 add_gids_from_group_sid(user_grpsids[i],
1174 &gid_list, &num_gids);
1178 /* We want at least one group... */
1179 if (gid_list == NULL)
1180 goto done;
1182 remove_duplicate_gids( &num_gids, gid_list );
1184 /* Send data back to client */
1186 state->response.data.num_entries = num_gids;
1187 state->response.extra_data = gid_list;
1188 state->response.length += num_gids * sizeof(gid_t);
1190 result = WINBINDD_OK;
1192 done:
1194 talloc_destroy(mem_ctx);
1196 return result;
1199 static void add_sid_to_parray_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1200 DOM_SID ***sids, int *num_sids)
1202 int i;
1204 for (i=0; i<(*num_sids); i++) {
1205 if (sid_compare(sid, (*sids)[i]) == 0)
1206 return;
1209 *sids = TALLOC_REALLOC_ARRAY(mem_ctx, *sids, DOM_SID *, *num_sids+1);
1211 if (*sids == NULL)
1212 return;
1214 (*sids)[*num_sids] = TALLOC_P(mem_ctx, DOM_SID);
1215 sid_copy((*sids)[*num_sids], sid);
1216 *num_sids += 1;
1217 return;
1220 static void add_local_sids_from_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1221 DOM_SID ***user_grpsids,
1222 int *num_groups)
1224 DOM_SID *aliases = NULL;
1225 int i, num_aliases = 0;
1227 if (!enum_alias_memberships(sid, &aliases, &num_aliases))
1228 return;
1230 if (num_aliases == 0)
1231 return;
1233 for (i=0; i<num_aliases; i++)
1234 add_sid_to_parray_unique(mem_ctx, &aliases[i], user_grpsids,
1235 num_groups);
1237 SAFE_FREE(aliases);
1239 return;
1242 /* Get user supplementary sids. This is equivalent to the
1243 winbindd_getgroups() function but it involves a SID->SIDs mapping
1244 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1245 idmap. This call is designed to be used with applications that need
1246 to do ACL evaluation themselves. Note that the cached info3 data is
1247 not used
1249 this function assumes that the SID that comes in is a user SID. If
1250 you pass in another type of SID then you may get unpredictable
1251 results.
1253 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1255 DOM_SID user_sid;
1256 NTSTATUS status;
1257 DOM_SID **user_grpsids;
1258 struct winbindd_domain *domain;
1259 enum winbindd_result result = WINBINDD_ERROR;
1260 unsigned int i;
1261 TALLOC_CTX *mem_ctx;
1262 char *ret = NULL;
1263 uint32 num_groups;
1264 unsigned ofs, ret_size = 0;
1266 /* Ensure null termination */
1267 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1269 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1270 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1271 return WINBINDD_ERROR;
1274 if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1275 state->request.data.username))) {
1276 return WINBINDD_ERROR;
1279 /* Get info for the domain */
1280 if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1281 DEBUG(0,("could not find domain entry for sid %s\n",
1282 sid_string_static(&user_sid)));
1283 goto done;
1286 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1287 &user_sid, &num_groups,
1288 &user_grpsids);
1289 if (!NT_STATUS_IS_OK(status))
1290 goto done;
1292 if (num_groups == 0) {
1293 goto no_groups;
1296 domain = find_our_domain();
1298 if (domain == NULL) {
1299 DEBUG(0, ("Could not find our domain\n"));
1300 goto done;
1303 /* Note that I do not check for AD or its mode. XP in a real NT4
1304 * domain also asks for this info. -- vl */
1306 if (!IS_DC) {
1307 uint32 *alias_rids = NULL;
1308 int num_aliases;
1310 /* We need to include the user SID to expand */
1311 user_grpsids = TALLOC_REALLOC_ARRAY(mem_ctx, user_grpsids,
1312 DOM_SID *, num_groups+1);
1313 user_grpsids[num_groups] = &user_sid;
1315 status = domain->methods->lookup_useraliases(domain, mem_ctx,
1316 num_groups+1,
1317 user_grpsids,
1318 &num_aliases,
1319 &alias_rids);
1321 if (!NT_STATUS_IS_OK(status)) {
1322 DEBUG(3, ("Could not expand alias sids: %s\n",
1323 nt_errstr(status)));
1324 goto done;
1327 for (i=0; i<num_aliases; i++) {
1328 DOM_SID sid;
1329 sid_copy(&sid, &domain->sid);
1330 sid_append_rid(&sid, alias_rids[i]);
1331 add_sid_to_parray_unique(mem_ctx, &sid, &user_grpsids,
1332 &num_groups);
1336 if (lp_winbind_nested_groups()) {
1337 int k;
1338 /* num_groups is changed during the loop, that's why we have
1339 to count down here.*/
1341 for (k=num_groups-1; k>=0; k--) {
1342 add_local_sids_from_sid(mem_ctx, user_grpsids[k],
1343 &user_grpsids, &num_groups);
1346 add_local_sids_from_sid(mem_ctx, &user_sid, &user_grpsids,
1347 &num_groups);
1350 /* work out the response size */
1351 for (i = 0; i < num_groups; i++) {
1352 const char *s = sid_string_static(user_grpsids[i]);
1353 ret_size += strlen(s) + 1;
1356 /* build the reply */
1357 ret = SMB_MALLOC(ret_size);
1358 if (!ret) goto done;
1359 ofs = 0;
1360 for (i = 0; i < num_groups; i++) {
1361 const char *s = sid_string_static(user_grpsids[i]);
1362 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1363 ofs += strlen(ret+ofs) + 1;
1366 no_groups:
1367 /* Send data back to client */
1368 state->response.data.num_entries = num_groups;
1369 state->response.extra_data = ret;
1370 state->response.length += ret_size;
1371 result = WINBINDD_OK;
1373 done:
1374 talloc_destroy(mem_ctx);
1376 return result;