r4231: commiting changes to 3.0.10
[Samba.git] / source / nsswitch / winbindd_group.c
blob8ece6f114a89e5a95bca655db28b592953604569
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 = SMB_XMALLOC_ARRAY(char, 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 = SMB_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 = SMB_MALLOC_P(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 state->getgrent_initialized = True;
476 return WINBINDD_OK;
479 /* Close file pointer to ntdom group database */
481 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
483 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
485 free_getent_state(state->getgrent_state);
486 state->getgrent_initialized = False;
487 state->getgrent_state = NULL;
489 return WINBINDD_OK;
492 /* Get the list of domain groups and domain aliases for a domain. We fill in
493 the sam_entries and num_sam_entries fields with domain group information.
494 The dispinfo_ndx field is incremented to the index of the next group to
495 fetch. Return True if some groups were returned, False otherwise. */
497 static BOOL get_sam_group_entries(struct getent_state *ent)
499 NTSTATUS status;
500 uint32 num_entries;
501 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
502 TALLOC_CTX *mem_ctx;
503 BOOL result = False;
504 struct acct_info *sam_grp_entries = NULL;
505 struct winbindd_domain *domain;
507 if (ent->got_sam_entries)
508 return False;
510 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
511 ent->domain_name))) {
512 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
513 return False;
516 /* Free any existing group info */
518 SAFE_FREE(ent->sam_entries);
519 ent->num_sam_entries = 0;
520 ent->got_sam_entries = True;
522 /* Enumerate domain groups */
524 num_entries = 0;
526 if (!(domain = find_domain_from_name(ent->domain_name))) {
527 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
528 goto done;
531 /* always get the domain global groups */
533 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
535 if (!NT_STATUS_IS_OK(status)) {
536 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
537 result = False;
538 goto done;
541 /* Copy entries into return buffer */
543 if (num_entries) {
544 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
545 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
546 num_entries));
547 result = False;
548 goto done;
550 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
553 ent->num_sam_entries = num_entries;
555 /* get the domain local groups if we are a member of a native win2k domain
556 and are not using LDAP to get the groups */
558 if ( ( lp_security() != SEC_ADS && domain->native_mode
559 && domain->primary) || domain->internal )
561 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
563 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
565 if ( !NT_STATUS_IS_OK(status) ) {
566 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
567 num_entries = 0;
569 else
570 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
572 /* Copy entries into return buffer */
574 if ( num_entries ) {
575 if ( !(tmp_name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
577 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
578 num_entries));
579 result = False;
580 SAFE_FREE( name_list );
581 goto done;
584 name_list = tmp_name_list;
586 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
587 num_entries * sizeof(struct acct_info) );
590 ent->num_sam_entries += num_entries;
594 /* Fill in remaining fields */
596 ent->sam_entries = name_list;
597 ent->sam_entry_index = 0;
599 result = (ent->num_sam_entries > 0);
601 done:
602 talloc_destroy(mem_ctx);
604 return result;
607 /* Fetch next group entry from ntdom database */
609 #define MAX_GETGRENT_GROUPS 500
611 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
613 struct getent_state *ent;
614 struct winbindd_gr *group_list = NULL;
615 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
616 char *new_extra_data, *gr_mem_list = NULL;
618 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
620 /* Check user has enabled this */
622 if (!lp_winbind_enum_groups())
623 return WINBINDD_ERROR;
625 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
627 if ((state->response.extra_data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL)
628 return WINBINDD_ERROR;
630 memset(state->response.extra_data, '\0',
631 num_groups * sizeof(struct winbindd_gr) );
633 state->response.data.num_entries = 0;
635 group_list = (struct winbindd_gr *)state->response.extra_data;
637 if (!state->getgrent_initialized)
638 winbindd_setgrent(state);
640 if (!(ent = state->getgrent_state))
641 return WINBINDD_ERROR;
643 /* Start sending back groups */
645 for (i = 0; i < num_groups; i++) {
646 struct acct_info *name_list = NULL;
647 fstring domain_group_name;
648 uint32 result;
649 gid_t group_gid;
650 int gr_mem_len;
651 char *gr_mem, *new_gr_mem_list;
652 DOM_SID group_sid;
653 struct winbindd_domain *domain;
655 /* Do we need to fetch another chunk of groups? */
657 tryagain:
659 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
660 ent->sam_entry_index, ent->num_sam_entries));
662 if (ent->num_sam_entries == ent->sam_entry_index) {
664 while(ent && !get_sam_group_entries(ent)) {
665 struct getent_state *next_ent;
667 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
669 /* Free state information for this domain */
671 SAFE_FREE(ent->sam_entries);
673 next_ent = ent->next;
674 DLIST_REMOVE(state->getgrent_state, ent);
676 SAFE_FREE(ent);
677 ent = next_ent;
680 /* No more domains */
682 if (!ent)
683 break;
686 name_list = ent->sam_entries;
688 if (!(domain =
689 find_domain_from_name(ent->domain_name))) {
690 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
691 result = False;
692 goto done;
695 /* Lookup group info */
697 sid_copy(&group_sid, &domain->sid);
698 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
700 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
702 DEBUG(1, ("could not look up gid for group %s\n",
703 name_list[ent->sam_entry_index].acct_name));
705 ent->sam_entry_index++;
706 goto tryagain;
709 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
710 (unsigned long)name_list[ent->sam_entry_index].rid));
712 /* Fill in group entry */
714 fill_domain_username(domain_group_name, ent->domain_name,
715 name_list[ent->sam_entry_index].acct_name);
717 result = fill_grent(&group_list[group_list_ndx],
718 ent->domain_name,
719 name_list[ent->sam_entry_index].acct_name,
720 group_gid);
722 /* Fill in group membership entry */
724 if (result) {
725 DOM_SID member_sid;
726 group_list[group_list_ndx].num_gr_mem = 0;
727 gr_mem = NULL;
728 gr_mem_len = 0;
730 /* Get group membership */
731 if (state->request.cmd == WINBINDD_GETGRLST) {
732 result = True;
733 } else {
734 sid_copy(&member_sid, &domain->sid);
735 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
736 result = fill_grent_mem(
737 domain,
738 &member_sid,
739 SID_NAME_DOM_GRP,
740 &group_list[group_list_ndx].num_gr_mem,
741 &gr_mem, &gr_mem_len);
745 if (result) {
746 /* Append to group membership list */
747 new_gr_mem_list = SMB_REALLOC( gr_mem_list, gr_mem_list_len + gr_mem_len);
749 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
750 DEBUG(0, ("out of memory\n"));
751 SAFE_FREE(gr_mem_list);
752 gr_mem_list_len = 0;
753 break;
756 DEBUG(10, ("list_len = %d, mem_len = %d\n",
757 gr_mem_list_len, gr_mem_len));
759 gr_mem_list = new_gr_mem_list;
761 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
762 gr_mem_len);
764 SAFE_FREE(gr_mem);
766 group_list[group_list_ndx].gr_mem_ofs =
767 gr_mem_list_len;
769 gr_mem_list_len += gr_mem_len;
772 ent->sam_entry_index++;
774 /* Add group to return list */
776 if (result) {
778 DEBUG(10, ("adding group num_entries = %d\n",
779 state->response.data.num_entries));
781 group_list_ndx++;
782 state->response.data.num_entries++;
784 state->response.length +=
785 sizeof(struct winbindd_gr);
787 } else {
788 DEBUG(0, ("could not lookup domain group %s\n",
789 domain_group_name));
793 /* Copy the list of group memberships to the end of the extra data */
795 if (group_list_ndx == 0)
796 goto done;
798 new_extra_data = SMB_REALLOC(
799 state->response.extra_data,
800 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
802 if (!new_extra_data) {
803 DEBUG(0, ("out of memory\n"));
804 group_list_ndx = 0;
805 SAFE_FREE(state->response.extra_data);
806 SAFE_FREE(gr_mem_list);
808 return WINBINDD_ERROR;
811 state->response.extra_data = new_extra_data;
813 memcpy(&((char *)state->response.extra_data)
814 [group_list_ndx * sizeof(struct winbindd_gr)],
815 gr_mem_list, gr_mem_list_len);
817 SAFE_FREE(gr_mem_list);
819 state->response.length += gr_mem_list_len;
821 DEBUG(10, ("returning %d groups, length = %d\n",
822 group_list_ndx, gr_mem_list_len));
824 /* Out of domains */
826 done:
828 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
831 /* List domain groups without mapping to unix ids */
833 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
835 uint32 total_entries = 0;
836 struct winbindd_domain *domain;
837 const char *which_domain;
838 char *extra_data = NULL;
839 char *ted = NULL;
840 unsigned int extra_data_len = 0, i;
842 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
844 /* Ensure null termination */
845 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
846 which_domain = state->request.domain_name;
848 /* Enumerate over trusted domains */
850 for (domain = domain_list(); domain; domain = domain->next) {
851 struct getent_state groups;
853 /* if we have a domain name restricting the request and this
854 one in the list doesn't match, then just bypass the remainder
855 of the loop */
857 if ( *which_domain && !strequal(which_domain, domain->name) )
858 continue;
860 ZERO_STRUCT(groups);
862 /* Get list of sam groups */
864 fstrcpy(groups.domain_name, domain->name);
866 get_sam_group_entries(&groups);
868 if (groups.num_sam_entries == 0) {
869 /* this domain is empty or in an error state */
870 continue;
873 /* keep track the of the total number of groups seen so
874 far over all domains */
875 total_entries += groups.num_sam_entries;
877 /* Allocate some memory for extra data. Note that we limit
878 account names to sizeof(fstring) = 128 characters. */
879 ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
881 if (!ted) {
882 DEBUG(0,("failed to enlarge buffer!\n"));
883 SAFE_FREE(extra_data);
884 return WINBINDD_ERROR;
885 } else
886 extra_data = ted;
888 /* Pack group list into extra data fields */
889 for (i = 0; i < groups.num_sam_entries; i++) {
890 char *group_name = ((struct acct_info *)
891 groups.sam_entries)[i].acct_name;
892 fstring name;
894 fill_domain_username(name, domain->name, group_name);
895 /* Append to extra data */
896 memcpy(&extra_data[extra_data_len], name,
897 strlen(name));
898 extra_data_len += strlen(name);
899 extra_data[extra_data_len++] = ',';
902 SAFE_FREE(groups.sam_entries);
905 /* Assign extra_data fields in response structure */
906 if (extra_data) {
907 extra_data[extra_data_len - 1] = '\0';
908 state->response.extra_data = extra_data;
909 state->response.length += extra_data_len;
912 /* No domains may have responded but that's still OK so don't
913 return an error. */
915 return WINBINDD_OK;
918 static void add_local_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num)
920 gid_t gid;
921 DOM_SID *aliases;
922 int j, num_aliases;
924 DEBUG(10, ("Adding local gids from SID: %s\n",
925 sid_string_static(sid)));
927 /* Don't expand aliases if not explicitly activated -- for now
928 -- jerry */
930 if (!lp_winbind_nested_groups())
931 return;
933 /* Add nested group memberships */
935 if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases))
936 return;
938 for (j=0; j<num_aliases; j++) {
939 enum SID_NAME_USE type;
941 if (!local_sid_to_gid(&gid, &aliases[j], &type)) {
942 DEBUG(1, ("Got an alias membership with no alias\n"));
943 continue;
946 if ((type != SID_NAME_ALIAS) && (type != SID_NAME_WKN_GRP)) {
947 DEBUG(1, ("Got an alias membership in a non-alias\n"));
948 continue;
951 add_gid_to_array_unique(gid, gids, num);
953 SAFE_FREE(aliases);
956 static void add_gids_from_user_sid(DOM_SID *sid, gid_t **gids, int *num)
958 DEBUG(10, ("Adding gids from user SID: %s\n",
959 sid_string_static(sid)));
961 add_local_gids_from_sid(sid, gids, num);
964 static void add_gids_from_group_sid(DOM_SID *sid, gid_t **gids, int *num)
966 gid_t gid;
968 DEBUG(10, ("Adding gids from group SID: %s\n",
969 sid_string_static(sid)));
971 if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0)))
972 add_gid_to_array_unique(gid, gids, num);
974 add_local_gids_from_sid(sid, gids, num);
977 /* Get user supplementary groups. This is much quicker than trying to
978 invert the groups database. We merge the groups from the gids and
979 other_sids info3 fields as trusted domain, universal group
980 memberships, and nested groups (win2k native mode only) are not
981 returned by the getgroups RPC call but are present in the info3. */
983 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
985 fstring name_domain, name_user;
986 DOM_SID user_sid, group_sid;
987 enum SID_NAME_USE name_type;
988 uint32 num_groups = 0;
989 uint32 num_gids = 0;
990 NTSTATUS status;
991 DOM_SID **user_grpsids;
992 struct winbindd_domain *domain;
993 enum winbindd_result result = WINBINDD_ERROR;
994 gid_t *gid_list = NULL;
995 unsigned int i;
996 TALLOC_CTX *mem_ctx;
997 NET_USER_INFO_3 *info3 = NULL;
999 /* Ensure null termination */
1000 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
1002 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1003 state->request.data.username));
1005 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
1006 state->request.data.username)))
1007 return WINBINDD_ERROR;
1009 /* Parse domain and username */
1011 parse_domain_user(state->request.data.username,
1012 name_domain, name_user);
1014 /* Get info for the domain */
1016 if ((domain = find_domain_from_name(name_domain)) == NULL) {
1017 DEBUG(7, ("could not find domain entry for domain %s\n",
1018 name_domain));
1019 goto done;
1022 if ( domain->primary && lp_winbind_trusted_domains_only()) {
1023 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
1024 name_domain, name_user));
1025 return WINBINDD_ERROR;
1028 /* Get rid and name type from name. The following costs 1 packet */
1030 if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid,
1031 &name_type)) {
1032 DEBUG(1, ("user '%s' does not exist\n", name_user));
1033 goto done;
1036 if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
1037 DEBUG(1, ("name '%s' is not a user name: %d\n",
1038 name_user, name_type));
1039 goto done;
1042 add_gids_from_user_sid(&user_sid, &gid_list, &num_gids);
1044 /* Treat the info3 cache as authoritative as the
1045 lookup_usergroups() function may return cached data. */
1047 if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
1049 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1050 info3->num_groups2, info3->num_other_sids));
1052 num_groups = info3->num_other_sids + info3->num_groups2;
1054 /* Go through each other sid and convert it to a gid */
1056 for (i = 0; i < info3->num_other_sids; i++) {
1057 fstring name;
1058 fstring dom_name;
1059 enum SID_NAME_USE sid_type;
1061 /* Is this sid known to us? It can either be
1062 a trusted domain sid or a foreign sid. */
1064 if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
1065 dom_name, name, &sid_type))
1067 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
1068 sid_string_static(&info3->other_sids[i].sid)));
1069 continue;
1072 /* Check it is a domain group or an alias (domain local group)
1073 in a win2k native mode domain. */
1075 if ( !((sid_type==SID_NAME_DOM_GRP) ||
1076 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1078 DEBUG(10, ("winbindd_getgroups: sid type %d "
1079 "for %s is not a domain group\n",
1080 sid_type,
1081 sid_string_static(
1082 &info3->other_sids[i].sid)));
1083 continue;
1086 add_gids_from_group_sid(&info3->other_sids[i].sid,
1087 &gid_list, &num_gids);
1089 if (gid_list == NULL)
1090 goto done;
1093 for (i = 0; i < info3->num_groups2; i++) {
1095 /* create the group SID */
1097 sid_copy( &group_sid, &domain->sid );
1098 sid_append_rid( &group_sid, info3->gids[i].g_rid );
1100 add_gids_from_group_sid(&group_sid, &gid_list,
1101 &num_gids);
1103 if (gid_list == NULL)
1104 goto done;
1107 SAFE_FREE(info3);
1109 } else {
1110 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1111 &user_sid, &num_groups,
1112 &user_grpsids);
1113 if (!NT_STATUS_IS_OK(status))
1114 goto done;
1116 if (state->response.extra_data)
1117 goto done;
1119 for (i = 0; i < num_groups; i++) {
1120 add_gids_from_group_sid(user_grpsids[i],
1121 &gid_list, &num_gids);
1123 if (gid_list == NULL)
1124 goto done;
1128 remove_duplicate_gids( &num_gids, gid_list );
1130 /* Send data back to client */
1132 state->response.data.num_entries = num_gids;
1133 state->response.extra_data = gid_list;
1134 state->response.length += num_gids * sizeof(gid_t);
1136 result = WINBINDD_OK;
1138 done:
1140 talloc_destroy(mem_ctx);
1142 return result;
1145 static void add_sid_to_array_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1146 DOM_SID ***sids, int *num_sids)
1148 int i;
1150 for (i=0; i<(*num_sids); i++) {
1151 if (sid_compare(sid, (*sids)[i]) == 0)
1152 return;
1155 *sids = TALLOC_REALLOC_ARRAY(mem_ctx, *sids, DOM_SID *, *num_sids+1);
1157 if (*sids == NULL)
1158 return;
1160 (*sids)[*num_sids] = TALLOC_P(mem_ctx, DOM_SID);
1161 sid_copy((*sids)[*num_sids], sid);
1162 *num_sids += 1;
1163 return;
1166 static void add_local_sids_from_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
1167 DOM_SID ***user_grpsids,
1168 int *num_groups)
1170 DOM_SID *aliases = NULL;
1171 int i, num_aliases = 0;
1173 if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases))
1174 return;
1176 if (num_aliases == 0)
1177 return;
1179 for (i=0; i<num_aliases; i++)
1180 add_sid_to_array_unique(mem_ctx, &aliases[i], user_grpsids,
1181 num_groups);
1183 SAFE_FREE(aliases);
1185 return;
1188 /* Get user supplementary sids. This is equivalent to the
1189 winbindd_getgroups() function but it involves a SID->SIDs mapping
1190 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1191 idmap. This call is designed to be used with applications that need
1192 to do ACL evaluation themselves. Note that the cached info3 data is
1193 not used
1195 this function assumes that the SID that comes in is a user SID. If
1196 you pass in another type of SID then you may get unpredictable
1197 results.
1199 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1201 DOM_SID user_sid;
1202 NTSTATUS status;
1203 DOM_SID **user_grpsids;
1204 struct winbindd_domain *domain;
1205 enum winbindd_result result = WINBINDD_ERROR;
1206 unsigned int i;
1207 TALLOC_CTX *mem_ctx;
1208 char *ret = NULL;
1209 uint32 num_groups;
1210 unsigned ofs, ret_size = 0;
1212 /* Ensure null termination */
1213 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1215 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1216 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1217 return WINBINDD_ERROR;
1220 if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1221 state->request.data.username))) {
1222 return WINBINDD_ERROR;
1225 /* Get info for the domain */
1226 if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1227 DEBUG(0,("could not find domain entry for sid %s\n",
1228 sid_string_static(&user_sid)));
1229 goto done;
1232 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1233 &user_sid, &num_groups,
1234 &user_grpsids);
1235 if (!NT_STATUS_IS_OK(status))
1236 goto done;
1238 if (num_groups == 0) {
1239 goto no_groups;
1242 if (lp_winbind_nested_groups()) {
1243 int k;
1244 /* num_groups is changed during the loop, that's why we have
1245 to count down here.*/
1247 for (k=num_groups-1; k>=0; k--) {
1248 add_local_sids_from_sid(mem_ctx, user_grpsids[k],
1249 &user_grpsids, &num_groups);
1252 add_local_sids_from_sid(mem_ctx, &user_sid, &user_grpsids,
1253 &num_groups);
1256 /* work out the response size */
1257 for (i = 0; i < num_groups; i++) {
1258 const char *s = sid_string_static(user_grpsids[i]);
1259 ret_size += strlen(s) + 1;
1262 /* build the reply */
1263 ret = SMB_MALLOC(ret_size);
1264 if (!ret) goto done;
1265 ofs = 0;
1266 for (i = 0; i < num_groups; i++) {
1267 const char *s = sid_string_static(user_grpsids[i]);
1268 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1269 ofs += strlen(ret+ofs) + 1;
1272 no_groups:
1273 /* Send data back to client */
1274 state->response.data.num_entries = num_groups;
1275 state->response.extra_data = ret;
1276 state->response.length += ret_size;
1277 result = WINBINDD_OK;
1279 done:
1280 talloc_destroy(mem_ctx);
1282 return result;