Ok, one of the latest cleanups did too much... :-)
[Samba/bb.git] / source / nsswitch / winbindd_group.c
blob74ede8621bb7e4721ef67638b129b7157eac98c5
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 /* HACK ALERT!! This whole routine does not cope with group members
109 * from more than one domain, ie aliases. Thus we have to work it out
110 * ourselves in a special routine. */
112 if (domain->internal)
113 return fill_passdb_alias_grmem(domain, group_sid,
114 num_gr_mem,
115 gr_mem, gr_mem_len);
117 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
118 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
120 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
121 sid_to_string(sid_string, group_sid), domain->name,
122 group_name_type));
123 goto done;
126 /* Lookup group members */
127 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
128 &sid_mem, &names, &name_types);
129 if (!NT_STATUS_IS_OK(status)) {
130 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
131 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
133 goto done;
136 DEBUG(10, ("looked up %d names\n", num_names));
138 if (DEBUGLEVEL >= 10) {
139 for (i = 0; i < num_names; i++)
140 DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
141 name_types[i]));
144 /* Add members to list */
146 buf = NULL;
147 buf_len = buf_ndx = 0;
149 again:
151 for (i = 0; i < num_names; i++) {
152 char *the_name;
153 fstring name;
154 int len;
156 the_name = names[i];
158 DEBUG(10, ("processing name %s\n", the_name));
160 /* FIXME: need to cope with groups within groups. These
161 occur in Universal groups on a Windows 2000 native mode
162 server. */
164 /* make sure to allow machine accounts */
166 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
167 DEBUG(3, ("name %s isn't a domain user\n", the_name));
168 continue;
171 /* Append domain name */
173 fill_domain_username(name, domain->name, the_name);
175 len = strlen(name);
177 /* Add to list or calculate buffer length */
179 if (!buf) {
180 buf_len += len + 1; /* List is comma separated */
181 (*num_gr_mem)++;
182 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
183 } else {
184 DEBUG(10, ("appending %s at ndx %d\n", name, len));
185 safe_strcpy(&buf[buf_ndx], name, len);
186 buf_ndx += len;
187 buf[buf_ndx] = ',';
188 buf_ndx++;
192 /* Allocate buffer */
194 if (!buf && buf_len != 0) {
195 if (!(buf = malloc(buf_len))) {
196 DEBUG(1, ("out of memory\n"));
197 result = False;
198 goto done;
200 memset(buf, 0, buf_len);
201 goto again;
204 if (buf && buf_ndx > 0) {
205 buf[buf_ndx - 1] = '\0';
208 *gr_mem = buf;
209 *gr_mem_len = buf_len;
211 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
212 buf_len, *num_gr_mem ? buf : "NULL"));
213 result = True;
215 done:
217 talloc_destroy(mem_ctx);
219 DEBUG(10, ("fill_grent_mem returning %d\n", result));
221 return result;
224 /* Return a group structure from a group name */
226 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
228 DOM_SID group_sid;
229 WINBINDD_GR *grp;
230 struct winbindd_domain *domain;
231 enum SID_NAME_USE name_type;
232 fstring name_domain, name_group;
233 char *tmp, *gr_mem;
234 int gr_mem_len;
235 gid_t gid;
237 /* Ensure null termination */
238 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
240 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
241 state->request.data.groupname));
243 /* Parse domain and groupname */
245 memset(name_group, 0, sizeof(fstring));
247 tmp = state->request.data.groupname;
249 parse_domain_user(tmp, name_domain, name_group);
251 /* if no domain or our local domain, then do a local tdb search */
253 if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) &&
254 ((grp = wb_getgrnam(name_group)) != NULL) ) {
256 char *buffer = NULL;
258 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
260 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
262 state->response.data.gr.gr_mem_ofs = 0;
263 state->response.length += gr_mem_len;
264 state->response.extra_data = buffer; /* give the memory away */
266 return WINBINDD_OK;
269 /* if no domain or our local domain and no local tdb group, default to
270 * our local domain for aliases */
272 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
273 fstrcpy(name_domain, get_global_sam_name());
276 /* Get info for the domain */
278 if ((domain = find_domain_from_name(name_domain)) == NULL) {
279 DEBUG(3, ("could not get domain sid for domain %s\n",
280 name_domain));
281 return WINBINDD_ERROR;
283 /* should we deal with users for our domain? */
285 if ( lp_winbind_trusted_domains_only() && domain->primary) {
286 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
287 name_domain, name_group));
288 return WINBINDD_ERROR;
291 /* Get rid and name type from name */
293 if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
294 &name_type)) {
295 DEBUG(1, ("group %s in domain %s does not exist\n",
296 name_group, name_domain));
297 return WINBINDD_ERROR;
300 if ( !((name_type==SID_NAME_DOM_GRP) ||
301 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
302 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
304 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
305 name_group, name_type));
306 return WINBINDD_ERROR;
309 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
310 DEBUG(1, ("error converting unix gid to sid\n"));
311 return WINBINDD_ERROR;
314 if (!fill_grent(&state->response.data.gr, name_domain,
315 name_group, gid) ||
316 !fill_grent_mem(domain, &group_sid, name_type,
317 &state->response.data.gr.num_gr_mem,
318 &gr_mem, &gr_mem_len)) {
319 return WINBINDD_ERROR;
322 /* Group membership lives at start of extra data */
324 state->response.data.gr.gr_mem_ofs = 0;
326 state->response.length += gr_mem_len;
327 state->response.extra_data = gr_mem;
329 return WINBINDD_OK;
332 /* Return a group structure from a gid number */
334 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
336 struct winbindd_domain *domain;
337 WINBINDD_GR *grp;
338 DOM_SID group_sid;
339 enum SID_NAME_USE name_type;
340 fstring dom_name;
341 fstring group_name;
342 int gr_mem_len;
343 char *gr_mem;
345 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
346 (unsigned long)state->request.data.gid));
348 /* Bug out if the gid isn't in the winbind range */
350 if ((state->request.data.gid < server_state.gid_low) ||
351 (state->request.data.gid > server_state.gid_high))
352 return WINBINDD_ERROR;
354 /* alway try local tdb lookup first */
355 if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
356 char *buffer = NULL;
358 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
360 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
362 state->response.data.gr.gr_mem_ofs = 0;
363 state->response.length += gr_mem_len;
364 state->response.extra_data = buffer; /* give away the memory */
366 return WINBINDD_OK;
369 /* Get rid from gid */
370 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
371 DEBUG(1, ("could not convert gid %lu to rid\n",
372 (unsigned long)state->request.data.gid));
373 return WINBINDD_ERROR;
376 /* Get name from sid */
378 if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
379 DEBUG(1, ("could not lookup sid\n"));
380 return WINBINDD_ERROR;
383 /* Fill in group structure */
385 domain = find_domain_from_sid(&group_sid);
387 if (!domain) {
388 DEBUG(1,("Can't find domain from sid\n"));
389 return WINBINDD_ERROR;
392 if ( !((name_type==SID_NAME_DOM_GRP) ||
393 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
394 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
396 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
397 group_name, name_type));
398 return WINBINDD_ERROR;
401 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
402 state->request.data.gid) ||
403 !fill_grent_mem(domain, &group_sid, name_type,
404 &state->response.data.gr.num_gr_mem,
405 &gr_mem, &gr_mem_len))
406 return WINBINDD_ERROR;
408 /* Group membership lives at start of extra data */
410 state->response.data.gr.gr_mem_ofs = 0;
412 state->response.length += gr_mem_len;
413 state->response.extra_data = gr_mem;
415 return WINBINDD_OK;
419 * set/get/endgrent functions
422 /* "Rewind" file pointer for group database enumeration */
424 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
426 struct winbindd_domain *domain;
428 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
430 /* Check user has enabled this */
432 if (!lp_winbind_enum_groups())
433 return WINBINDD_ERROR;
435 /* Free old static data if it exists */
437 if (state->getgrent_state != NULL) {
438 free_getent_state(state->getgrent_state);
439 state->getgrent_state = NULL;
442 /* Create sam pipes for each domain we know about */
444 for (domain = domain_list(); domain != NULL; domain = domain->next) {
445 struct getent_state *domain_state;
448 /* don't add our domaina if we are a PDC or if we
449 are a member of a Samba domain */
451 if ( (IS_DC || lp_winbind_trusted_domains_only())
452 && domain->primary )
454 continue;
457 /* Create a state record for this domain */
459 if ((domain_state = (struct getent_state *)
460 malloc(sizeof(struct getent_state))) == NULL) {
461 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
462 return WINBINDD_ERROR;
465 ZERO_STRUCTP(domain_state);
467 fstrcpy(domain_state->domain_name, domain->name);
469 /* Add to list of open domains */
471 DLIST_ADD(state->getgrent_state, domain_state);
474 return WINBINDD_OK;
477 /* Close file pointer to ntdom group database */
479 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
481 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
483 free_getent_state(state->getgrent_state);
484 state->getgrent_state = NULL;
486 return WINBINDD_OK;
489 /* Get the list of domain groups and domain aliases for a domain. We fill in
490 the sam_entries and num_sam_entries fields with domain group information.
491 The dispinfo_ndx field is incremented to the index of the next group to
492 fetch. Return True if some groups were returned, False otherwise. */
494 #define MAX_FETCH_SAM_ENTRIES 100
496 static BOOL get_sam_group_entries(struct getent_state *ent)
498 NTSTATUS status;
499 uint32 num_entries;
500 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
501 TALLOC_CTX *mem_ctx;
502 BOOL result = False;
503 struct acct_info *sam_grp_entries = NULL;
504 struct winbindd_domain *domain;
506 if (ent->got_sam_entries)
507 return False;
509 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
510 ent->domain_name))) {
511 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
512 return False;
515 /* Free any existing group info */
517 SAFE_FREE(ent->sam_entries);
518 ent->num_sam_entries = 0;
519 ent->got_sam_entries = True;
521 /* Enumerate domain groups */
523 num_entries = 0;
525 if (!(domain = find_domain_from_name(ent->domain_name))) {
526 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
527 goto done;
530 /* always get the domain global groups */
532 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
534 if (!NT_STATUS_IS_OK(status)) {
535 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
536 result = False;
537 goto done;
540 /* Copy entries into return buffer */
542 if (num_entries) {
543 if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
544 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
545 num_entries));
546 result = False;
547 goto done;
549 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
552 ent->num_sam_entries = num_entries;
554 /* get the domain local groups if we are a member of a native win2k domain
555 and are not using LDAP to get the groups */
557 if ( ( lp_security() != SEC_ADS && domain->native_mode
558 && domain->primary) || domain->internal )
560 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
562 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
564 if ( !NT_STATUS_IS_OK(status) ) {
565 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
566 num_entries = 0;
568 else
569 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
571 /* Copy entries into return buffer */
573 if ( num_entries ) {
574 if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
576 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
577 num_entries));
578 result = False;
579 SAFE_FREE( name_list );
580 goto done;
583 name_list = tmp_name_list;
585 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
586 num_entries * sizeof(struct acct_info) );
589 ent->num_sam_entries += num_entries;
593 /* Fill in remaining fields */
595 ent->sam_entries = name_list;
596 ent->sam_entry_index = 0;
598 result = (ent->num_sam_entries > 0);
600 done:
601 talloc_destroy(mem_ctx);
603 return result;
606 /* Fetch next group entry from ntdom database */
608 #define MAX_GETGRENT_GROUPS 500
610 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
612 struct getent_state *ent;
613 struct winbindd_gr *group_list = NULL;
614 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
615 char *new_extra_data, *gr_mem_list = NULL;
617 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
619 /* Check user has enabled this */
621 if (!lp_winbind_enum_groups())
622 return WINBINDD_ERROR;
624 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
626 if ((state->response.extra_data =
627 malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
628 return WINBINDD_ERROR;
630 state->response.data.num_entries = 0;
632 group_list = (struct winbindd_gr *)state->response.extra_data;
634 if (!(ent = state->getgrent_state))
635 return WINBINDD_ERROR;
637 /* Start sending back groups */
639 for (i = 0; i < num_groups; i++) {
640 struct acct_info *name_list = NULL;
641 fstring domain_group_name;
642 uint32 result;
643 gid_t group_gid;
644 int gr_mem_len;
645 char *gr_mem, *new_gr_mem_list;
646 DOM_SID group_sid;
647 struct winbindd_domain *domain;
649 /* Do we need to fetch another chunk of groups? */
651 tryagain:
653 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
654 ent->sam_entry_index, ent->num_sam_entries));
656 if (ent->num_sam_entries == ent->sam_entry_index) {
658 while(ent && !get_sam_group_entries(ent)) {
659 struct getent_state *next_ent;
661 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
663 /* Free state information for this domain */
665 SAFE_FREE(ent->sam_entries);
667 next_ent = ent->next;
668 DLIST_REMOVE(state->getgrent_state, ent);
670 SAFE_FREE(ent);
671 ent = next_ent;
674 /* No more domains */
676 if (!ent)
677 break;
680 name_list = ent->sam_entries;
682 if (!(domain =
683 find_domain_from_name(ent->domain_name))) {
684 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
685 result = False;
686 goto done;
689 /* Lookup group info */
691 sid_copy(&group_sid, &domain->sid);
692 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
694 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
696 DEBUG(1, ("could not look up gid for group %s\n",
697 name_list[ent->sam_entry_index].acct_name));
699 ent->sam_entry_index++;
700 goto tryagain;
703 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
704 (unsigned long)name_list[ent->sam_entry_index].rid));
706 /* Fill in group entry */
708 fill_domain_username(domain_group_name, ent->domain_name,
709 name_list[ent->sam_entry_index].acct_name);
711 result = fill_grent(&group_list[group_list_ndx],
712 ent->domain_name,
713 name_list[ent->sam_entry_index].acct_name,
714 group_gid);
716 /* Fill in group membership entry */
718 if (result) {
719 DOM_SID member_sid;
720 group_list[group_list_ndx].num_gr_mem = 0;
721 gr_mem = NULL;
722 gr_mem_len = 0;
724 /* Get group membership */
725 if (state->request.cmd == WINBINDD_GETGRLST) {
726 result = True;
727 } else {
728 sid_copy(&member_sid, &domain->sid);
729 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
730 result = fill_grent_mem(
731 domain,
732 &member_sid,
733 SID_NAME_DOM_GRP,
734 &group_list[group_list_ndx].num_gr_mem,
735 &gr_mem, &gr_mem_len);
739 if (result) {
740 /* Append to group membership list */
741 new_gr_mem_list = Realloc(
742 gr_mem_list,
743 gr_mem_list_len + gr_mem_len);
745 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
746 DEBUG(0, ("out of memory\n"));
747 SAFE_FREE(gr_mem_list);
748 gr_mem_list_len = 0;
749 break;
752 DEBUG(10, ("list_len = %d, mem_len = %d\n",
753 gr_mem_list_len, gr_mem_len));
755 gr_mem_list = new_gr_mem_list;
757 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
758 gr_mem_len);
760 SAFE_FREE(gr_mem);
762 group_list[group_list_ndx].gr_mem_ofs =
763 gr_mem_list_len;
765 gr_mem_list_len += gr_mem_len;
768 ent->sam_entry_index++;
770 /* Add group to return list */
772 if (result) {
774 DEBUG(10, ("adding group num_entries = %d\n",
775 state->response.data.num_entries));
777 group_list_ndx++;
778 state->response.data.num_entries++;
780 state->response.length +=
781 sizeof(struct winbindd_gr);
783 } else {
784 DEBUG(0, ("could not lookup domain group %s\n",
785 domain_group_name));
789 /* Copy the list of group memberships to the end of the extra data */
791 if (group_list_ndx == 0)
792 goto done;
794 new_extra_data = Realloc(
795 state->response.extra_data,
796 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
798 if (!new_extra_data) {
799 DEBUG(0, ("out of memory\n"));
800 group_list_ndx = 0;
801 SAFE_FREE(state->response.extra_data);
802 SAFE_FREE(gr_mem_list);
804 return WINBINDD_ERROR;
807 state->response.extra_data = new_extra_data;
809 memcpy(&((char *)state->response.extra_data)
810 [group_list_ndx * sizeof(struct winbindd_gr)],
811 gr_mem_list, gr_mem_list_len);
813 SAFE_FREE(gr_mem_list);
815 state->response.length += gr_mem_list_len;
817 DEBUG(10, ("returning %d groups, length = %d\n",
818 group_list_ndx, gr_mem_list_len));
820 /* Out of domains */
822 done:
824 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
827 /* List domain groups without mapping to unix ids */
829 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
831 uint32 total_entries = 0;
832 struct winbindd_domain *domain;
833 const char *which_domain;
834 char *extra_data = NULL;
835 char *ted = NULL;
836 unsigned int extra_data_len = 0, i;
838 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
840 /* Ensure null termination */
841 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
842 which_domain = state->request.domain_name;
844 /* Enumerate over trusted domains */
846 for (domain = domain_list(); domain; domain = domain->next) {
847 struct getent_state groups;
849 /* if we have a domain name restricting the request and this
850 one in the list doesn't match, then just bypass the remainder
851 of the loop */
853 if ( *which_domain && !strequal(which_domain, domain->name) )
854 continue;
856 ZERO_STRUCT(groups);
858 /* Get list of sam groups */
860 fstrcpy(groups.domain_name, domain->name);
862 get_sam_group_entries(&groups);
864 if (groups.num_sam_entries == 0) {
865 /* this domain is empty or in an error state */
866 continue;
869 /* keep track the of the total number of groups seen so
870 far over all domains */
871 total_entries += groups.num_sam_entries;
873 /* Allocate some memory for extra data. Note that we limit
874 account names to sizeof(fstring) = 128 characters. */
875 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
877 if (!ted) {
878 DEBUG(0,("failed to enlarge buffer!\n"));
879 SAFE_FREE(extra_data);
880 return WINBINDD_ERROR;
881 } else
882 extra_data = ted;
884 /* Pack group list into extra data fields */
885 for (i = 0; i < groups.num_sam_entries; i++) {
886 char *group_name = ((struct acct_info *)
887 groups.sam_entries)[i].acct_name;
888 fstring name;
890 fill_domain_username(name, domain->name, group_name);
891 /* Append to extra data */
892 memcpy(&extra_data[extra_data_len], name,
893 strlen(name));
894 extra_data_len += strlen(name);
895 extra_data[extra_data_len++] = ',';
898 SAFE_FREE(groups.sam_entries);
901 /* Assign extra_data fields in response structure */
902 if (extra_data) {
903 extra_data[extra_data_len - 1] = '\0';
904 state->response.extra_data = extra_data;
905 state->response.length += extra_data_len;
908 /* No domains may have responded but that's still OK so don't
909 return an error. */
911 return WINBINDD_OK;
914 static void add_gid_to_array_unique(gid_t gid, gid_t **gids, int *num)
916 int i;
918 if ((*num) >= groups_max())
919 return;
921 for (i=0; i<*num; i++) {
922 if ((*gids)[i] == gid)
923 return;
926 *gids = Realloc(*gids, (*num+1) * sizeof(gid_t));
928 if (*gids == NULL)
929 return;
931 (*gids)[*num] = gid;
932 *num += 1;
935 static void add_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
937 gid_t gid;
938 DOM_SID *aliases;
939 int j, num_aliases;
941 DEBUG(10, ("Adding gids from SID: %s\n", sid_string_static(sid)));
943 if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
944 add_gid_to_array_unique(gid, gids, num);
946 /* Add nested group memberships */
948 if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases))
949 return;
951 for (j=0; j<num_aliases; j++) {
953 if (!NT_STATUS_IS_OK(sid_to_gid(&aliases[j], &gid)))
954 continue;
956 add_gid_to_array_unique(gid, gids, num);
958 SAFE_FREE(aliases);
961 /* Get user supplementary groups. This is much quicker than trying to
962 invert the groups database. We merge the groups from the gids and
963 other_sids info3 fields as trusted domain, universal group
964 memberships, and nested groups (win2k native mode only) are not
965 returned by the getgroups RPC call but are present in the info3. */
967 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
969 fstring name_domain, name_user;
970 DOM_SID user_sid, group_sid;
971 enum SID_NAME_USE name_type;
972 uint32 num_groups = 0;
973 uint32 num_gids = 0;
974 NTSTATUS status;
975 DOM_SID **user_grpsids;
976 struct winbindd_domain *domain;
977 enum winbindd_result result = WINBINDD_ERROR;
978 gid_t *gid_list = NULL;
979 unsigned int i;
980 TALLOC_CTX *mem_ctx;
981 NET_USER_INFO_3 *info3 = NULL;
983 /* Ensure null termination */
984 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
986 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
987 state->request.data.username));
989 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
990 state->request.data.username)))
991 return WINBINDD_ERROR;
993 /* Parse domain and username */
995 parse_domain_user(state->request.data.username,
996 name_domain, name_user);
998 /* Get info for the domain */
1000 if ((domain = find_domain_from_name(name_domain)) == NULL) {
1001 DEBUG(7, ("could not find domain entry for domain %s\n",
1002 name_domain));
1003 goto done;
1006 if ( domain->primary && lp_winbind_trusted_domains_only()) {
1007 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
1008 name_domain, name_user));
1009 return WINBINDD_ERROR;
1012 /* Get rid and name type from name. The following costs 1 packet */
1014 if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
1015 &name_type)) {
1016 DEBUG(1, ("user '%s' does not exist\n", name_user));
1017 goto done;
1020 if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1021 DEBUG(1, ("name '%s' is not a user name: %d\n",
1022 name_user, name_type));
1023 goto done;
1026 add_gids_from_sid(&user_sid, &gid_list, &num_gids);
1028 /* Treat the info3 cache as authoritative as the
1029 lookup_usergroups() function may return cached data. */
1031 if ((info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1033 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1034 info3->num_groups2, info3->num_other_sids));
1036 num_groups = info3->num_other_sids + info3->num_groups2;
1038 /* Go through each other sid and convert it to a gid */
1040 for (i = 0; i < info3->num_other_sids; i++) {
1041 fstring name;
1042 fstring dom_name;
1043 enum SID_NAME_USE sid_type;
1045 /* Is this sid known to us? It can either be
1046 a trusted domain sid or a foreign sid. */
1048 if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
1049 dom_name, name, &sid_type))
1051 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
1052 sid_string_static(&info3->other_sids[i].sid)));
1053 continue;
1056 /* Check it is a domain group or an alias (domain local group)
1057 in a win2k native mode domain. */
1059 if ( !((sid_type==SID_NAME_DOM_GRP) ||
1060 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1062 DEBUG(10, ("winbindd_getgroups: sid type %d "
1063 "for %s is not a domain group\n",
1064 sid_type,
1065 sid_string_static(
1066 &info3->other_sids[i].sid)));
1067 continue;
1070 add_gids_from_sid(&info3->other_sids[i].sid,
1071 &gid_list, &num_gids);
1073 if (gid_list == NULL)
1074 goto done;
1077 for (i = 0; i < info3->num_groups2; i++) {
1079 /* create the group SID */
1081 sid_copy( &group_sid, &domain->sid );
1082 sid_append_rid( &group_sid, info3->gids[i].g_rid );
1084 add_gids_from_sid(&group_sid, &gid_list, &num_gids);
1086 if (gid_list == NULL)
1087 goto done;
1090 SAFE_FREE(info3);
1092 } else {
1093 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1094 &user_sid, &num_groups,
1095 &user_grpsids);
1096 if (!NT_STATUS_IS_OK(status))
1097 goto done;
1099 gid_list = malloc(sizeof(gid_t) * num_groups);
1101 if (state->response.extra_data)
1102 goto done;
1104 for (i = 0; i < num_groups; i++) {
1105 add_gids_from_sid(user_grpsids[i],
1106 &gid_list, &num_gids);
1108 if (gid_list == NULL)
1109 goto done;
1113 /* Send data back to client */
1115 state->response.data.num_entries = num_gids;
1116 state->response.extra_data = gid_list;
1117 state->response.length += num_gids * sizeof(gid_t);
1119 result = WINBINDD_OK;
1121 done:
1123 talloc_destroy(mem_ctx);
1125 return result;
1129 /* Get user supplementary sids. This is equivalent to the
1130 winbindd_getgroups() function but it involves a SID->SIDs mapping
1131 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1132 idmap. This call is designed to be used with applications that need
1133 to do ACL evaluation themselves. Note that the cached info3 data is
1134 not used
1136 this function assumes that the SID that comes in is a user SID. If
1137 you pass in another type of SID then you may get unpredictable
1138 results.
1140 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1142 DOM_SID user_sid;
1143 NTSTATUS status;
1144 DOM_SID **user_grpsids;
1145 struct winbindd_domain *domain;
1146 enum winbindd_result result = WINBINDD_ERROR;
1147 unsigned int i;
1148 TALLOC_CTX *mem_ctx;
1149 char *ret = NULL;
1150 uint32 num_groups;
1151 unsigned ofs, ret_size = 0;
1153 /* Ensure null termination */
1154 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1156 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1157 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1158 return WINBINDD_ERROR;
1161 if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1162 state->request.data.username))) {
1163 return WINBINDD_ERROR;
1166 /* Get info for the domain */
1167 if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1168 DEBUG(0,("could not find domain entry for sid %s\n",
1169 sid_string_static(&user_sid)));
1170 goto done;
1173 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1174 &user_sid, &num_groups,
1175 &user_grpsids);
1176 if (!NT_STATUS_IS_OK(status))
1177 goto done;
1179 if (num_groups == 0) {
1180 goto no_groups;
1183 /* work out the response size */
1184 for (i = 0; i < num_groups; i++) {
1185 const char *s = sid_string_static(user_grpsids[i]);
1186 ret_size += strlen(s) + 1;
1189 /* build the reply */
1190 ret = malloc(ret_size);
1191 if (!ret) goto done;
1192 ofs = 0;
1193 for (i = 0; i < num_groups; i++) {
1194 const char *s = sid_string_static(user_grpsids[i]);
1195 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1196 ofs += strlen(ret+ofs) + 1;
1199 no_groups:
1200 /* Send data back to client */
1201 state->response.data.num_entries = num_groups;
1202 state->response.extra_data = ret;
1203 state->response.length += ret_size;
1204 result = WINBINDD_OK;
1206 done:
1207 talloc_destroy(mem_ctx);
1209 return result;