r23730: Squashed commit of the following:
[Samba/bb.git] / source / nsswitch / winbindd_group.c
blob2b2ae7e5795861297029aa177512d58837f9e405
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
9 Copyright (C) Volker Lendecke 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "includes.h"
27 #include "winbindd.h"
29 extern BOOL opt_nocache;
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
34 static void add_member(const char *domain, const char *user,
35 char **pp_members, size_t *p_num_members)
37 fstring name;
39 fill_domain_username(name, domain, user, True);
40 safe_strcat(name, ",", sizeof(name)-1);
41 string_append(pp_members, name);
42 *p_num_members += 1;
45 /**********************************************************************
46 Add member users resulting from sid. Expand if it is a domain group.
47 **********************************************************************/
49 static void add_expanded_sid(const DOM_SID *sid, char **pp_members, size_t *p_num_members)
51 DOM_SID dom_sid;
52 uint32 rid;
53 struct winbindd_domain *domain;
54 size_t i;
56 char *domain_name = NULL;
57 char *name = NULL;
58 enum lsa_SidType type;
60 uint32 num_names;
61 DOM_SID *sid_mem;
62 char **names;
63 uint32 *types;
65 NTSTATUS result;
67 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
69 if (mem_ctx == NULL) {
70 DEBUG(1, ("talloc_init failed\n"));
71 return;
74 sid_copy(&dom_sid, sid);
75 sid_split_rid(&dom_sid, &rid);
77 domain = find_lookup_domain_from_sid(sid);
79 if (domain == NULL) {
80 DEBUG(3, ("Could not find domain for sid %s\n",
81 sid_string_static(sid)));
82 goto done;
85 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
86 &domain_name, &name, &type);
88 if (!NT_STATUS_IS_OK(result)) {
89 DEBUG(3, ("sid_to_name failed for sid %s\n",
90 sid_string_static(sid)));
91 goto done;
94 DEBUG(10, ("Found name %s, type %d\n", name, type));
96 if (type == SID_NAME_USER) {
97 add_member(domain_name, name, pp_members, p_num_members);
98 goto done;
101 if (type != SID_NAME_DOM_GRP) {
102 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
103 name));
104 goto done;
107 /* Expand the domain group, this must be done via the target domain */
109 domain = find_domain_from_sid(sid);
111 if (domain == NULL) {
112 DEBUG(3, ("Could not find domain from SID %s\n",
113 sid_string_static(sid)));
114 goto done;
117 result = domain->methods->lookup_groupmem(domain, mem_ctx,
118 sid, &num_names,
119 &sid_mem, &names,
120 &types);
122 if (!NT_STATUS_IS_OK(result)) {
123 DEBUG(10, ("Could not lookup group members for %s: %s\n",
124 name, nt_errstr(result)));
125 goto done;
128 for (i=0; i<num_names; i++) {
129 DEBUG(10, ("Adding group member SID %s\n",
130 sid_string_static(&sid_mem[i])));
132 if (types[i] != SID_NAME_USER) {
133 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
134 "Ignoring.\n", names[i], name));
135 continue;
138 add_member(domain->name, names[i], pp_members, p_num_members);
141 done:
142 talloc_destroy(mem_ctx);
143 return;
146 static BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain,
147 DOM_SID *group_sid,
148 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
150 DOM_SID *members;
151 size_t i, num_members;
153 *num_gr_mem = 0;
154 *gr_mem = NULL;
155 *gr_mem_len = 0;
157 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
158 &num_members)))
159 return True;
161 for (i=0; i<num_members; i++) {
162 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
165 TALLOC_FREE(members);
167 if (*gr_mem != NULL) {
168 size_t len;
170 /* We have at least one member, strip off the last "," */
171 len = strlen(*gr_mem);
172 (*gr_mem)[len-1] = '\0';
173 *gr_mem_len = len;
176 return True;
179 /* Fill a grent structure from various other information */
181 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
182 const char *gr_name, gid_t unix_gid)
184 fstring full_group_name;
186 fill_domain_username( full_group_name, dom_name, gr_name, True );
188 gr->gr_gid = unix_gid;
190 /* Group name and password */
192 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
193 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
195 return True;
198 /***********************************************************************
199 If "enum users" is set to false, and the group being looked
200 up is the Domain Users SID: S-1-5-domain-513, then for the
201 list of members check if the querying user is in that group,
202 and if so only return that user as the gr_mem array.
203 We can change this to a different parameter than "enum users"
204 if neccessaey, or parameterize the group list we do this for.
205 ***********************************************************************/
207 static BOOL fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
208 struct winbindd_domain *domain,
209 struct winbindd_cli_state *state,
210 DOM_SID *group_sid,
211 enum lsa_SidType group_name_type,
212 size_t *num_gr_mem, char **gr_mem,
213 size_t *gr_mem_len)
215 DOM_SID querying_user_sid;
216 DOM_SID *pquerying_user_sid = NULL;
217 uint32 num_groups = 0;
218 DOM_SID *user_sids = NULL;
219 BOOL u_in_group = False;
220 NTSTATUS status;
221 int i;
222 unsigned int buf_len = 0;
223 char *buf = NULL;
225 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
226 domain->name ));
228 if (state) {
229 uid_t ret_uid = (uid_t)-1;
230 if (sys_getpeereid(state->sock, &ret_uid)==0) {
231 /* We know who's asking - look up their SID if
232 it's one we've mapped before. */
233 status = idmap_uid_to_sid(&querying_user_sid, ret_uid);
234 if (NT_STATUS_IS_OK(status)) {
235 pquerying_user_sid = &querying_user_sid;
236 DEBUG(10,("fill_grent_mem_domain_users: querying uid %u -> %s\n",
237 (unsigned int)ret_uid,
238 sid_string_static(pquerying_user_sid) ));
243 /* Only look up if it was a winbindd user in this domain. */
244 if (pquerying_user_sid &&
245 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
247 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
248 sid_string_static(pquerying_user_sid) ));
250 status = domain->methods->lookup_usergroups(domain,
251 mem_ctx,
252 pquerying_user_sid,
253 &num_groups,
254 &user_sids);
255 if (!NT_STATUS_IS_OK(status)) {
256 DEBUG(1, ("fill_grent_mem_domain_users: lookup_usergroups failed "
257 "for sid %s in domain %s (error: %s)\n",
258 sid_string_static(pquerying_user_sid),
259 domain->name,
260 nt_errstr(status)));
261 return False;
264 for (i = 0; i < num_groups; i++) {
265 if (sid_equal(group_sid, &user_sids[i])) {
266 /* User is in Domain Users, add their name
267 as the only group member. */
268 u_in_group = True;
269 break;
274 if (u_in_group) {
275 size_t len = 0;
276 char *domainname = NULL;
277 char *username = NULL;
278 fstring name;
279 enum lsa_SidType type;
281 DEBUG(10,("fill_grent_mem_domain_users: sid %s in 'Domain Users' in domain %s\n",
282 sid_string_static(pquerying_user_sid), domain->name ));
284 status = domain->methods->sid_to_name(domain, mem_ctx,
285 pquerying_user_sid,
286 &domainname,
287 &username,
288 &type);
289 if (!NT_STATUS_IS_OK(status)) {
290 DEBUG(1, ("could not lookup username for user "
291 "sid %s in domain %s (error: %s)\n",
292 sid_string_static(pquerying_user_sid),
293 domain->name,
294 nt_errstr(status)));
295 return False;
297 fill_domain_username(name, domain->name, username, True);
298 len = strlen(name);
299 buf_len = len + 1;
300 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
301 DEBUG(1, ("out of memory\n"));
302 return False;
304 memcpy(buf, name, buf_len);
306 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
307 "'Domain Users' in domain %s\n",
308 name, domain->name ));
310 /* user is the only member */
311 *num_gr_mem = 1;
314 *gr_mem = buf;
315 *gr_mem_len = buf_len;
317 DEBUG(10, ("fill_grent_mem_domain_users: num_mem = %u, len = %u, mem = %s\n",
318 (unsigned int)*num_gr_mem,
319 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
321 return True;
324 /***********************************************************************
325 Add names to a list. Assumes a canonical version of the string
326 in DOMAIN\user
327 ***********************************************************************/
329 static int namecmp( const void *a, const void *b )
331 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
334 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
335 char ***list, uint32 *n_list,
336 char **names, uint32 n_names )
338 char **new_list = NULL;
339 uint32 n_new_list = 0;
340 int i, j;
342 if ( !names || (n_names == 0) )
343 return NT_STATUS_OK;
345 /* Alloc the maximum size we'll need */
347 if ( *list == NULL ) {
348 if ( (new_list = TALLOC_ARRAY( ctx, char *, n_names )) == NULL )
349 return NT_STATUS_NO_MEMORY;
350 n_new_list = n_names;
351 } else {
352 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
353 (*n_list) + n_names );
354 if ( !new_list )
355 return NT_STATUS_NO_MEMORY;
356 n_new_list = (*n_list) + n_names;
359 /* Add all names */
361 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
362 new_list[i] = talloc_strdup( new_list, names[j] );
365 /* search for duplicates for sorting and looking for matching
366 neighbors */
368 qsort( new_list, n_new_list, sizeof(char*), QSORT_CAST namecmp );
370 for ( i=1; i<n_new_list; i++ ) {
371 if ( strcmp( new_list[i-1], new_list[i] ) == 0 ) {
372 memmove( &new_list[i-1], &new_list[i],
373 sizeof(char*)*(n_new_list-i) );
374 n_new_list--;
378 *list = new_list;
379 *n_list = n_new_list;
381 return NT_STATUS_OK;
384 /***********************************************************************
385 ***********************************************************************/
387 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
388 struct winbindd_domain *d,
389 DOM_SID *glist, uint32 n_glist,
390 DOM_SID **new_glist, uint32 *n_new_glist,
391 char ***members, uint32 *n_members )
393 int i, j;
394 NTSTATUS status = NT_STATUS_OK;
395 uint32 num_names = 0;
396 uint32 *name_types = NULL;
397 char **names = NULL;
398 DOM_SID *sid_mem = NULL;
399 TALLOC_CTX *tmp_ctx = NULL;
400 DOM_SID *new_groups = NULL;
401 size_t new_groups_size = 0;
403 *members = NULL;
404 *n_members = 0;
405 *new_glist = NULL;
406 *n_new_glist = 0;
408 for ( i=0; i<n_glist; i++ ) {
409 tmp_ctx = talloc_new( ctx );
411 /* Lookup the group membership */
413 status = d->methods->lookup_groupmem(d, tmp_ctx,
414 &glist[i], &num_names,
415 &sid_mem, &names,
416 &name_types);
417 if ( !NT_STATUS_IS_OK(status) )
418 goto out;
420 /* Separate users and groups into two lists */
422 for ( j=0; j<num_names; j++ ) {
424 /* Users */
425 if ( name_types[j] == SID_NAME_USER ||
426 name_types[j] == SID_NAME_COMPUTER )
428 status = add_names_to_list( ctx, members,
429 n_members,
430 names+j, 1 );
431 if ( !NT_STATUS_IS_OK(status) )
432 goto out;
434 continue;
437 /* Groups */
438 if ( name_types[j] == SID_NAME_DOM_GRP ||
439 name_types[j] == SID_NAME_ALIAS )
441 BOOL ret;
443 ret = add_sid_to_array_unique( ctx,
444 &sid_mem[j],
445 &new_groups,
446 &new_groups_size );
447 if ( !ret ) {
448 status = NT_STATUS_NO_MEMORY;
449 goto out;
452 continue;
456 TALLOC_FREE( tmp_ctx );
459 *new_glist = new_groups;
460 *n_new_glist = (uint32)new_groups_size;
462 out:
463 TALLOC_FREE( tmp_ctx );
465 return status;
468 /***********************************************************************
469 Fill in the group membership field of a NT group given by group_sid
470 ***********************************************************************/
472 static BOOL fill_grent_mem(struct winbindd_domain *domain,
473 struct winbindd_cli_state *state,
474 DOM_SID *group_sid,
475 enum lsa_SidType group_name_type,
476 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
478 uint32 num_names = 0;
479 unsigned int buf_len = 0, buf_ndx = 0, i;
480 char **names = NULL, *buf = NULL;
481 BOOL result = False;
482 TALLOC_CTX *mem_ctx;
483 uint32 group_rid;
484 DOM_SID *glist = NULL;
485 DOM_SID *new_glist = NULL;
486 uint32 n_glist, n_new_glist;
487 int max_depth = lp_winbind_expand_groups();
489 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
490 return False;
492 DEBUG(10, ("group SID %s\n", sid_string_static(group_sid)));
494 /* Initialize with no members */
496 *num_gr_mem = 0;
498 /* HACK ALERT!! This whole routine does not cope with group members
499 * from more than one domain, ie aliases. Thus we have to work it out
500 * ourselves in a special routine. */
502 if (domain->internal) {
503 result = fill_passdb_alias_grmem(domain, group_sid,
504 num_gr_mem,
505 gr_mem, gr_mem_len);
506 goto done;
509 /* Verify name type */
511 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
512 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
514 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
515 sid_string_static(group_sid), domain->name,
516 group_name_type));
517 goto done;
520 /* OPTIMIZATION / HACK. See comment in
521 fill_grent_mem_domusers() */
523 sid_peek_rid( group_sid, &group_rid );
524 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
525 result = fill_grent_mem_domusers( mem_ctx, domain, state,
526 group_sid, group_name_type,
527 num_gr_mem, gr_mem,
528 gr_mem_len );
529 goto done;
532 /* Real work goes here. Create a list of group names to
533 expand startign with the initial one. Pass that to
534 expand_groups() which returns a list of more group names
535 to expand. Do this up to the max search depth. */
537 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
538 result = False;
539 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
540 goto done;
542 sid_copy( &glist[0], group_sid );
543 n_glist = 1;
545 for ( i=0; i<max_depth && glist; i++ ) {
546 uint32 n_members = 0;
547 char **members = NULL;
548 NTSTATUS nt_status;
550 nt_status = expand_groups( mem_ctx, domain,
551 glist, n_glist,
552 &new_glist, &n_new_glist,
553 &members, &n_members);
554 if ( !NT_STATUS_IS_OK(nt_status) ) {
555 result = False;
556 goto done;
559 /* Add new group members to list */
561 nt_status = add_names_to_list( mem_ctx, &names, &num_names,
562 members, n_members );
563 if ( !NT_STATUS_IS_OK(nt_status) ) {
564 result = False;
565 goto done;
568 TALLOC_FREE( members );
570 /* If we have no more groups to expand, break out
571 early */
573 if ( !&new_glist )
574 break;
576 /* One more round */
577 TALLOC_FREE(glist);
578 glist = new_glist;
579 n_glist = n_new_glist;
581 TALLOC_FREE( glist );
583 DEBUG(10, ("looked up %d names\n", num_names));
585 again:
586 /* Add members to list */
588 for (i = 0; i < num_names; i++) {
589 int len;
591 DEBUG(10, ("processing name %s\n", names[i]));
593 len = strlen(names[i]);
595 /* Add to list or calculate buffer length */
597 if (!buf) {
598 buf_len += len + 1; /* List is comma separated */
599 (*num_gr_mem)++;
600 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
601 } else {
602 DEBUG(10, ("appending %s at ndx %d\n", names[i], buf_ndx));
603 safe_strcpy(&buf[buf_ndx], names[i], len);
604 buf_ndx += len;
605 buf[buf_ndx] = ',';
606 buf_ndx++;
610 /* Allocate buffer */
612 if (!buf && buf_len != 0) {
613 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
614 DEBUG(1, ("out of memory\n"));
615 result = False;
616 goto done;
618 memset(buf, 0, buf_len);
619 goto again;
622 /* Now we're done */
624 if (buf && buf_ndx > 0) {
625 buf[buf_ndx - 1] = '\0';
628 *gr_mem = buf;
629 *gr_mem_len = buf_len;
631 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
632 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
633 result = True;
635 done:
637 talloc_destroy(mem_ctx);
639 DEBUG(10, ("fill_grent_mem returning %d\n", result));
641 return result;
644 static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid );
646 static void getgrnam_recv( void *private_data, BOOL success, const DOM_SID *sid,
647 enum lsa_SidType type )
649 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
651 if (!success) {
652 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
653 request_error(state);
654 return;
657 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
658 DEBUG(5,("getgrnam_recv: not a group!\n"));
659 request_error(state);
660 return;
663 winbindd_getgrsid( state, *sid );
667 /* Return a group structure from a group name */
669 void winbindd_getgrnam(struct winbindd_cli_state *state)
671 struct winbindd_domain *domain;
672 fstring name_domain, name_group;
673 char *tmp;
675 /* Ensure null termination */
676 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
678 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
679 state->request.data.groupname));
681 /* Parse domain and groupname */
683 memset(name_group, 0, sizeof(fstring));
685 tmp = state->request.data.groupname;
687 name_domain[0] = '\0';
688 name_group[0] = '\0';
690 parse_domain_user(tmp, name_domain, name_group);
692 /* if no domain or our local domain and no local tdb group, default to
693 * our local domain for aliases */
695 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
696 fstrcpy(name_domain, get_global_sam_name());
699 /* Get info for the domain */
701 if ((domain = find_domain_from_name(name_domain)) == NULL) {
702 DEBUG(3, ("could not get domain sid for domain %s\n",
703 name_domain));
704 request_error(state);
705 return;
707 /* should we deal with users for our domain? */
709 if ( lp_winbind_trusted_domains_only() && domain->primary) {
710 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
711 "getgrnam() for %s\\%s.\n", name_domain, name_group));
712 request_error(state);
713 return;
716 /* Get rid and name type from name */
718 ws_name_replace( name_group, WB_REPLACE_CHAR );
720 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
721 getgrnam_recv, WINBINDD_GETGRNAM, state );
724 struct getgrsid_state {
725 struct winbindd_cli_state *state;
726 struct winbindd_domain *domain;
727 char *group_name;
728 enum lsa_SidType group_type;
729 uid_t gid;
730 DOM_SID group_sid;
733 static void getgrsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
735 struct getgrsid_state *s =
736 (struct getgrsid_state *)private_data;
737 struct winbindd_domain *domain;
738 size_t gr_mem_len;
739 size_t num_gr_mem;
740 char *gr_mem;
741 fstring dom_name, group_name;
743 if (!success) {
744 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
745 request_error(s->state);
746 return;
749 s->gid = gid;
751 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
752 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
753 request_error(s->state);
754 return;
758 /* Fill in group structure */
760 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
761 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
762 request_error(s->state);
763 return;
766 if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
767 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
768 &num_gr_mem, &gr_mem, &gr_mem_len))
770 request_error(s->state);
771 return;
774 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
776 /* Group membership lives at start of extra data */
778 s->state->response.data.gr.gr_mem_ofs = 0;
780 s->state->response.length += gr_mem_len;
781 s->state->response.extra_data.data = gr_mem;
783 request_ok(s->state);
786 static void getgrsid_lookupsid_recv( void *private_data, BOOL success,
787 const char *dom_name, const char *name,
788 enum lsa_SidType name_type )
790 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
792 if (!success) {
793 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
794 request_error(s->state);
795 return;
798 /* either it's a domain group, a domain local group, or a
799 local group in an internal domain */
801 if ( !( (name_type==SID_NAME_DOM_GRP) ||
802 ((name_type==SID_NAME_ALIAS) &&
803 (s->domain->primary || s->domain->internal)) ) )
805 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
806 dom_name, name, name_type));
807 request_error(s->state);
808 return;
811 if ( (s->group_name = talloc_asprintf( s->state->mem_ctx,
812 "%s\\%s",
813 dom_name, name )) == NULL )
815 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
816 request_error(s->state);
817 return;
820 s->group_type = name_type;
822 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
823 getgrsid_sid2gid_recv, s);
826 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
828 struct getgrsid_state *s;
830 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
831 DEBUG(0, ("talloc failed\n"));
832 request_error(state);
833 return;
836 s->state = state;
838 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
839 DEBUG(3, ("Could not find domain for sid %s\n",
840 sid_string_static(&group_sid)));
841 request_error(state);
842 return;
845 sid_copy(&s->group_sid, &group_sid);
847 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
848 getgrsid_lookupsid_recv, s );
852 static void getgrgid_recv(void *private_data, BOOL success, const char *sid)
854 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
855 enum lsa_SidType name_type;
856 DOM_SID group_sid;
858 if (success) {
859 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
860 (unsigned long)(state->request.data.gid), sid));
862 string_to_sid(&group_sid, sid);
863 winbindd_getgrsid(state, group_sid);
864 return;
867 /* Ok, this might be "ours", i.e. an alias */
868 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
869 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
870 (name_type == SID_NAME_ALIAS)) {
871 /* Hey, got an alias */
872 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
873 (unsigned long)(state->request.data.gid), sid));
874 winbindd_getgrsid(state, group_sid);
875 return;
878 DEBUG(1, ("could not convert gid %lu to sid\n",
879 (unsigned long)state->request.data.gid));
880 request_error(state);
883 /* Return a group structure from a gid number */
884 void winbindd_getgrgid(struct winbindd_cli_state *state)
886 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
887 (unsigned long)state->request.data.gid));
889 /* always use the async interface */
890 winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
894 * set/get/endgrent functions
897 /* "Rewind" file pointer for group database enumeration */
899 static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state)
901 struct winbindd_domain *domain;
903 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
905 /* Check user has enabled this */
907 if (!lp_winbind_enum_groups()) {
908 return False;
911 /* Free old static data if it exists */
913 if (state->getgrent_state != NULL) {
914 free_getent_state(state->getgrent_state);
915 state->getgrent_state = NULL;
918 /* Create sam pipes for each domain we know about */
920 for (domain = domain_list(); domain != NULL; domain = domain->next) {
921 struct getent_state *domain_state;
923 /* Create a state record for this domain */
925 /* don't add our domaina if we are a PDC or if we
926 are a member of a Samba domain */
928 if ( lp_winbind_trusted_domains_only() && domain->primary )
930 continue;
934 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
935 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
936 return False;
939 ZERO_STRUCTP(domain_state);
941 fstrcpy(domain_state->domain_name, domain->name);
943 /* Add to list of open domains */
945 DLIST_ADD(state->getgrent_state, domain_state);
948 state->getgrent_initialized = True;
949 return True;
952 void winbindd_setgrent(struct winbindd_cli_state *state)
954 if (winbindd_setgrent_internal(state)) {
955 request_ok(state);
956 } else {
957 request_error(state);
961 /* Close file pointer to ntdom group database */
963 void winbindd_endgrent(struct winbindd_cli_state *state)
965 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
967 free_getent_state(state->getgrent_state);
968 state->getgrent_initialized = False;
969 state->getgrent_state = NULL;
970 request_ok(state);
973 /* Get the list of domain groups and domain aliases for a domain. We fill in
974 the sam_entries and num_sam_entries fields with domain group information.
975 The dispinfo_ndx field is incremented to the index of the next group to
976 fetch. Return True if some groups were returned, False otherwise. */
978 static BOOL get_sam_group_entries(struct getent_state *ent)
980 NTSTATUS status;
981 uint32 num_entries;
982 struct acct_info *name_list = NULL;
983 TALLOC_CTX *mem_ctx;
984 BOOL result = False;
985 struct acct_info *sam_grp_entries = NULL;
986 struct winbindd_domain *domain;
988 if (ent->got_sam_entries)
989 return False;
991 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
992 ent->domain_name))) {
993 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
994 return False;
997 /* Free any existing group info */
999 SAFE_FREE(ent->sam_entries);
1000 ent->num_sam_entries = 0;
1001 ent->got_sam_entries = True;
1003 /* Enumerate domain groups */
1005 num_entries = 0;
1007 if (!(domain = find_domain_from_name(ent->domain_name))) {
1008 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
1009 goto done;
1012 /* always get the domain global groups */
1014 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1016 if (!NT_STATUS_IS_OK(status)) {
1017 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
1018 result = False;
1019 goto done;
1022 /* Copy entries into return buffer */
1024 if (num_entries) {
1025 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
1026 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
1027 num_entries));
1028 result = False;
1029 goto done;
1031 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
1034 ent->num_sam_entries = num_entries;
1036 /* get the domain local groups if we are a member of a native win2k domain
1037 and are not using LDAP to get the groups */
1039 if ( ( lp_security() != SEC_ADS && domain->native_mode
1040 && domain->primary) || domain->internal )
1042 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
1043 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
1045 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1047 if ( !NT_STATUS_IS_OK(status) ) {
1048 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
1049 num_entries = 0;
1051 else
1052 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
1054 /* Copy entries into return buffer */
1056 if ( num_entries ) {
1057 if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
1059 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
1060 num_entries));
1061 result = False;
1062 goto done;
1065 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
1066 num_entries * sizeof(struct acct_info) );
1069 ent->num_sam_entries += num_entries;
1073 /* Fill in remaining fields */
1075 ent->sam_entries = name_list;
1076 ent->sam_entry_index = 0;
1078 result = (ent->num_sam_entries > 0);
1080 done:
1081 talloc_destroy(mem_ctx);
1083 return result;
1086 /* Fetch next group entry from ntdom database */
1088 #define MAX_GETGRENT_GROUPS 500
1090 void winbindd_getgrent(struct winbindd_cli_state *state)
1092 struct getent_state *ent;
1093 struct winbindd_gr *group_list = NULL;
1094 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1095 char *gr_mem_list = NULL;
1097 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1099 /* Check user has enabled this */
1101 if (!lp_winbind_enum_groups()) {
1102 request_error(state);
1103 return;
1106 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1108 if (num_groups == 0) {
1109 request_error(state);
1110 return;
1113 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
1114 request_error(state);
1115 return;
1118 memset(state->response.extra_data.data, '\0',
1119 num_groups * sizeof(struct winbindd_gr) );
1121 state->response.data.num_entries = 0;
1123 group_list = (struct winbindd_gr *)state->response.extra_data.data;
1125 if (!state->getgrent_initialized)
1126 winbindd_setgrent_internal(state);
1128 if (!(ent = state->getgrent_state)) {
1129 request_error(state);
1130 return;
1133 /* Start sending back groups */
1135 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1136 struct acct_info *name_list = NULL;
1137 fstring domain_group_name;
1138 uint32 result;
1139 gid_t group_gid;
1140 size_t gr_mem_len;
1141 char *gr_mem;
1142 DOM_SID group_sid;
1143 struct winbindd_domain *domain;
1145 /* Do we need to fetch another chunk of groups? */
1147 tryagain:
1149 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1150 ent->sam_entry_index, ent->num_sam_entries));
1152 if (ent->num_sam_entries == ent->sam_entry_index) {
1154 while(ent && !get_sam_group_entries(ent)) {
1155 struct getent_state *next_ent;
1157 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
1159 /* Free state information for this domain */
1161 SAFE_FREE(ent->sam_entries);
1163 next_ent = ent->next;
1164 DLIST_REMOVE(state->getgrent_state, ent);
1166 SAFE_FREE(ent);
1167 ent = next_ent;
1170 /* No more domains */
1172 if (!ent)
1173 break;
1176 name_list = (struct acct_info *)ent->sam_entries;
1178 if (!(domain =
1179 find_domain_from_name(ent->domain_name))) {
1180 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
1181 result = False;
1182 goto done;
1185 /* Lookup group info */
1187 sid_copy(&group_sid, &domain->sid);
1188 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1190 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) {
1191 union unid_t id;
1192 enum lsa_SidType type;
1194 DEBUG(10, ("SID %s not in idmap\n",
1195 sid_string_static(&group_sid)));
1197 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1198 DEBUG(1, ("could not look up gid for group "
1199 "%s\n",
1200 name_list[ent->sam_entry_index].acct_name));
1201 ent->sam_entry_index++;
1202 goto tryagain;
1205 if ((type != SID_NAME_DOM_GRP) &&
1206 (type != SID_NAME_ALIAS) &&
1207 (type != SID_NAME_WKN_GRP)) {
1208 DEBUG(1, ("Group %s is a %s, not a group\n",
1209 sid_type_lookup(type),
1210 name_list[ent->sam_entry_index].acct_name));
1211 ent->sam_entry_index++;
1212 goto tryagain;
1214 group_gid = id.gid;
1217 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
1218 (unsigned long)name_list[ent->sam_entry_index].rid));
1220 /* Fill in group entry */
1222 fill_domain_username(domain_group_name, ent->domain_name,
1223 name_list[ent->sam_entry_index].acct_name, True);
1225 result = fill_grent(&group_list[group_list_ndx],
1226 ent->domain_name,
1227 name_list[ent->sam_entry_index].acct_name,
1228 group_gid);
1230 /* Fill in group membership entry */
1232 if (result) {
1233 size_t num_gr_mem = 0;
1234 DOM_SID member_sid;
1235 group_list[group_list_ndx].num_gr_mem = 0;
1236 gr_mem = NULL;
1237 gr_mem_len = 0;
1239 /* Get group membership */
1240 if (state->request.cmd == WINBINDD_GETGRLST) {
1241 result = True;
1242 } else {
1243 sid_copy(&member_sid, &domain->sid);
1244 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1245 result = fill_grent_mem(
1246 domain,
1247 NULL,
1248 &member_sid,
1249 SID_NAME_DOM_GRP,
1250 &num_gr_mem,
1251 &gr_mem, &gr_mem_len);
1253 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1257 if (result) {
1258 /* Append to group membership list */
1259 gr_mem_list = (char *)SMB_REALLOC(
1260 gr_mem_list, gr_mem_list_len + gr_mem_len);
1262 if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
1263 DEBUG(0, ("out of memory\n"));
1264 gr_mem_list_len = 0;
1265 break;
1268 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1269 gr_mem_list_len, (unsigned int)gr_mem_len));
1271 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1272 gr_mem_len);
1274 SAFE_FREE(gr_mem);
1276 group_list[group_list_ndx].gr_mem_ofs =
1277 gr_mem_list_len;
1279 gr_mem_list_len += gr_mem_len;
1282 ent->sam_entry_index++;
1284 /* Add group to return list */
1286 if (result) {
1288 DEBUG(10, ("adding group num_entries = %d\n",
1289 state->response.data.num_entries));
1291 group_list_ndx++;
1292 state->response.data.num_entries++;
1294 state->response.length +=
1295 sizeof(struct winbindd_gr);
1297 } else {
1298 DEBUG(0, ("could not lookup domain group %s\n",
1299 domain_group_name));
1303 /* Copy the list of group memberships to the end of the extra data */
1305 if (group_list_ndx == 0)
1306 goto done;
1308 state->response.extra_data.data = SMB_REALLOC(
1309 state->response.extra_data.data,
1310 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1312 if (!state->response.extra_data.data) {
1313 DEBUG(0, ("out of memory\n"));
1314 group_list_ndx = 0;
1315 SAFE_FREE(gr_mem_list);
1316 request_error(state);
1317 return;
1320 memcpy(&((char *)state->response.extra_data.data)
1321 [group_list_ndx * sizeof(struct winbindd_gr)],
1322 gr_mem_list, gr_mem_list_len);
1324 state->response.length += gr_mem_list_len;
1326 DEBUG(10, ("returning %d groups, length = %d\n",
1327 group_list_ndx, gr_mem_list_len));
1329 /* Out of domains */
1331 done:
1333 SAFE_FREE(gr_mem_list);
1335 if (group_list_ndx > 0)
1336 request_ok(state);
1337 else
1338 request_error(state);
1341 /* List domain groups without mapping to unix ids */
1343 void winbindd_list_groups(struct winbindd_cli_state *state)
1345 uint32 total_entries = 0;
1346 struct winbindd_domain *domain;
1347 const char *which_domain;
1348 char *extra_data = NULL;
1349 unsigned int extra_data_len = 0, i;
1351 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
1353 /* Ensure null termination */
1354 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
1355 which_domain = state->request.domain_name;
1357 /* Enumerate over trusted domains */
1359 for (domain = domain_list(); domain; domain = domain->next) {
1360 struct getent_state groups;
1362 /* if we have a domain name restricting the request and this
1363 one in the list doesn't match, then just bypass the remainder
1364 of the loop */
1366 if ( *which_domain && !strequal(which_domain, domain->name) )
1367 continue;
1369 ZERO_STRUCT(groups);
1371 /* Get list of sam groups */
1373 fstrcpy(groups.domain_name, domain->name);
1375 get_sam_group_entries(&groups);
1377 if (groups.num_sam_entries == 0) {
1378 /* this domain is empty or in an error state */
1379 continue;
1382 /* keep track the of the total number of groups seen so
1383 far over all domains */
1384 total_entries += groups.num_sam_entries;
1386 /* Allocate some memory for extra data. Note that we limit
1387 account names to sizeof(fstring) = 128 characters. */
1388 extra_data = (char *)SMB_REALLOC(
1389 extra_data, sizeof(fstring) * total_entries);
1391 if (!extra_data) {
1392 DEBUG(0,("failed to enlarge buffer!\n"));
1393 request_error(state);
1394 return;
1397 /* Pack group list into extra data fields */
1398 for (i = 0; i < groups.num_sam_entries; i++) {
1399 char *group_name = ((struct acct_info *)
1400 groups.sam_entries)[i].acct_name;
1401 fstring name;
1403 fill_domain_username(name, domain->name, group_name, True);
1404 /* Append to extra data */
1405 memcpy(&extra_data[extra_data_len], name,
1406 strlen(name));
1407 extra_data_len += strlen(name);
1408 extra_data[extra_data_len++] = ',';
1411 SAFE_FREE(groups.sam_entries);
1414 /* Assign extra_data fields in response structure */
1415 if (extra_data) {
1416 extra_data[extra_data_len - 1] = '\0';
1417 state->response.extra_data.data = extra_data;
1418 state->response.length += extra_data_len;
1421 /* No domains may have responded but that's still OK so don't
1422 return an error. */
1424 request_ok(state);
1427 /* Get user supplementary groups. This is much quicker than trying to
1428 invert the groups database. We merge the groups from the gids and
1429 other_sids info3 fields as trusted domain, universal group
1430 memberships, and nested groups (win2k native mode only) are not
1431 returned by the getgroups RPC call but are present in the info3. */
1433 struct getgroups_state {
1434 struct winbindd_cli_state *state;
1435 struct winbindd_domain *domain;
1436 char *domname;
1437 char *username;
1438 DOM_SID user_sid;
1440 const DOM_SID *token_sids;
1441 size_t i, num_token_sids;
1443 gid_t *token_gids;
1444 size_t num_token_gids;
1447 static void getgroups_usersid_recv(void *private_data, BOOL success,
1448 const DOM_SID *sid, enum lsa_SidType type);
1449 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1450 DOM_SID *token_sids, size_t num_token_sids);
1451 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
1453 void winbindd_getgroups(struct winbindd_cli_state *state)
1455 struct getgroups_state *s;
1457 /* Ensure null termination */
1458 state->request.data.username
1459 [sizeof(state->request.data.username)-1]='\0';
1461 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1462 state->request.data.username));
1464 /* Parse domain and username */
1466 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1467 if (s == NULL) {
1468 DEBUG(0, ("talloc failed\n"));
1469 request_error(state);
1470 return;
1473 s->state = state;
1475 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1477 if (!parse_domain_user_talloc(state->mem_ctx,
1478 state->request.data.username,
1479 &s->domname, &s->username)) {
1480 DEBUG(5, ("Could not parse domain user: %s\n",
1481 state->request.data.username));
1483 /* error out if we do not have nested group support */
1485 if ( !lp_winbind_nested_groups() ) {
1486 request_error(state);
1487 return;
1490 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1491 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1494 /* Get info for the domain */
1496 s->domain = find_domain_from_name_noinit(s->domname);
1498 if (s->domain == NULL) {
1499 DEBUG(7, ("could not find domain entry for domain %s\n",
1500 s->domname));
1501 request_error(state);
1502 return;
1505 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1506 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1507 "getgroups() for %s\\%s.\n", s->domname,
1508 s->username));
1509 request_error(state);
1510 return;
1513 /* Get rid and name type from name. The following costs 1 packet */
1515 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1516 getgroups_usersid_recv, WINBINDD_GETGROUPS, s);
1519 static void getgroups_usersid_recv(void *private_data, BOOL success,
1520 const DOM_SID *sid, enum lsa_SidType type)
1522 struct getgroups_state *s =
1523 (struct getgroups_state *)private_data;
1525 if ((!success) ||
1526 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1527 request_error(s->state);
1528 return;
1531 sid_copy(&s->user_sid, sid);
1533 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1534 getgroups_tokensids_recv, s);
1537 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1538 DOM_SID *token_sids, size_t num_token_sids)
1540 struct getgroups_state *s =
1541 (struct getgroups_state *)private_data;
1543 /* We need at least the user sid and the primary group in the token,
1544 * otherwise it's an error */
1546 if ((!success) || (num_token_sids < 2)) {
1547 request_error(s->state);
1548 return;
1551 s->token_sids = token_sids;
1552 s->num_token_sids = num_token_sids;
1553 s->i = 0;
1555 s->token_gids = NULL;
1556 s->num_token_gids = 0;
1558 getgroups_sid2gid_recv(s, False, 0);
1561 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
1563 struct getgroups_state *s =
1564 (struct getgroups_state *)private_data;
1566 if (success) {
1567 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1568 &s->token_gids,
1569 &s->num_token_gids)) {
1570 return;
1574 if (s->i < s->num_token_sids) {
1575 const DOM_SID *sid = &s->token_sids[s->i];
1576 s->i += 1;
1578 if (sid_equal(sid, &s->user_sid)) {
1579 getgroups_sid2gid_recv(s, False, 0);
1580 return;
1583 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1584 getgroups_sid2gid_recv, s);
1585 return;
1588 s->state->response.data.num_entries = s->num_token_gids;
1589 /* s->token_gids are talloced */
1590 s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t));
1591 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1592 request_ok(s->state);
1595 /* Get user supplementary sids. This is equivalent to the
1596 winbindd_getgroups() function but it involves a SID->SIDs mapping
1597 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1598 idmap. This call is designed to be used with applications that need
1599 to do ACL evaluation themselves. Note that the cached info3 data is
1600 not used
1602 this function assumes that the SID that comes in is a user SID. If
1603 you pass in another type of SID then you may get unpredictable
1604 results.
1607 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1608 size_t num_sids);
1610 void winbindd_getusersids(struct winbindd_cli_state *state)
1612 DOM_SID *user_sid;
1614 /* Ensure null termination */
1615 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1617 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1618 if (user_sid == NULL) {
1619 DEBUG(1, ("talloc failed\n"));
1620 request_error(state);
1621 return;
1624 if (!string_to_sid(user_sid, state->request.data.sid)) {
1625 DEBUG(1, ("Could not get convert sid %s from string\n",
1626 state->request.data.sid));
1627 request_error(state);
1628 return;
1631 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1632 state);
1635 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1636 size_t num_sids)
1638 struct winbindd_cli_state *state =
1639 (struct winbindd_cli_state *)private_data;
1640 char *ret = NULL;
1641 unsigned ofs, ret_size = 0;
1642 size_t i;
1644 if (!success) {
1645 request_error(state);
1646 return;
1649 /* work out the response size */
1650 for (i = 0; i < num_sids; i++) {
1651 const char *s = sid_string_static(&sids[i]);
1652 ret_size += strlen(s) + 1;
1655 /* build the reply */
1656 ret = (char *)SMB_MALLOC(ret_size);
1657 if (!ret) {
1658 DEBUG(0, ("malloc failed\n"));
1659 request_error(state);
1660 return;
1662 ofs = 0;
1663 for (i = 0; i < num_sids; i++) {
1664 const char *s = sid_string_static(&sids[i]);
1665 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1666 ofs += strlen(ret+ofs) + 1;
1669 /* Send data back to client */
1670 state->response.data.num_entries = num_sids;
1671 state->response.extra_data.data = ret;
1672 state->response.length += ret_size;
1673 request_ok(state);
1676 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1678 DOM_SID user_sid;
1679 struct winbindd_domain *domain;
1681 /* Ensure null termination */
1682 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1684 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1685 DEBUG(1, ("Could not get convert sid %s from string\n",
1686 state->request.data.sid));
1687 request_error(state);
1688 return;
1691 /* Get info for the domain */
1692 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1693 DEBUG(0,("could not find domain entry for sid %s\n",
1694 sid_string_static(&user_sid)));
1695 request_error(state);
1696 return;
1699 sendto_domain(state, domain);
1702 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1703 struct winbindd_cli_state *state)
1705 DOM_SID user_sid;
1706 NTSTATUS status;
1708 char *sidstring;
1709 ssize_t len;
1710 DOM_SID *groups;
1711 uint32 num_groups;
1713 /* Ensure null termination */
1714 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1716 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1717 DEBUG(1, ("Could not get convert sid %s from string\n",
1718 state->request.data.sid));
1719 return WINBINDD_ERROR;
1722 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1723 &user_sid, &num_groups,
1724 &groups);
1725 if (!NT_STATUS_IS_OK(status))
1726 return WINBINDD_ERROR;
1728 if (num_groups == 0) {
1729 state->response.data.num_entries = 0;
1730 state->response.extra_data.data = NULL;
1731 return WINBINDD_OK;
1734 if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) {
1735 DEBUG(0, ("talloc failed\n"));
1736 return WINBINDD_ERROR;
1739 state->response.extra_data.data = SMB_STRDUP(sidstring);
1740 if (!state->response.extra_data.data) {
1741 return WINBINDD_ERROR;
1743 state->response.length += len+1;
1744 state->response.data.num_entries = num_groups;
1746 return WINBINDD_OK;