r989: Calling sid_to_gid from within winbind makes no sense, as this calls
[Samba/bb.git] / source3 / nsswitch / winbindd_group.c
blob346a2711b6c8b75aa8a13f6e5b4416e3b4d5c954
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
26 #include "winbindd.h"
28 extern BOOL opt_nocache;
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 /*********************************************************************
34 *********************************************************************/
36 static int gr_mem_buffer( char **buffer, char **members, int num_members )
38 int i;
39 int len = 0;
40 int idx = 0;
42 if ( num_members == 0 ) {
43 *buffer = NULL;
44 return 0;
47 for ( i=0; i<num_members; i++ )
48 len += strlen(members[i])+1;
50 *buffer = (char*)smb_xmalloc(len);
51 for ( i=0; i<num_members; i++ ) {
52 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
53 idx += strlen(members[i])+1;
55 /* terminate with NULL */
56 (*buffer)[len-1] = '\0';
58 return len;
61 /***************************************************************
62 Empty static struct for negative caching.
63 ****************************************************************/
65 /* Fill a grent structure from various other information */
67 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
68 const char *gr_name, gid_t unix_gid)
70 fstring full_group_name;
71 /* Fill in uid/gid */
72 fill_domain_username(full_group_name, dom_name, gr_name);
74 gr->gr_gid = unix_gid;
76 /* Group name and password */
78 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
79 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
81 return True;
84 /* Fill in the group membership field of a NT group given by group_sid */
86 static BOOL fill_grent_mem(struct winbindd_domain *domain,
87 DOM_SID *group_sid,
88 enum SID_NAME_USE group_name_type,
89 int *num_gr_mem, char **gr_mem, int *gr_mem_len)
91 DOM_SID **sid_mem = NULL;
92 uint32 num_names = 0;
93 uint32 *name_types = NULL;
94 unsigned int buf_len, buf_ndx, i;
95 char **names = NULL, *buf;
96 BOOL result = False;
97 TALLOC_CTX *mem_ctx;
98 NTSTATUS status;
99 fstring sid_string;
101 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
102 return False;
104 /* Initialise group membership information */
106 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
108 *num_gr_mem = 0;
110 /* HACK ALERT!! This whole routine does not cope with group members
111 * from more than one domain, ie aliases. Thus we have to work it out
112 * ourselves in a special routine. */
114 if (domain->internal)
115 return fill_passdb_alias_grmem(domain, group_sid,
116 num_gr_mem,
117 gr_mem, gr_mem_len);
119 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
120 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
122 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
123 sid_to_string(sid_string, group_sid), domain->name,
124 group_name_type));
125 goto done;
128 /* Lookup group members */
129 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
130 &sid_mem, &names, &name_types);
131 if (!NT_STATUS_IS_OK(status)) {
132 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
133 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
135 goto done;
138 DEBUG(10, ("looked up %d names\n", num_names));
140 if (DEBUGLEVEL >= 10) {
141 for (i = 0; i < num_names; i++)
142 DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
143 name_types[i]));
146 /* Add members to list */
148 buf = NULL;
149 buf_len = buf_ndx = 0;
151 again:
153 for (i = 0; i < num_names; i++) {
154 char *the_name;
155 fstring name;
156 int len;
158 the_name = names[i];
160 DEBUG(10, ("processing name %s\n", the_name));
162 /* FIXME: need to cope with groups within groups. These
163 occur in Universal groups on a Windows 2000 native mode
164 server. */
166 /* make sure to allow machine accounts */
168 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
169 DEBUG(3, ("name %s isn't a domain user\n", the_name));
170 continue;
173 /* Append domain name */
175 fill_domain_username(name, domain->name, the_name);
177 len = strlen(name);
179 /* Add to list or calculate buffer length */
181 if (!buf) {
182 buf_len += len + 1; /* List is comma separated */
183 (*num_gr_mem)++;
184 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
185 } else {
186 DEBUG(10, ("appending %s at ndx %d\n", name, len));
187 safe_strcpy(&buf[buf_ndx], name, len);
188 buf_ndx += len;
189 buf[buf_ndx] = ',';
190 buf_ndx++;
194 /* Allocate buffer */
196 if (!buf && buf_len != 0) {
197 if (!(buf = malloc(buf_len))) {
198 DEBUG(1, ("out of memory\n"));
199 result = False;
200 goto done;
202 memset(buf, 0, buf_len);
203 goto again;
206 if (buf && buf_ndx > 0) {
207 buf[buf_ndx - 1] = '\0';
210 *gr_mem = buf;
211 *gr_mem_len = buf_len;
213 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
214 buf_len, *num_gr_mem ? buf : "NULL"));
215 result = True;
217 done:
219 talloc_destroy(mem_ctx);
221 DEBUG(10, ("fill_grent_mem returning %d\n", result));
223 return result;
226 /* Return a group structure from a group name */
228 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
230 DOM_SID group_sid;
231 WINBINDD_GR *grp;
232 struct winbindd_domain *domain;
233 enum SID_NAME_USE name_type;
234 fstring name_domain, name_group;
235 char *tmp, *gr_mem;
236 int gr_mem_len;
237 gid_t gid;
239 /* Ensure null termination */
240 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
242 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
243 state->request.data.groupname));
245 /* Parse domain and groupname */
247 memset(name_group, 0, sizeof(fstring));
249 tmp = state->request.data.groupname;
251 parse_domain_user(tmp, name_domain, name_group);
253 /* if no domain or our local domain, then do a local tdb search */
255 if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) &&
256 ((grp = wb_getgrnam(name_group)) != NULL) ) {
258 char *buffer = NULL;
260 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
262 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
264 state->response.data.gr.gr_mem_ofs = 0;
265 state->response.length += gr_mem_len;
266 state->response.extra_data = buffer; /* give the memory away */
268 return WINBINDD_OK;
271 /* if no domain or our local domain and no local tdb group, default to
272 * our local domain for aliases */
274 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
275 fstrcpy(name_domain, get_global_sam_name());
278 /* Get info for the domain */
280 if ((domain = find_domain_from_name(name_domain)) == NULL) {
281 DEBUG(3, ("could not get domain sid for domain %s\n",
282 name_domain));
283 return WINBINDD_ERROR;
285 /* should we deal with users for our domain? */
287 if ( lp_winbind_trusted_domains_only() && domain->primary) {
288 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
289 name_domain, name_group));
290 return WINBINDD_ERROR;
293 /* Get rid and name type from name */
295 if (!winbindd_lookup_sid_by_name(domain, domain->name, name_group, &group_sid,
296 &name_type)) {
297 DEBUG(1, ("group %s in domain %s does not exist\n",
298 name_group, name_domain));
299 return WINBINDD_ERROR;
302 if ( !((name_type==SID_NAME_DOM_GRP) ||
303 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
304 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
306 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
307 name_group, name_type));
308 return WINBINDD_ERROR;
311 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
312 DEBUG(1, ("error converting unix gid to sid\n"));
313 return WINBINDD_ERROR;
316 if (!fill_grent(&state->response.data.gr, name_domain,
317 name_group, gid) ||
318 !fill_grent_mem(domain, &group_sid, name_type,
319 &state->response.data.gr.num_gr_mem,
320 &gr_mem, &gr_mem_len)) {
321 return WINBINDD_ERROR;
324 /* Group membership lives at start of extra data */
326 state->response.data.gr.gr_mem_ofs = 0;
328 state->response.length += gr_mem_len;
329 state->response.extra_data = gr_mem;
331 return WINBINDD_OK;
334 /* Return a group structure from a gid number */
336 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
338 struct winbindd_domain *domain;
339 WINBINDD_GR *grp;
340 DOM_SID group_sid;
341 enum SID_NAME_USE name_type;
342 fstring dom_name;
343 fstring group_name;
344 int gr_mem_len;
345 char *gr_mem;
347 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
348 (unsigned long)state->request.data.gid));
350 /* Bug out if the gid isn't in the winbind range */
352 if ((state->request.data.gid < server_state.gid_low) ||
353 (state->request.data.gid > server_state.gid_high))
354 return WINBINDD_ERROR;
356 /* alway try local tdb lookup first */
357 if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
358 char *buffer = NULL;
360 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
362 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
364 state->response.data.gr.gr_mem_ofs = 0;
365 state->response.length += gr_mem_len;
366 state->response.extra_data = buffer; /* give away the memory */
368 return WINBINDD_OK;
371 /* Get rid from gid */
372 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
373 DEBUG(1, ("could not convert gid %lu to rid\n",
374 (unsigned long)state->request.data.gid));
375 return WINBINDD_ERROR;
378 /* Get name from sid */
380 if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
381 DEBUG(1, ("could not lookup sid\n"));
382 return WINBINDD_ERROR;
385 /* Fill in group structure */
387 domain = find_domain_from_sid(&group_sid);
389 if (!domain) {
390 DEBUG(1,("Can't find domain from sid\n"));
391 return WINBINDD_ERROR;
394 if ( !((name_type==SID_NAME_DOM_GRP) ||
395 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
396 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
398 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
399 group_name, name_type));
400 return WINBINDD_ERROR;
403 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
404 state->request.data.gid) ||
405 !fill_grent_mem(domain, &group_sid, name_type,
406 &state->response.data.gr.num_gr_mem,
407 &gr_mem, &gr_mem_len))
408 return WINBINDD_ERROR;
410 /* Group membership lives at start of extra data */
412 state->response.data.gr.gr_mem_ofs = 0;
414 state->response.length += gr_mem_len;
415 state->response.extra_data = gr_mem;
417 return WINBINDD_OK;
421 * set/get/endgrent functions
424 /* "Rewind" file pointer for group database enumeration */
426 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
428 struct winbindd_domain *domain;
430 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
432 /* Check user has enabled this */
434 if (!lp_winbind_enum_groups())
435 return WINBINDD_ERROR;
437 /* Free old static data if it exists */
439 if (state->getgrent_state != NULL) {
440 free_getent_state(state->getgrent_state);
441 state->getgrent_state = NULL;
444 /* Create sam pipes for each domain we know about */
446 for (domain = domain_list(); domain != NULL; domain = domain->next) {
447 struct getent_state *domain_state;
449 /* Create a state record for this domain */
451 /* don't add our domaina if we are a PDC or if we
452 are a member of a Samba domain */
454 if ( lp_winbind_trusted_domains_only() && domain->primary )
456 continue;
460 if ((domain_state = (struct getent_state *)
461 malloc(sizeof(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 #define MAX_FETCH_SAM_ENTRIES 100
500 static BOOL get_sam_group_entries(struct getent_state *ent)
502 NTSTATUS status;
503 uint32 num_entries;
504 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
505 TALLOC_CTX *mem_ctx;
506 BOOL result = False;
507 struct acct_info *sam_grp_entries = NULL;
508 struct winbindd_domain *domain;
510 if (ent->got_sam_entries)
511 return False;
513 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
514 ent->domain_name))) {
515 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
516 return False;
519 /* Free any existing group info */
521 SAFE_FREE(ent->sam_entries);
522 ent->num_sam_entries = 0;
523 ent->got_sam_entries = True;
525 /* Enumerate domain groups */
527 num_entries = 0;
529 if (!(domain = find_domain_from_name(ent->domain_name))) {
530 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
531 goto done;
534 /* always get the domain global groups */
536 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
538 if (!NT_STATUS_IS_OK(status)) {
539 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
540 result = False;
541 goto done;
544 /* Copy entries into return buffer */
546 if (num_entries) {
547 if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
548 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
549 num_entries));
550 result = False;
551 goto done;
553 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
556 ent->num_sam_entries = num_entries;
558 /* get the domain local groups if we are a member of a native win2k domain
559 and are not using LDAP to get the groups */
561 if ( ( lp_security() != SEC_ADS && domain->native_mode
562 && domain->primary) || domain->internal )
564 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
566 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
568 if ( !NT_STATUS_IS_OK(status) ) {
569 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
570 num_entries = 0;
572 else
573 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
575 /* Copy entries into return buffer */
577 if ( num_entries ) {
578 if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
580 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
581 num_entries));
582 result = False;
583 SAFE_FREE( name_list );
584 goto done;
587 name_list = tmp_name_list;
589 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
590 num_entries * sizeof(struct acct_info) );
593 ent->num_sam_entries += num_entries;
597 /* Fill in remaining fields */
599 ent->sam_entries = name_list;
600 ent->sam_entry_index = 0;
602 result = (ent->num_sam_entries > 0);
604 done:
605 talloc_destroy(mem_ctx);
607 return result;
610 /* Fetch next group entry from ntdom database */
612 #define MAX_GETGRENT_GROUPS 500
614 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
616 struct getent_state *ent;
617 struct winbindd_gr *group_list = NULL;
618 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
619 char *new_extra_data, *gr_mem_list = NULL;
621 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
623 /* Check user has enabled this */
625 if (!lp_winbind_enum_groups())
626 return WINBINDD_ERROR;
628 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
630 if ((state->response.extra_data =
631 malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
632 return WINBINDD_ERROR;
634 memset(state->response.extra_data, '\0',
635 num_groups * sizeof(struct winbindd_gr) );
637 state->response.data.num_entries = 0;
639 group_list = (struct winbindd_gr *)state->response.extra_data;
641 if (!state->getgrent_initialized)
642 winbindd_setgrent(state);
644 if (!(ent = state->getgrent_state))
645 return WINBINDD_ERROR;
647 /* Start sending back groups */
649 for (i = 0; i < num_groups; i++) {
650 struct acct_info *name_list = NULL;
651 fstring domain_group_name;
652 uint32 result;
653 gid_t group_gid;
654 int gr_mem_len;
655 char *gr_mem, *new_gr_mem_list;
656 DOM_SID group_sid;
657 struct winbindd_domain *domain;
659 /* Do we need to fetch another chunk of groups? */
661 tryagain:
663 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
664 ent->sam_entry_index, ent->num_sam_entries));
666 if (ent->num_sam_entries == ent->sam_entry_index) {
668 while(ent && !get_sam_group_entries(ent)) {
669 struct getent_state *next_ent;
671 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
673 /* Free state information for this domain */
675 SAFE_FREE(ent->sam_entries);
677 next_ent = ent->next;
678 DLIST_REMOVE(state->getgrent_state, ent);
680 SAFE_FREE(ent);
681 ent = next_ent;
684 /* No more domains */
686 if (!ent)
687 break;
690 name_list = ent->sam_entries;
692 if (!(domain =
693 find_domain_from_name(ent->domain_name))) {
694 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
695 result = False;
696 goto done;
699 /* Lookup group info */
701 sid_copy(&group_sid, &domain->sid);
702 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
704 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
706 DEBUG(1, ("could not look up gid for group %s\n",
707 name_list[ent->sam_entry_index].acct_name));
709 ent->sam_entry_index++;
710 goto tryagain;
713 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
714 (unsigned long)name_list[ent->sam_entry_index].rid));
716 /* Fill in group entry */
718 fill_domain_username(domain_group_name, ent->domain_name,
719 name_list[ent->sam_entry_index].acct_name);
721 result = fill_grent(&group_list[group_list_ndx],
722 ent->domain_name,
723 name_list[ent->sam_entry_index].acct_name,
724 group_gid);
726 /* Fill in group membership entry */
728 if (result) {
729 DOM_SID member_sid;
730 group_list[group_list_ndx].num_gr_mem = 0;
731 gr_mem = NULL;
732 gr_mem_len = 0;
734 /* Get group membership */
735 if (state->request.cmd == WINBINDD_GETGRLST) {
736 result = True;
737 } else {
738 sid_copy(&member_sid, &domain->sid);
739 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
740 result = fill_grent_mem(
741 domain,
742 &member_sid,
743 SID_NAME_DOM_GRP,
744 &group_list[group_list_ndx].num_gr_mem,
745 &gr_mem, &gr_mem_len);
749 if (result) {
750 /* Append to group membership list */
751 new_gr_mem_list = Realloc(
752 gr_mem_list,
753 gr_mem_list_len + gr_mem_len);
755 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
756 DEBUG(0, ("out of memory\n"));
757 SAFE_FREE(gr_mem_list);
758 gr_mem_list_len = 0;
759 break;
762 DEBUG(10, ("list_len = %d, mem_len = %d\n",
763 gr_mem_list_len, gr_mem_len));
765 gr_mem_list = new_gr_mem_list;
767 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
768 gr_mem_len);
770 SAFE_FREE(gr_mem);
772 group_list[group_list_ndx].gr_mem_ofs =
773 gr_mem_list_len;
775 gr_mem_list_len += gr_mem_len;
778 ent->sam_entry_index++;
780 /* Add group to return list */
782 if (result) {
784 DEBUG(10, ("adding group num_entries = %d\n",
785 state->response.data.num_entries));
787 group_list_ndx++;
788 state->response.data.num_entries++;
790 state->response.length +=
791 sizeof(struct winbindd_gr);
793 } else {
794 DEBUG(0, ("could not lookup domain group %s\n",
795 domain_group_name));
799 /* Copy the list of group memberships to the end of the extra data */
801 if (group_list_ndx == 0)
802 goto done;
804 new_extra_data = Realloc(
805 state->response.extra_data,
806 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
808 if (!new_extra_data) {
809 DEBUG(0, ("out of memory\n"));
810 group_list_ndx = 0;
811 SAFE_FREE(state->response.extra_data);
812 SAFE_FREE(gr_mem_list);
814 return WINBINDD_ERROR;
817 state->response.extra_data = new_extra_data;
819 memcpy(&((char *)state->response.extra_data)
820 [group_list_ndx * sizeof(struct winbindd_gr)],
821 gr_mem_list, gr_mem_list_len);
823 SAFE_FREE(gr_mem_list);
825 state->response.length += gr_mem_list_len;
827 DEBUG(10, ("returning %d groups, length = %d\n",
828 group_list_ndx, gr_mem_list_len));
830 /* Out of domains */
832 done:
834 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
837 /* List domain groups without mapping to unix ids */
839 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
841 uint32 total_entries = 0;
842 struct winbindd_domain *domain;
843 const char *which_domain;
844 char *extra_data = NULL;
845 char *ted = NULL;
846 unsigned int extra_data_len = 0, i;
848 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
850 /* Ensure null termination */
851 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
852 which_domain = state->request.domain_name;
854 /* Enumerate over trusted domains */
856 for (domain = domain_list(); domain; domain = domain->next) {
857 struct getent_state groups;
859 /* if we have a domain name restricting the request and this
860 one in the list doesn't match, then just bypass the remainder
861 of the loop */
863 if ( *which_domain && !strequal(which_domain, domain->name) )
864 continue;
866 ZERO_STRUCT(groups);
868 /* Get list of sam groups */
870 fstrcpy(groups.domain_name, domain->name);
872 get_sam_group_entries(&groups);
874 if (groups.num_sam_entries == 0) {
875 /* this domain is empty or in an error state */
876 continue;
879 /* keep track the of the total number of groups seen so
880 far over all domains */
881 total_entries += groups.num_sam_entries;
883 /* Allocate some memory for extra data. Note that we limit
884 account names to sizeof(fstring) = 128 characters. */
885 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
887 if (!ted) {
888 DEBUG(0,("failed to enlarge buffer!\n"));
889 SAFE_FREE(extra_data);
890 return WINBINDD_ERROR;
891 } else
892 extra_data = ted;
894 /* Pack group list into extra data fields */
895 for (i = 0; i < groups.num_sam_entries; i++) {
896 char *group_name = ((struct acct_info *)
897 groups.sam_entries)[i].acct_name;
898 fstring name;
900 fill_domain_username(name, domain->name, group_name);
901 /* Append to extra data */
902 memcpy(&extra_data[extra_data_len], name,
903 strlen(name));
904 extra_data_len += strlen(name);
905 extra_data[extra_data_len++] = ',';
908 SAFE_FREE(groups.sam_entries);
911 /* Assign extra_data fields in response structure */
912 if (extra_data) {
913 extra_data[extra_data_len - 1] = '\0';
914 state->response.extra_data = extra_data;
915 state->response.length += extra_data_len;
918 /* No domains may have responded but that's still OK so don't
919 return an error. */
921 return WINBINDD_OK;
924 static void add_gid_to_array_unique(gid_t gid, gid_t **gids, int *num)
926 int i;
928 if ((*num) >= groups_max())
929 return;
931 for (i=0; i<*num; i++) {
932 if ((*gids)[i] == gid)
933 return;
936 *gids = Realloc(*gids, (*num+1) * sizeof(gid_t));
938 if (*gids == NULL)
939 return;
941 (*gids)[*num] = gid;
942 *num += 1;
945 static void add_local_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
947 gid_t gid;
948 DOM_SID *aliases;
949 int j, num_aliases;
951 DEBUG(10, ("Adding local gids from SID: %s\n",
952 sid_string_static(sid)));
954 /* Don't expand aliases if not explicitly activated -- for now
955 -- jerry */
957 if (!lp_winbind_nested_groups())
958 return;
960 /* Add nested group memberships */
962 if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases))
963 return;
965 for (j=0; j<num_aliases; j++) {
966 enum SID_NAME_USE type;
968 if (!local_sid_to_gid(&gid, &aliases[j], &type)) {
969 DEBUG(1, ("Got an alias membership with no alias\n"));
970 continue;
973 if ((type != SID_NAME_ALIAS) && (type != SID_NAME_WKN_GRP)) {
974 DEBUG(1, ("Got an alias membership in a non-alias\n"));
975 continue;
978 add_gid_to_array_unique(gid, gids, num);
980 SAFE_FREE(aliases);
983 static void add_gids_from_user_sid(DOM_SID *sid, gid_t **gids, int *num)
985 DEBUG(10, ("Adding gids from user SID: %s\n",
986 sid_string_static(sid)));
988 add_local_gids_from_sid(sid, gids, num);
991 static void add_gids_from_group_sid(DOM_SID *sid, gid_t **gids, int *num)
993 gid_t gid;
995 DEBUG(10, ("Adding gids from group SID: %s\n",
996 sid_string_static(sid)));
998 if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
999 add_gid_to_array_unique(gid, gids, num);
1001 add_local_gids_from_sid(sid, gids, num);
1004 /* Get user supplementary groups. This is much quicker than trying to
1005 invert the groups database. We merge the groups from the gids and
1006 other_sids info3 fields as trusted domain, universal group
1007 memberships, and nested groups (win2k native mode only) are not
1008 returned by the getgroups RPC call but are present in the info3. */
1010 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
1012 fstring name_domain, name_user;
1013 DOM_SID user_sid, group_sid;
1014 enum SID_NAME_USE name_type;
1015 uint32 num_groups = 0;
1016 uint32 num_gids = 0;
1017 NTSTATUS status;
1018 DOM_SID **user_grpsids;
1019 struct winbindd_domain *domain;
1020 enum winbindd_result result = WINBINDD_ERROR;
1021 gid_t *gid_list = NULL;
1022 unsigned int i;
1023 TALLOC_CTX *mem_ctx;
1024 NET_USER_INFO_3 *info3 = NULL;
1026 /* Ensure null termination */
1027 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
1029 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1030 state->request.data.username));
1032 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1033 state->request.data.username)))
1034 return WINBINDD_ERROR;
1036 /* Parse domain and username */
1038 parse_domain_user(state->request.data.username,
1039 name_domain, name_user);
1041 /* Get info for the domain */
1043 if ((domain = find_domain_from_name(name_domain)) == NULL) {
1044 DEBUG(7, ("could not find domain entry for domain %s\n",
1045 name_domain));
1046 goto done;
1049 if ( domain->primary && lp_winbind_trusted_domains_only()) {
1050 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
1051 name_domain, name_user));
1052 return WINBINDD_ERROR;
1055 /* Get rid and name type from name. The following costs 1 packet */
1057 if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid,
1058 &name_type)) {
1059 DEBUG(1, ("user '%s' does not exist\n", name_user));
1060 goto done;
1063 if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1064 DEBUG(1, ("name '%s' is not a user name: %d\n",
1065 name_user, name_type));
1066 goto done;
1069 add_gids_from_user_sid(&user_sid, &gid_list, &num_gids);
1071 /* Treat the info3 cache as authoritative as the
1072 lookup_usergroups() function may return cached data. */
1074 if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1076 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1077 info3->num_groups2, info3->num_other_sids));
1079 num_groups = info3->num_other_sids + info3->num_groups2;
1081 /* Go through each other sid and convert it to a gid */
1083 for (i = 0; i < info3->num_other_sids; i++) {
1084 fstring name;
1085 fstring dom_name;
1086 enum SID_NAME_USE sid_type;
1088 /* Is this sid known to us? It can either be
1089 a trusted domain sid or a foreign sid. */
1091 if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
1092 dom_name, name, &sid_type))
1094 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
1095 sid_string_static(&info3->other_sids[i].sid)));
1096 continue;
1099 /* Check it is a domain group or an alias (domain local group)
1100 in a win2k native mode domain. */
1102 if ( !((sid_type==SID_NAME_DOM_GRP) ||
1103 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1105 DEBUG(10, ("winbindd_getgroups: sid type %d "
1106 "for %s is not a domain group\n",
1107 sid_type,
1108 sid_string_static(
1109 &info3->other_sids[i].sid)));
1110 continue;
1113 add_gids_from_group_sid(&info3->other_sids[i].sid,
1114 &gid_list, &num_gids);
1116 if (gid_list == NULL)
1117 goto done;
1120 for (i = 0; i < info3->num_groups2; i++) {
1122 /* create the group SID */
1124 sid_copy( &group_sid, &domain->sid );
1125 sid_append_rid( &group_sid, info3->gids[i].g_rid );
1127 add_gids_from_group_sid(&group_sid, &gid_list,
1128 &num_gids);
1130 if (gid_list == NULL)
1131 goto done;
1134 SAFE_FREE(info3);
1136 } else {
1137 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1138 &user_sid, &num_groups,
1139 &user_grpsids);
1140 if (!NT_STATUS_IS_OK(status))
1141 goto done;
1143 if (state->response.extra_data)
1144 goto done;
1146 for (i = 0; i < num_groups; i++) {
1147 add_gids_from_group_sid(user_grpsids[i],
1148 &gid_list, &num_gids);
1150 if (gid_list == NULL)
1151 goto done;
1155 remove_duplicate_gids( &num_gids, gid_list );
1157 /* Send data back to client */
1159 state->response.data.num_entries = num_gids;
1160 state->response.extra_data = gid_list;
1161 state->response.length += num_gids * sizeof(gid_t);
1163 result = WINBINDD_OK;
1165 done:
1167 talloc_destroy(mem_ctx);
1169 return result;
1173 /* Get user supplementary sids. This is equivalent to the
1174 winbindd_getgroups() function but it involves a SID->SIDs mapping
1175 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1176 idmap. This call is designed to be used with applications that need
1177 to do ACL evaluation themselves. Note that the cached info3 data is
1178 not used
1180 this function assumes that the SID that comes in is a user SID. If
1181 you pass in another type of SID then you may get unpredictable
1182 results.
1184 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1186 DOM_SID user_sid;
1187 NTSTATUS status;
1188 DOM_SID **user_grpsids;
1189 struct winbindd_domain *domain;
1190 enum winbindd_result result = WINBINDD_ERROR;
1191 unsigned int i;
1192 TALLOC_CTX *mem_ctx;
1193 char *ret = NULL;
1194 uint32 num_groups;
1195 unsigned ofs, ret_size = 0;
1197 /* Ensure null termination */
1198 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1200 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1201 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1202 return WINBINDD_ERROR;
1205 if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1206 state->request.data.username))) {
1207 return WINBINDD_ERROR;
1210 /* Get info for the domain */
1211 if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1212 DEBUG(0,("could not find domain entry for sid %s\n",
1213 sid_string_static(&user_sid)));
1214 goto done;
1217 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1218 &user_sid, &num_groups,
1219 &user_grpsids);
1220 if (!NT_STATUS_IS_OK(status))
1221 goto done;
1223 if (num_groups == 0) {
1224 goto no_groups;
1227 /* work out the response size */
1228 for (i = 0; i < num_groups; i++) {
1229 const char *s = sid_string_static(user_grpsids[i]);
1230 ret_size += strlen(s) + 1;
1233 /* build the reply */
1234 ret = malloc(ret_size);
1235 if (!ret) goto done;
1236 ofs = 0;
1237 for (i = 0; i < num_groups; i++) {
1238 const char *s = sid_string_static(user_grpsids[i]);
1239 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1240 ofs += strlen(ret+ofs) + 1;
1243 no_groups:
1244 /* Send data back to client */
1245 state->response.data.num_entries = num_groups;
1246 state->response.extra_data = ret;
1247 state->response.length += ret_size;
1248 result = WINBINDD_OK;
1250 done:
1251 talloc_destroy(mem_ctx);
1253 return result;