s3:winbind: Convert WINBINDD_GETUSERSIDS to the new API
[Samba/bb.git] / source3 / winbindd / winbindd_group.c
blob16defc793d5675010ea8e5f566fe4109a4a39fee
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
9 Copyright (C) Volker Lendecke 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
31 static void add_member(const char *domain, const char *user,
32 char **pp_members, size_t *p_num_members)
34 fstring name;
36 if (domain != NULL) {
37 fill_domain_username(name, domain, user, True);
38 } else {
39 fstrcpy(name, user);
41 safe_strcat(name, ",", sizeof(name)-1);
42 string_append(pp_members, name);
43 *p_num_members += 1;
46 /**********************************************************************
47 Add member users resulting from sid. Expand if it is a domain group.
48 **********************************************************************/
50 static void add_expanded_sid(const DOM_SID *sid,
51 char **pp_members,
52 size_t *p_num_members)
54 DOM_SID dom_sid;
55 uint32 rid;
56 struct winbindd_domain *domain;
57 size_t i;
59 char *domain_name = NULL;
60 char *name = NULL;
61 enum lsa_SidType type;
63 uint32 num_names;
64 DOM_SID *sid_mem;
65 char **names;
66 uint32 *types;
68 NTSTATUS result;
70 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
72 if (mem_ctx == NULL) {
73 DEBUG(1, ("talloc_init failed\n"));
74 return;
77 sid_copy(&dom_sid, sid);
78 sid_split_rid(&dom_sid, &rid);
80 domain = find_lookup_domain_from_sid(sid);
82 if (domain == NULL) {
83 DEBUG(3, ("Could not find domain for sid %s\n",
84 sid_string_dbg(sid)));
85 goto done;
88 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
89 &domain_name, &name, &type);
91 if (!NT_STATUS_IS_OK(result)) {
92 DEBUG(3, ("sid_to_name failed for sid %s\n",
93 sid_string_dbg(sid)));
94 goto done;
97 DEBUG(10, ("Found name %s, type %d\n", name, type));
99 if (type == SID_NAME_USER) {
100 add_member(domain_name, name, pp_members, p_num_members);
101 goto done;
104 if (type != SID_NAME_DOM_GRP) {
105 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
106 name));
107 goto done;
110 /* Expand the domain group, this must be done via the target domain */
112 domain = find_domain_from_sid(sid);
114 if (domain == NULL) {
115 DEBUG(3, ("Could not find domain from SID %s\n",
116 sid_string_dbg(sid)));
117 goto done;
120 result = domain->methods->lookup_groupmem(domain, mem_ctx,
121 sid, &num_names,
122 &sid_mem, &names,
123 &types);
125 if (!NT_STATUS_IS_OK(result)) {
126 DEBUG(10, ("Could not lookup group members for %s: %s\n",
127 name, nt_errstr(result)));
128 goto done;
131 for (i=0; i<num_names; i++) {
132 DEBUG(10, ("Adding group member SID %s\n",
133 sid_string_dbg(&sid_mem[i])));
135 if (types[i] != SID_NAME_USER) {
136 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
137 "Ignoring.\n", names[i], name));
138 continue;
141 add_member(NULL, names[i], pp_members, p_num_members);
144 done:
145 talloc_destroy(mem_ctx);
146 return;
149 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
150 DOM_SID *group_sid, size_t *num_gr_mem,
151 char **gr_mem, size_t *gr_mem_len)
153 DOM_SID *members;
154 size_t i, num_members;
156 *num_gr_mem = 0;
157 *gr_mem = NULL;
158 *gr_mem_len = 0;
160 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, talloc_tos(),
161 &members, &num_members)))
162 return True;
164 for (i=0; i<num_members; i++) {
165 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
168 TALLOC_FREE(members);
170 if (*gr_mem != NULL) {
171 size_t len;
173 /* We have at least one member, strip off the last "," */
174 len = strlen(*gr_mem);
175 (*gr_mem)[len-1] = '\0';
176 *gr_mem_len = len;
179 return True;
182 /* Fill a grent structure from various other information */
184 bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
185 const char *dom_name, const char *gr_name, gid_t unix_gid)
187 fstring full_group_name;
188 char *mapped_name = NULL;
189 struct winbindd_domain *domain = find_domain_from_name_noinit(dom_name);
190 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
192 nt_status = normalize_name_map(mem_ctx, domain, gr_name,
193 &mapped_name);
195 /* Basic whitespace replacement */
196 if (NT_STATUS_IS_OK(nt_status)) {
197 fill_domain_username(full_group_name, dom_name,
198 mapped_name, true);
200 /* Mapped to an aliase */
201 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
202 fstrcpy(full_group_name, mapped_name);
204 /* no change */
205 else {
206 fill_domain_username( full_group_name, dom_name,
207 gr_name, True );
210 gr->gr_gid = unix_gid;
212 /* Group name and password */
214 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
215 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
217 return True;
220 /***********************************************************************
221 If "enum users" is set to false, and the group being looked
222 up is the Domain Users SID: S-1-5-domain-513, then for the
223 list of members check if the querying user is in that group,
224 and if so only return that user as the gr_mem array.
225 We can change this to a different parameter than "enum users"
226 if neccessaey, or parameterize the group list we do this for.
227 ***********************************************************************/
229 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
230 struct winbindd_domain *domain,
231 struct winbindd_cli_state *state,
232 DOM_SID *group_sid,
233 enum lsa_SidType group_name_type,
234 size_t *num_gr_mem, char **gr_mem,
235 size_t *gr_mem_len)
237 DOM_SID querying_user_sid;
238 DOM_SID *pquerying_user_sid = NULL;
239 uint32 num_groups = 0;
240 DOM_SID *user_sids = NULL;
241 bool u_in_group = False;
242 NTSTATUS status;
243 int i;
244 unsigned int buf_len = 0;
245 char *buf = NULL;
247 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
248 domain->name ));
250 if (state) {
251 uid_t ret_uid = (uid_t)-1;
252 if (sys_getpeereid(state->sock, &ret_uid)==0) {
253 /* We know who's asking - look up their SID if
254 it's one we've mapped before. */
255 status = idmap_uid_to_sid(domain->name,
256 &querying_user_sid, ret_uid);
257 if (NT_STATUS_IS_OK(status)) {
258 pquerying_user_sid = &querying_user_sid;
259 DEBUG(10,("fill_grent_mem_domain_users: "
260 "querying uid %u -> %s\n",
261 (unsigned int)ret_uid,
262 sid_string_dbg(pquerying_user_sid)));
267 /* Only look up if it was a winbindd user in this domain. */
268 if (pquerying_user_sid &&
269 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
271 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
272 sid_string_dbg(pquerying_user_sid) ));
274 status = domain->methods->lookup_usergroups(domain,
275 mem_ctx,
276 pquerying_user_sid,
277 &num_groups,
278 &user_sids);
279 if (!NT_STATUS_IS_OK(status)) {
280 DEBUG(1, ("fill_grent_mem_domain_users: "
281 "lookup_usergroups failed "
282 "for sid %s in domain %s (error: %s)\n",
283 sid_string_dbg(pquerying_user_sid),
284 domain->name,
285 nt_errstr(status)));
286 return False;
289 for (i = 0; i < num_groups; i++) {
290 if (sid_equal(group_sid, &user_sids[i])) {
291 /* User is in Domain Users, add their name
292 as the only group member. */
293 u_in_group = True;
294 break;
299 if (u_in_group) {
300 size_t len = 0;
301 char *domainname = NULL;
302 char *username = NULL;
303 fstring name;
304 char *mapped_name = NULL;
305 enum lsa_SidType type;
306 struct winbindd_domain *target_domain = NULL;
307 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
309 DEBUG(10,("fill_grent_mem_domain_users: "
310 "sid %s in 'Domain Users' in domain %s\n",
311 sid_string_dbg(pquerying_user_sid),
312 domain->name ));
314 status = domain->methods->sid_to_name(domain, mem_ctx,
315 pquerying_user_sid,
316 &domainname,
317 &username,
318 &type);
319 if (!NT_STATUS_IS_OK(status)) {
320 DEBUG(1, ("could not lookup username for user "
321 "sid %s in domain %s (error: %s)\n",
322 sid_string_dbg(pquerying_user_sid),
323 domain->name,
324 nt_errstr(status)));
325 return False;
328 target_domain = find_domain_from_name_noinit(domainname);
329 name_map_status = normalize_name_map(mem_ctx, target_domain,
330 username, &mapped_name);
332 /* Basic whitespace replacement */
333 if (NT_STATUS_IS_OK(name_map_status)) {
334 fill_domain_username(name, domainname, mapped_name, true);
336 /* Mapped to an alias */
337 else if (NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
338 fstrcpy(name, mapped_name);
340 /* no mapping done...use original name */
341 else {
342 fill_domain_username(name, domainname, username, true);
345 len = strlen(name);
346 buf_len = len + 1;
347 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
348 DEBUG(1, ("out of memory\n"));
349 return False;
351 memcpy(buf, name, buf_len);
353 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
354 "'Domain Users' in domain %s\n",
355 name, domain->name ));
357 /* user is the only member */
358 *num_gr_mem = 1;
361 *gr_mem = buf;
362 *gr_mem_len = buf_len;
364 DEBUG(10, ("fill_grent_mem_domain_users: "
365 "num_mem = %u, len = %u, mem = %s\n",
366 (unsigned int)*num_gr_mem,
367 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
369 return True;
372 /***********************************************************************
373 Add names to a list. Assumes a canonical version of the string
374 in DOMAIN\user
375 ***********************************************************************/
377 static int namecmp( const void *a, const void *b )
379 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
382 static void sort_unique_list(char ***list, uint32 *n_list)
384 uint32_t i;
386 /* search for duplicates for sorting and looking for matching
387 neighbors */
389 qsort(*list, *n_list, sizeof(char*), QSORT_CAST namecmp);
391 for (i=1; i < *n_list; i++) {
392 if (strcmp((*list)[i-1], (*list)[i]) == 0) {
393 memmove(&((*list)[i-1]), &((*list)[i]),
394 sizeof(char*)*((*n_list)-i));
395 (*n_list)--;
400 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
401 char ***list, uint32 *n_list,
402 char **names, uint32 n_names )
404 char **new_list = NULL;
405 uint32 n_new_list = 0;
406 int i, j;
408 if ( !names || (n_names == 0) )
409 return NT_STATUS_OK;
411 /* Alloc the maximum size we'll need */
413 if ( *list == NULL ) {
414 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
415 return NT_STATUS_NO_MEMORY;
417 n_new_list = n_names;
418 } else {
419 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
420 (*n_list) + n_names );
421 if ( !new_list )
422 return NT_STATUS_NO_MEMORY;
423 n_new_list = (*n_list) + n_names;
426 /* Add all names */
428 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
429 new_list[i] = talloc_strdup( new_list, names[j] );
432 *list = new_list;
433 *n_list = n_new_list;
435 return NT_STATUS_OK;
438 /***********************************************************************
439 ***********************************************************************/
441 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
442 struct winbindd_domain *d,
443 DOM_SID *glist, uint32 n_glist,
444 DOM_SID **new_glist, uint32 *n_new_glist,
445 char ***members, uint32 *n_members )
447 int i, j;
448 NTSTATUS status = NT_STATUS_OK;
449 uint32 num_names = 0;
450 uint32 *name_types = NULL;
451 char **names = NULL;
452 DOM_SID *sid_mem = NULL;
453 TALLOC_CTX *tmp_ctx = NULL;
454 DOM_SID *new_groups = NULL;
455 size_t new_groups_size = 0;
457 *members = NULL;
458 *n_members = 0;
459 *new_glist = NULL;
460 *n_new_glist = 0;
462 DEBUG(10,("expand_groups:\n"));
464 for ( i=0; i<n_glist; i++ ) {
466 NTSTATUS lookup_status;
468 tmp_ctx = talloc_new( ctx );
470 /* Lookup the group membership */
472 lookup_status = d->methods->lookup_groupmem(d, tmp_ctx,
473 &glist[i], &num_names,
474 &sid_mem, &names,
475 &name_types);
476 if (!NT_STATUS_IS_OK(lookup_status)) {
477 DEBUG(10,("expand_groups: lookup_groupmem for "
478 "sid %s failed with: %s\n",
479 sid_string_dbg(&glist[i]),
480 nt_errstr(lookup_status)));
482 /* we might have hit a logic error when called for an
483 * alias, in that case just continue with group
484 * expansion - Guenther */
486 if (NT_STATUS_EQUAL(lookup_status, NT_STATUS_NO_SUCH_GROUP)) {
487 continue;
489 status = lookup_status;
490 goto out;
493 /* Separate users and groups into two lists */
495 for ( j=0; j<num_names; j++ ) {
497 /* Users */
498 if ( name_types[j] == SID_NAME_USER ||
499 name_types[j] == SID_NAME_COMPUTER )
501 status = add_names_to_list( ctx, members,
502 n_members,
503 names+j, 1 );
504 if ( !NT_STATUS_IS_OK(status) )
505 goto out;
507 continue;
510 /* Groups */
511 if ( name_types[j] == SID_NAME_DOM_GRP ||
512 name_types[j] == SID_NAME_ALIAS )
514 status = add_sid_to_array_unique(ctx,
515 &sid_mem[j],
516 &new_groups,
517 &new_groups_size);
518 if (!NT_STATUS_IS_OK(status)) {
519 goto out;
522 continue;
526 TALLOC_FREE( tmp_ctx );
529 *new_glist = new_groups;
530 *n_new_glist = (uint32)new_groups_size;
532 out:
533 TALLOC_FREE( tmp_ctx );
535 if (!NT_STATUS_IS_OK(status)) {
536 DEBUG(10,("expand_groups: returning with %s\n",
537 nt_errstr(status)));
540 return status;
543 /***********************************************************************
544 Fill in the group membership field of a NT group given by group_sid
545 ***********************************************************************/
547 static bool fill_grent_mem(struct winbindd_domain *domain,
548 struct winbindd_cli_state *state,
549 DOM_SID *group_sid,
550 enum lsa_SidType group_name_type,
551 size_t *num_gr_mem, char **gr_mem,
552 size_t *gr_mem_len)
554 uint32 num_names = 0;
555 unsigned int buf_len = 0, buf_ndx = 0, i;
556 char **names = NULL, *buf = NULL;
557 bool result = False;
558 TALLOC_CTX *mem_ctx;
559 uint32 group_rid;
560 DOM_SID *glist = NULL;
561 DOM_SID *new_glist = NULL;
562 uint32 n_glist, n_new_glist;
563 int max_depth = lp_winbind_expand_groups();
565 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
566 return False;
568 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
570 /* Initialize with no members */
572 *num_gr_mem = 0;
574 /* HACK ALERT!! This whole routine does not cope with group members
575 * from more than one domain, ie aliases. Thus we have to work it out
576 * ourselves in a special routine. */
578 if (domain->internal) {
579 result = fill_passdb_alias_grmem(domain, group_sid,
580 num_gr_mem,
581 gr_mem, gr_mem_len);
582 goto done;
585 /* Verify name type */
587 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
588 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
590 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
591 sid_string_dbg(group_sid),
592 domain->name, group_name_type));
593 goto done;
596 /* OPTIMIZATION / HACK. See comment in
597 fill_grent_mem_domusers() */
599 sid_peek_rid( group_sid, &group_rid );
600 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
601 result = fill_grent_mem_domusers( mem_ctx, domain, state,
602 group_sid, group_name_type,
603 num_gr_mem, gr_mem,
604 gr_mem_len );
605 goto done;
608 /* Real work goes here. Create a list of group names to
609 expand starting with the initial one. Pass that to
610 expand_groups() which returns a list of more group names
611 to expand. Do this up to the max search depth. */
613 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
614 result = False;
615 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
616 goto done;
618 sid_copy( &glist[0], group_sid );
619 n_glist = 1;
621 for ( i=0; i<max_depth && glist; i++ ) {
622 uint32 n_members = 0;
623 char **members = NULL;
624 NTSTATUS nt_status;
625 int j;
627 nt_status = expand_groups( mem_ctx, domain,
628 glist, n_glist,
629 &new_glist, &n_new_glist,
630 &members, &n_members);
631 if ( !NT_STATUS_IS_OK(nt_status) ) {
632 result = False;
633 goto done;
636 /* Add new group members to list. Pass through the
637 alias mapping function */
639 for (j=0; j<n_members; j++) {
640 fstring name_domain, name_acct;
641 fstring qualified_name;
642 char *mapped_name = NULL;
643 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
644 struct winbindd_domain *target_domain = NULL;
646 if (parse_domain_user(members[j], name_domain, name_acct)) {
647 target_domain = find_domain_from_name_noinit(name_domain);
648 /* NOW WHAT ? */
650 if (!target_domain) {
651 target_domain = domain;
654 name_map_status = normalize_name_map(members, target_domain,
655 name_acct, &mapped_name);
657 /* Basic whitespace replacement */
658 if (NT_STATUS_IS_OK(name_map_status)) {
659 fill_domain_username(qualified_name, name_domain,
660 mapped_name, true);
661 mapped_name = qualified_name;
663 /* no mapping at all */
664 else if (!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
665 mapped_name = members[j];
668 nt_status = add_names_to_list( mem_ctx, &names,
669 &num_names,
670 &mapped_name, 1);
671 if ( !NT_STATUS_IS_OK(nt_status) ) {
672 result = False;
673 goto done;
677 TALLOC_FREE( members );
679 /* If we have no more groups to expand, break out
680 early */
682 if (new_glist == NULL)
683 break;
685 /* One more round */
686 TALLOC_FREE(glist);
687 glist = new_glist;
688 n_glist = n_new_glist;
690 TALLOC_FREE( glist );
692 sort_unique_list(&names, &num_names);
694 DEBUG(10, ("looked up %d names\n", num_names));
696 again:
697 /* Add members to list */
699 for (i = 0; i < num_names; i++) {
700 int len;
702 DEBUG(10, ("processing name %s\n", names[i]));
704 len = strlen(names[i]);
706 /* Add to list or calculate buffer length */
708 if (!buf) {
709 buf_len += len + 1; /* List is comma separated */
710 (*num_gr_mem)++;
711 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
712 } else {
713 DEBUG(10, ("appending %s at ndx %d\n",
714 names[i], buf_ndx));
715 parse_add_domuser(&buf[buf_ndx], names[i], &len);
716 buf_ndx += len;
717 buf[buf_ndx] = ',';
718 buf_ndx++;
722 /* Allocate buffer */
724 if (!buf && buf_len != 0) {
725 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
726 DEBUG(1, ("out of memory\n"));
727 result = False;
728 goto done;
730 memset(buf, 0, buf_len);
731 goto again;
734 /* Now we're done */
736 if (buf && buf_ndx > 0) {
737 buf[buf_ndx - 1] = '\0';
740 *gr_mem = buf;
741 *gr_mem_len = buf_len;
743 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
744 (unsigned int)*num_gr_mem,
745 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
746 result = True;
748 done:
750 talloc_destroy(mem_ctx);
752 DEBUG(10,("fill_grent_mem returning %s\n",
753 result == true ? "true" : "false"));
755 return result;
759 * set/get/endgrent functions
762 /* "Rewind" file pointer for group database enumeration */
764 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
766 struct winbindd_domain *domain;
768 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
770 /* Check user has enabled this */
772 if (!lp_winbind_enum_groups()) {
773 return False;
776 /* Free old static data if it exists */
778 if (state->getgrent_state != NULL) {
779 free_getent_state(state->getgrent_state);
780 state->getgrent_state = NULL;
783 /* Create sam pipes for each domain we know about */
785 for (domain = domain_list(); domain != NULL; domain = domain->next) {
786 struct getent_state *domain_state;
788 /* Create a state record for this domain */
790 /* don't add our domaina if we are a PDC or if we
791 are a member of a Samba domain */
793 if ( lp_winbind_trusted_domains_only() && domain->primary )
795 continue;
798 domain_state = SMB_MALLOC_P(struct getent_state);
799 if (!domain_state) {
800 DEBUG(1, ("winbindd_setgrent: "
801 "malloc failed for domain_state!\n"));
802 return False;
805 ZERO_STRUCTP(domain_state);
807 fstrcpy(domain_state->domain_name, domain->name);
809 /* Add to list of open domains */
811 DLIST_ADD(state->getgrent_state, domain_state);
814 state->getgrent_initialized = True;
815 return True;
818 void winbindd_setgrent(struct winbindd_cli_state *state)
820 if (winbindd_setgrent_internal(state)) {
821 request_ok(state);
822 } else {
823 request_error(state);
827 /* Close file pointer to ntdom group database */
829 void winbindd_endgrent(struct winbindd_cli_state *state)
831 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
833 free_getent_state(state->getgrent_state);
834 state->getgrent_initialized = False;
835 state->getgrent_state = NULL;
836 request_ok(state);
839 /* Get the list of domain groups and domain aliases for a domain. We fill in
840 the sam_entries and num_sam_entries fields with domain group information.
841 Return True if some groups were returned, False otherwise. */
843 bool get_sam_group_entries(struct getent_state *ent)
845 NTSTATUS status;
846 uint32 num_entries;
847 struct acct_info *name_list = NULL;
848 TALLOC_CTX *mem_ctx;
849 bool result = False;
850 struct acct_info *sam_grp_entries = NULL;
851 struct winbindd_domain *domain;
853 if (ent->got_sam_entries)
854 return False;
856 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
857 ent->domain_name))) {
858 DEBUG(1, ("get_sam_group_entries: "
859 "could not create talloc context!\n"));
860 return False;
863 /* Free any existing group info */
865 SAFE_FREE(ent->sam_entries);
866 ent->num_sam_entries = 0;
867 ent->got_sam_entries = True;
869 /* Enumerate domain groups */
871 num_entries = 0;
873 if (!(domain = find_domain_from_name(ent->domain_name))) {
874 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
875 ent->domain_name));
876 goto done;
879 /* always get the domain global groups */
881 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
882 &sam_grp_entries);
884 if (!NT_STATUS_IS_OK(status)) {
885 DEBUG(3, ("get_sam_group_entries: "
886 "could not enumerate domain groups! Error: %s\n",
887 nt_errstr(status)));
888 result = False;
889 goto done;
892 /* Copy entries into return buffer */
894 if (num_entries) {
895 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
896 if (!name_list) {
897 DEBUG(0,("get_sam_group_entries: Failed to malloc "
898 "memory for %d domain groups!\n",
899 num_entries));
900 result = False;
901 goto done;
903 memcpy(name_list, sam_grp_entries,
904 num_entries * sizeof(struct acct_info));
907 ent->num_sam_entries = num_entries;
909 /* get the domain local groups if we are a member of a native win2k
910 * domain and are not using LDAP to get the groups */
912 if ( ( lp_security() != SEC_ADS && domain->native_mode
913 && domain->primary) || domain->internal )
915 DEBUG(4,("get_sam_group_entries: %s domain; "
916 "enumerating local groups as well\n",
917 domain->native_mode ? "Native Mode 2k":
918 "BUILTIN or local"));
920 status = domain->methods->enum_local_groups(domain, mem_ctx,
921 &num_entries,
922 &sam_grp_entries);
924 if ( !NT_STATUS_IS_OK(status) ) {
925 DEBUG(3,("get_sam_group_entries: "
926 "Failed to enumerate "
927 "domain local groups with error %s!\n",
928 nt_errstr(status)));
929 num_entries = 0;
931 else
932 DEBUG(4,("get_sam_group_entries: "
933 "Returned %d local groups\n",
934 num_entries));
936 /* Copy entries into return buffer */
938 if ( num_entries ) {
939 name_list = SMB_REALLOC_ARRAY(name_list,
940 struct acct_info,
941 ent->num_sam_entries+
942 num_entries);
943 if (!name_list) {
944 DEBUG(0,("get_sam_group_entries: "
945 "Failed to realloc more memory "
946 "for %d local groups!\n",
947 num_entries));
948 result = False;
949 goto done;
952 memcpy(&name_list[ent->num_sam_entries],
953 sam_grp_entries,
954 num_entries * sizeof(struct acct_info));
957 ent->num_sam_entries += num_entries;
961 /* Fill in remaining fields */
963 ent->sam_entries = name_list;
964 ent->sam_entry_index = 0;
966 result = (ent->num_sam_entries > 0);
968 done:
969 talloc_destroy(mem_ctx);
971 return result;
974 /* Fetch next group entry from ntdom database */
976 #define MAX_GETGRENT_GROUPS 500
978 void winbindd_getgrent(struct winbindd_cli_state *state)
980 struct getent_state *ent;
981 struct winbindd_gr *group_list = NULL;
982 int num_groups, group_list_ndx, gr_mem_list_len = 0;
983 char *gr_mem_list = NULL;
985 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
987 /* Check user has enabled this */
989 if (!lp_winbind_enum_groups()) {
990 request_error(state);
991 return;
994 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request->data.num_entries);
996 if (num_groups == 0) {
997 request_error(state);
998 return;
1001 group_list = talloc_zero_array(state->mem_ctx, struct winbindd_gr,
1002 num_groups);
1003 if (!group_list) {
1004 request_error(state);
1005 return;
1007 state->response->extra_data.data = group_list;
1009 state->response->data.num_entries = 0;
1011 if (!state->getgrent_initialized)
1012 winbindd_setgrent_internal(state);
1014 if (!(ent = state->getgrent_state)) {
1015 request_error(state);
1016 return;
1019 /* Start sending back groups */
1021 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1022 struct acct_info *name_list = NULL;
1023 fstring domain_group_name;
1024 uint32 result;
1025 gid_t group_gid;
1026 size_t gr_mem_len;
1027 char *gr_mem;
1028 DOM_SID group_sid;
1029 struct winbindd_domain *domain;
1031 /* Do we need to fetch another chunk of groups? */
1033 tryagain:
1035 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1036 ent->sam_entry_index, ent->num_sam_entries));
1038 if (ent->num_sam_entries == ent->sam_entry_index) {
1040 while(ent && !get_sam_group_entries(ent)) {
1041 struct getent_state *next_ent;
1043 DEBUG(10, ("freeing state info for domain %s\n",
1044 ent->domain_name));
1046 /* Free state information for this domain */
1048 SAFE_FREE(ent->sam_entries);
1050 next_ent = ent->next;
1051 DLIST_REMOVE(state->getgrent_state, ent);
1053 SAFE_FREE(ent);
1054 ent = next_ent;
1057 /* No more domains */
1059 if (!ent)
1060 break;
1063 name_list = (struct acct_info *)ent->sam_entries;
1065 if (!(domain = find_domain_from_name(ent->domain_name))) {
1066 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1067 ent->domain_name));
1068 result = False;
1069 goto done;
1072 /* Lookup group info */
1074 sid_copy(&group_sid, &domain->sid);
1075 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1077 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->have_idmap_config
1078 ? domain->name : "",
1079 &group_sid, &group_gid)))
1081 union unid_t id;
1082 enum lsa_SidType type;
1084 DEBUG(10, ("SID %s not in idmap\n",
1085 sid_string_dbg(&group_sid)));
1087 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1088 DEBUG(1,("could not look up gid for group %s\n",
1089 name_list[ent->sam_entry_index].acct_name));
1090 ent->sam_entry_index++;
1091 goto tryagain;
1094 if ((type != SID_NAME_DOM_GRP) &&
1095 (type != SID_NAME_ALIAS) &&
1096 (type != SID_NAME_WKN_GRP)) {
1097 DEBUG(1, ("Group %s is a %s, not a group\n",
1098 sid_type_lookup(type),
1099 name_list[ent->sam_entry_index].acct_name));
1100 ent->sam_entry_index++;
1101 goto tryagain;
1103 group_gid = id.gid;
1106 DEBUG(10, ("got gid %lu for group %lu\n",
1107 (unsigned long)group_gid,
1108 (unsigned long)name_list[ent->sam_entry_index].rid));
1110 /* Fill in group entry */
1112 fill_domain_username(domain_group_name, ent->domain_name,
1113 name_list[ent->sam_entry_index].acct_name, True);
1115 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1116 ent->domain_name,
1117 name_list[ent->sam_entry_index].acct_name,
1118 group_gid);
1120 /* Fill in group membership entry */
1122 if (result) {
1123 size_t num_gr_mem = 0;
1124 DOM_SID member_sid;
1125 group_list[group_list_ndx].num_gr_mem = 0;
1126 gr_mem = NULL;
1127 gr_mem_len = 0;
1129 /* Get group membership */
1130 if (state->request->cmd == WINBINDD_GETGRLST) {
1131 result = True;
1132 } else {
1133 sid_copy(&member_sid, &domain->sid);
1134 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1135 result = fill_grent_mem(
1136 domain,
1137 NULL,
1138 &member_sid,
1139 SID_NAME_DOM_GRP,
1140 &num_gr_mem,
1141 &gr_mem, &gr_mem_len);
1143 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1147 if (result) {
1148 /* Append to group membership list */
1149 gr_mem_list = (char *)SMB_REALLOC(
1150 gr_mem_list, gr_mem_list_len + gr_mem_len);
1152 if (!gr_mem_list &&
1153 (group_list[group_list_ndx].num_gr_mem != 0)) {
1154 DEBUG(0, ("out of memory\n"));
1155 gr_mem_list_len = 0;
1156 break;
1159 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1160 gr_mem_list_len, (unsigned int)gr_mem_len));
1162 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1163 gr_mem_len);
1165 SAFE_FREE(gr_mem);
1167 group_list[group_list_ndx].gr_mem_ofs =
1168 gr_mem_list_len;
1170 gr_mem_list_len += gr_mem_len;
1173 ent->sam_entry_index++;
1175 /* Add group to return list */
1177 if (result) {
1179 DEBUG(10, ("adding group num_entries = %d\n",
1180 state->response->data.num_entries));
1182 group_list_ndx++;
1183 state->response->data.num_entries++;
1185 state->response->length +=
1186 sizeof(struct winbindd_gr);
1188 } else {
1189 DEBUG(0, ("could not lookup domain group %s\n",
1190 domain_group_name));
1194 /* Copy the list of group memberships to the end of the extra data */
1196 if (group_list_ndx == 0)
1197 goto done;
1199 state->response->extra_data.data = talloc_realloc_size(
1200 state->mem_ctx, state->response->extra_data.data,
1201 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1203 if (!state->response->extra_data.data) {
1204 DEBUG(0, ("out of memory\n"));
1205 group_list_ndx = 0;
1206 SAFE_FREE(gr_mem_list);
1207 request_error(state);
1208 return;
1211 memcpy(&((char *)state->response->extra_data.data)
1212 [group_list_ndx * sizeof(struct winbindd_gr)],
1213 gr_mem_list, gr_mem_list_len);
1215 state->response->length += gr_mem_list_len;
1217 DEBUG(10, ("returning %d groups, length = %d\n",
1218 group_list_ndx, gr_mem_list_len));
1220 /* Out of domains */
1222 done:
1224 SAFE_FREE(gr_mem_list);
1226 if (group_list_ndx > 0)
1227 request_ok(state);
1228 else
1229 request_error(state);
1232 /* List domain groups without mapping to unix ids */
1233 void winbindd_list_groups(struct winbindd_cli_state *state)
1235 winbindd_list_ent(state, LIST_GROUPS);
1238 /* Get user supplementary groups. This is much quicker than trying to
1239 invert the groups database. We merge the groups from the gids and
1240 other_sids info3 fields as trusted domain, universal group
1241 memberships, and nested groups (win2k native mode only) are not
1242 returned by the getgroups RPC call but are present in the info3. */
1244 struct getgroups_state {
1245 struct winbindd_cli_state *state;
1246 struct winbindd_domain *domain;
1247 char *domname;
1248 char *username;
1249 DOM_SID user_sid;
1251 const DOM_SID *token_sids;
1252 size_t i, num_token_sids;
1254 gid_t *token_gids;
1255 size_t num_token_gids;
1258 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1259 struct winbindd_cli_state *state)
1261 DOM_SID user_sid;
1262 NTSTATUS status;
1264 char *sidstring;
1265 ssize_t len;
1266 DOM_SID *groups;
1267 uint32 num_groups;
1269 /* Ensure null termination */
1270 state->request->data.sid[sizeof(state->request->data.sid)-1]='\0';
1272 if (!string_to_sid(&user_sid, state->request->data.sid)) {
1273 DEBUG(1, ("Could not get convert sid %s from string\n",
1274 state->request->data.sid));
1275 return WINBINDD_ERROR;
1278 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1279 &user_sid, &num_groups,
1280 &groups);
1281 if (!NT_STATUS_IS_OK(status))
1282 return WINBINDD_ERROR;
1284 if (num_groups == 0) {
1285 state->response->data.num_entries = 0;
1286 state->response->extra_data.data = NULL;
1287 return WINBINDD_OK;
1290 if (!print_sidlist(state->mem_ctx,
1291 groups, num_groups,
1292 &sidstring, &len)) {
1293 DEBUG(0, ("talloc failed\n"));
1294 return WINBINDD_ERROR;
1297 state->response->extra_data.data = sidstring;
1298 state->response->length += len+1;
1299 state->response->data.num_entries = num_groups;
1301 return WINBINDD_OK;
1304 enum winbindd_result winbindd_dual_getsidaliases(struct winbindd_domain *domain,
1305 struct winbindd_cli_state *state)
1307 DOM_SID *sids = NULL;
1308 size_t num_sids = 0;
1309 char *sidstr = NULL;
1310 ssize_t len;
1311 size_t i;
1312 uint32 num_aliases;
1313 uint32 *alias_rids;
1314 NTSTATUS result;
1316 DEBUG(3, ("[%5lu]: getsidaliases\n", (unsigned long)state->pid));
1318 sidstr = state->request->extra_data.data;
1319 if (sidstr == NULL) {
1320 sidstr = talloc_strdup(state->mem_ctx, "\n"); /* No SID */
1321 if (!sidstr) {
1322 DEBUG(0, ("Out of memory\n"));
1323 return WINBINDD_ERROR;
1327 DEBUG(10, ("Sidlist: %s\n", sidstr));
1329 if (!parse_sidlist(state->mem_ctx, sidstr, &sids, &num_sids)) {
1330 DEBUG(0, ("Could not parse SID list: %s\n", sidstr));
1331 return WINBINDD_ERROR;
1334 num_aliases = 0;
1335 alias_rids = NULL;
1337 result = domain->methods->lookup_useraliases(domain,
1338 state->mem_ctx,
1339 num_sids, sids,
1340 &num_aliases,
1341 &alias_rids);
1343 if (!NT_STATUS_IS_OK(result)) {
1344 DEBUG(3, ("Could not lookup_useraliases: %s\n",
1345 nt_errstr(result)));
1346 return WINBINDD_ERROR;
1349 num_sids = 0;
1350 sids = NULL;
1351 sidstr = NULL;
1353 DEBUG(10, ("Got %d aliases\n", num_aliases));
1355 for (i=0; i<num_aliases; i++) {
1356 DOM_SID sid;
1357 DEBUGADD(10, (" rid %d\n", alias_rids[i]));
1358 sid_copy(&sid, &domain->sid);
1359 sid_append_rid(&sid, alias_rids[i]);
1360 result = add_sid_to_array(state->mem_ctx, &sid, &sids,
1361 &num_sids);
1362 if (!NT_STATUS_IS_OK(result)) {
1363 return WINBINDD_ERROR;
1368 if (!print_sidlist(state->mem_ctx, sids, num_sids, &sidstr, &len)) {
1369 DEBUG(0, ("Could not print_sidlist\n"));
1370 state->response->extra_data.data = NULL;
1371 return WINBINDD_ERROR;
1374 state->response->extra_data.data = NULL;
1376 if (sidstr) {
1377 state->response->extra_data.data = sidstr;
1378 DEBUG(10, ("aliases_list: %s\n",
1379 (char *)state->response->extra_data.data));
1380 state->response->length += len+1;
1381 state->response->data.num_entries = num_sids;
1384 return WINBINDD_OK;
1387 struct getgr_countmem {
1388 int num;
1389 size_t len;
1392 static int getgr_calc_memberlen(DATA_BLOB key, void *data, void *priv)
1394 struct wbint_GroupMember *m = talloc_get_type_abort(
1395 data, struct wbint_GroupMember);
1396 struct getgr_countmem *buf = (struct getgr_countmem *)priv;
1398 buf->num += 1;
1399 buf->len += strlen(m->name) + 1;
1400 return 0;
1403 struct getgr_stringmem {
1404 size_t ofs;
1405 char *buf;
1408 static int getgr_unparse_members(DATA_BLOB key, void *data, void *priv)
1410 struct wbint_GroupMember *m = talloc_get_type_abort(
1411 data, struct wbint_GroupMember);
1412 struct getgr_stringmem *buf = (struct getgr_stringmem *)priv;
1413 int len;
1415 len = strlen(m->name);
1417 memcpy(buf->buf + buf->ofs, m->name, len);
1418 buf->ofs += len;
1419 buf->buf[buf->ofs] = ',';
1420 buf->ofs += 1;
1421 return 0;
1424 NTSTATUS winbindd_print_groupmembers(struct talloc_dict *members,
1425 TALLOC_CTX *mem_ctx,
1426 int *num_members, char **result)
1428 struct getgr_countmem c;
1429 struct getgr_stringmem m;
1430 int res;
1432 c.num = 0;
1433 c.len = 0;
1435 res = talloc_dict_traverse(members, getgr_calc_memberlen, &c);
1436 if (res != 0) {
1437 DEBUG(5, ("talloc_dict_traverse failed\n"));
1438 return NT_STATUS_INTERNAL_ERROR;
1441 m.ofs = 0;
1442 m.buf = talloc_array(mem_ctx, char, c.len);
1443 if (m.buf == NULL) {
1444 DEBUG(5, ("talloc failed\n"));
1445 return NT_STATUS_NO_MEMORY;
1448 res = talloc_dict_traverse(members, getgr_unparse_members, &m);
1449 if (res != 0) {
1450 DEBUG(5, ("talloc_dict_traverse failed\n"));
1451 TALLOC_FREE(m.buf);
1452 return NT_STATUS_INTERNAL_ERROR;
1454 m.buf[c.len-1] = '\0';
1456 *num_members = c.num;
1457 *result = m.buf;
1458 return NT_STATUS_OK;