SMB traffic analyzer vfs module from Holger Hetterich <hhetter@novell.com>
[Samba.git] / source / winbindd / winbindd_group.c
blobf81caa22b6c3314096f266eef631c81b687e9250
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 extern bool opt_nocache;
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 static void add_member(const char *domain, const char *user,
34 char **pp_members, size_t *p_num_members)
36 fstring name;
38 fill_domain_username(name, domain, user, True);
39 safe_strcat(name, ",", sizeof(name)-1);
40 string_append(pp_members, name);
41 *p_num_members += 1;
44 /**********************************************************************
45 Add member users resulting from sid. Expand if it is a domain group.
46 **********************************************************************/
48 static void add_expanded_sid(const DOM_SID *sid, char **pp_members, size_t *p_num_members)
50 DOM_SID dom_sid;
51 uint32 rid;
52 struct winbindd_domain *domain;
53 size_t i;
55 char *domain_name = NULL;
56 char *name = NULL;
57 enum lsa_SidType type;
59 uint32 num_names;
60 DOM_SID *sid_mem;
61 char **names;
62 uint32 *types;
64 NTSTATUS result;
66 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
68 if (mem_ctx == NULL) {
69 DEBUG(1, ("talloc_init failed\n"));
70 return;
73 sid_copy(&dom_sid, sid);
74 sid_split_rid(&dom_sid, &rid);
76 domain = find_lookup_domain_from_sid(sid);
78 if (domain == NULL) {
79 DEBUG(3, ("Could not find domain for sid %s\n",
80 sid_string_dbg(sid)));
81 goto done;
84 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
85 &domain_name, &name, &type);
87 if (!NT_STATUS_IS_OK(result)) {
88 DEBUG(3, ("sid_to_name failed for sid %s\n",
89 sid_string_dbg(sid)));
90 goto done;
93 DEBUG(10, ("Found name %s, type %d\n", name, type));
95 if (type == SID_NAME_USER) {
96 add_member(domain_name, name, pp_members, p_num_members);
97 goto done;
100 if (type != SID_NAME_DOM_GRP) {
101 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
102 name));
103 goto done;
106 /* Expand the domain group, this must be done via the target domain */
108 domain = find_domain_from_sid(sid);
110 if (domain == NULL) {
111 DEBUG(3, ("Could not find domain from SID %s\n",
112 sid_string_dbg(sid)));
113 goto done;
116 result = domain->methods->lookup_groupmem(domain, mem_ctx,
117 sid, &num_names,
118 &sid_mem, &names,
119 &types);
121 if (!NT_STATUS_IS_OK(result)) {
122 DEBUG(10, ("Could not lookup group members for %s: %s\n",
123 name, nt_errstr(result)));
124 goto done;
127 for (i=0; i<num_names; i++) {
128 DEBUG(10, ("Adding group member SID %s\n",
129 sid_string_dbg(&sid_mem[i])));
131 if (types[i] != SID_NAME_USER) {
132 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
133 "Ignoring.\n", names[i], name));
134 continue;
137 add_member(domain->name, names[i], pp_members, p_num_members);
140 done:
141 talloc_destroy(mem_ctx);
142 return;
145 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
146 DOM_SID *group_sid,
147 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
149 DOM_SID *members;
150 size_t i, num_members;
152 *num_gr_mem = 0;
153 *gr_mem = NULL;
154 *gr_mem_len = 0;
156 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
157 &num_members)))
158 return True;
160 for (i=0; i<num_members; i++) {
161 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
164 TALLOC_FREE(members);
166 if (*gr_mem != NULL) {
167 size_t len;
169 /* We have at least one member, strip off the last "," */
170 len = strlen(*gr_mem);
171 (*gr_mem)[len-1] = '\0';
172 *gr_mem_len = len;
175 return True;
178 /* Fill a grent structure from various other information */
180 static bool fill_grent(struct winbindd_gr *gr, const char *dom_name,
181 const char *gr_name, gid_t unix_gid)
183 fstring full_group_name;
185 fill_domain_username( full_group_name, dom_name, gr_name, True );
187 gr->gr_gid = unix_gid;
189 /* Group name and password */
191 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
192 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
194 return True;
197 /***********************************************************************
198 If "enum users" is set to false, and the group being looked
199 up is the Domain Users SID: S-1-5-domain-513, then for the
200 list of members check if the querying user is in that group,
201 and if so only return that user as the gr_mem array.
202 We can change this to a different parameter than "enum users"
203 if neccessaey, or parameterize the group list we do this for.
204 ***********************************************************************/
206 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
207 struct winbindd_domain *domain,
208 struct winbindd_cli_state *state,
209 DOM_SID *group_sid,
210 enum lsa_SidType group_name_type,
211 size_t *num_gr_mem, char **gr_mem,
212 size_t *gr_mem_len)
214 DOM_SID querying_user_sid;
215 DOM_SID *pquerying_user_sid = NULL;
216 uint32 num_groups = 0;
217 DOM_SID *user_sids = NULL;
218 bool u_in_group = False;
219 NTSTATUS status;
220 int i;
221 unsigned int buf_len = 0;
222 char *buf = NULL;
224 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
225 domain->name ));
227 if (state) {
228 uid_t ret_uid = (uid_t)-1;
229 if (sys_getpeereid(state->sock, &ret_uid)==0) {
230 /* We know who's asking - look up their SID if
231 it's one we've mapped before. */
232 status = idmap_uid_to_sid(&querying_user_sid, ret_uid);
233 if (NT_STATUS_IS_OK(status)) {
234 pquerying_user_sid = &querying_user_sid;
235 DEBUG(10,("fill_grent_mem_domain_users: querying uid %u -> %s\n",
236 (unsigned int)ret_uid,
237 sid_string_dbg(pquerying_user_sid)));
242 /* Only look up if it was a winbindd user in this domain. */
243 if (pquerying_user_sid &&
244 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
246 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
247 sid_string_dbg(pquerying_user_sid) ));
249 status = domain->methods->lookup_usergroups(domain,
250 mem_ctx,
251 pquerying_user_sid,
252 &num_groups,
253 &user_sids);
254 if (!NT_STATUS_IS_OK(status)) {
255 DEBUG(1, ("fill_grent_mem_domain_users: lookup_usergroups failed "
256 "for sid %s in domain %s (error: %s)\n",
257 sid_string_dbg(pquerying_user_sid),
258 domain->name,
259 nt_errstr(status)));
260 return False;
263 for (i = 0; i < num_groups; i++) {
264 if (sid_equal(group_sid, &user_sids[i])) {
265 /* User is in Domain Users, add their name
266 as the only group member. */
267 u_in_group = True;
268 break;
273 if (u_in_group) {
274 size_t len = 0;
275 char *domainname = NULL;
276 char *username = NULL;
277 fstring name;
278 enum lsa_SidType type;
280 DEBUG(10,("fill_grent_mem_domain_users: sid %s in 'Domain Users' in domain %s\n",
281 sid_string_dbg(pquerying_user_sid),
282 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_dbg(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 status = add_sid_to_array_unique(ctx,
442 &sid_mem[j],
443 &new_groups,
444 &new_groups_size);
445 if (!NT_STATUS_IS_OK(status)) {
446 goto out;
449 continue;
453 TALLOC_FREE( tmp_ctx );
456 *new_glist = new_groups;
457 *n_new_glist = (uint32)new_groups_size;
459 out:
460 TALLOC_FREE( tmp_ctx );
462 return status;
465 /***********************************************************************
466 Fill in the group membership field of a NT group given by group_sid
467 ***********************************************************************/
469 static bool fill_grent_mem(struct winbindd_domain *domain,
470 struct winbindd_cli_state *state,
471 DOM_SID *group_sid,
472 enum lsa_SidType group_name_type,
473 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
475 uint32 num_names = 0;
476 unsigned int buf_len = 0, buf_ndx = 0, i;
477 char **names = NULL, *buf = NULL;
478 bool result = False;
479 TALLOC_CTX *mem_ctx;
480 uint32 group_rid;
481 DOM_SID *glist = NULL;
482 DOM_SID *new_glist = NULL;
483 uint32 n_glist, n_new_glist;
484 int max_depth = lp_winbind_expand_groups();
486 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
487 return False;
489 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
491 /* Initialize with no members */
493 *num_gr_mem = 0;
495 /* HACK ALERT!! This whole routine does not cope with group members
496 * from more than one domain, ie aliases. Thus we have to work it out
497 * ourselves in a special routine. */
499 if (domain->internal) {
500 result = fill_passdb_alias_grmem(domain, group_sid,
501 num_gr_mem,
502 gr_mem, gr_mem_len);
503 goto done;
506 /* Verify name type */
508 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
509 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
511 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
512 sid_string_dbg(group_sid),
513 domain->name, group_name_type));
514 goto done;
517 /* OPTIMIZATION / HACK. See comment in
518 fill_grent_mem_domusers() */
520 sid_peek_rid( group_sid, &group_rid );
521 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
522 result = fill_grent_mem_domusers( mem_ctx, domain, state,
523 group_sid, group_name_type,
524 num_gr_mem, gr_mem,
525 gr_mem_len );
526 goto done;
529 /* Real work goes here. Create a list of group names to
530 expand startign with the initial one. Pass that to
531 expand_groups() which returns a list of more group names
532 to expand. Do this up to the max search depth. */
534 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
535 result = False;
536 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
537 goto done;
539 sid_copy( &glist[0], group_sid );
540 n_glist = 1;
542 for ( i=0; i<max_depth && glist; i++ ) {
543 uint32 n_members = 0;
544 char **members = NULL;
545 NTSTATUS nt_status;
547 nt_status = expand_groups( mem_ctx, domain,
548 glist, n_glist,
549 &new_glist, &n_new_glist,
550 &members, &n_members);
551 if ( !NT_STATUS_IS_OK(nt_status) ) {
552 result = False;
553 goto done;
556 /* Add new group members to list */
558 nt_status = add_names_to_list( mem_ctx, &names, &num_names,
559 members, n_members );
560 if ( !NT_STATUS_IS_OK(nt_status) ) {
561 result = False;
562 goto done;
565 TALLOC_FREE( members );
567 /* If we have no more groups to expand, break out
568 early */
570 if (new_glist == NULL)
571 break;
573 /* One more round */
574 TALLOC_FREE(glist);
575 glist = new_glist;
576 n_glist = n_new_glist;
578 TALLOC_FREE( glist );
580 DEBUG(10, ("looked up %d names\n", num_names));
582 again:
583 /* Add members to list */
585 for (i = 0; i < num_names; i++) {
586 int len;
588 DEBUG(10, ("processing name %s\n", names[i]));
590 len = strlen(names[i]);
592 /* Add to list or calculate buffer length */
594 if (!buf) {
595 buf_len += len + 1; /* List is comma separated */
596 (*num_gr_mem)++;
597 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
598 } else {
599 DEBUG(10, ("appending %s at ndx %d\n",
600 names[i], buf_ndx));
601 parse_add_domuser(&buf[buf_ndx], names[i], &len);
602 buf_ndx += len;
603 buf[buf_ndx] = ',';
604 buf_ndx++;
608 /* Allocate buffer */
610 if (!buf && buf_len != 0) {
611 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
612 DEBUG(1, ("out of memory\n"));
613 result = False;
614 goto done;
616 memset(buf, 0, buf_len);
617 goto again;
620 /* Now we're done */
622 if (buf && buf_ndx > 0) {
623 buf[buf_ndx - 1] = '\0';
626 *gr_mem = buf;
627 *gr_mem_len = buf_len;
629 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
630 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
631 result = True;
633 done:
635 talloc_destroy(mem_ctx);
637 DEBUG(10, ("fill_grent_mem returning %d\n", result));
639 return result;
642 static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid );
644 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
645 enum lsa_SidType type )
647 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
649 if (!success) {
650 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
651 request_error(state);
652 return;
655 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
656 DEBUG(5,("getgrnam_recv: not a group!\n"));
657 request_error(state);
658 return;
661 winbindd_getgrsid( state, *sid );
665 /* Return a group structure from a group name */
667 void winbindd_getgrnam(struct winbindd_cli_state *state)
669 struct winbindd_domain *domain;
670 fstring name_domain, name_group;
671 char *tmp;
673 /* Ensure null termination */
674 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
676 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
677 state->request.data.groupname));
679 /* Parse domain and groupname */
681 memset(name_group, 0, sizeof(fstring));
683 tmp = state->request.data.groupname;
685 name_domain[0] = '\0';
686 name_group[0] = '\0';
688 parse_domain_user(tmp, name_domain, name_group);
690 /* if no domain or our local domain and no local tdb group, default to
691 * our local domain for aliases */
693 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
694 fstrcpy(name_domain, get_global_sam_name());
697 /* Get info for the domain */
699 if ((domain = find_domain_from_name(name_domain)) == NULL) {
700 DEBUG(3, ("could not get domain sid for domain %s\n",
701 name_domain));
702 request_error(state);
703 return;
705 /* should we deal with users for our domain? */
707 if ( lp_winbind_trusted_domains_only() && domain->primary) {
708 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
709 "getgrnam() for %s\\%s.\n", name_domain, name_group));
710 request_error(state);
711 return;
714 /* Get rid and name type from name */
716 ws_name_replace( name_group, WB_REPLACE_CHAR );
718 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
719 getgrnam_recv, WINBINDD_GETGRNAM, state );
722 struct getgrsid_state {
723 struct winbindd_cli_state *state;
724 struct winbindd_domain *domain;
725 char *group_name;
726 enum lsa_SidType group_type;
727 uid_t gid;
728 DOM_SID group_sid;
731 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
733 struct getgrsid_state *s =
734 (struct getgrsid_state *)private_data;
735 struct winbindd_domain *domain;
736 size_t gr_mem_len;
737 size_t num_gr_mem;
738 char *gr_mem;
739 fstring dom_name, group_name;
741 if (!success) {
742 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
743 request_error(s->state);
744 return;
747 s->gid = gid;
749 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
750 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
751 request_error(s->state);
752 return;
756 /* Fill in group structure */
758 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
759 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
760 request_error(s->state);
761 return;
764 if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
765 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
766 &num_gr_mem, &gr_mem, &gr_mem_len))
768 request_error(s->state);
769 return;
772 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
774 /* Group membership lives at start of extra data */
776 s->state->response.data.gr.gr_mem_ofs = 0;
778 s->state->response.length += gr_mem_len;
779 s->state->response.extra_data.data = gr_mem;
781 request_ok(s->state);
784 static void getgrsid_lookupsid_recv( void *private_data, bool success,
785 const char *dom_name, const char *name,
786 enum lsa_SidType name_type )
788 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
790 if (!success) {
791 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
792 request_error(s->state);
793 return;
796 /* either it's a domain group, a domain local group, or a
797 local group in an internal domain */
799 if ( !( (name_type==SID_NAME_DOM_GRP) ||
800 ((name_type==SID_NAME_ALIAS) &&
801 (s->domain->primary || s->domain->internal)) ) )
803 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
804 dom_name, name, name_type));
805 request_error(s->state);
806 return;
809 if ( (s->group_name = talloc_asprintf( s->state->mem_ctx,
810 "%s%c%s",
811 dom_name,
812 *lp_winbind_separator(),
813 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_dbg(&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 Return True if some groups were returned, False otherwise. */
977 bool get_sam_group_entries(struct getent_state *ent)
979 NTSTATUS status;
980 uint32 num_entries;
981 struct acct_info *name_list = NULL;
982 TALLOC_CTX *mem_ctx;
983 bool result = False;
984 struct acct_info *sam_grp_entries = NULL;
985 struct winbindd_domain *domain;
987 if (ent->got_sam_entries)
988 return False;
990 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
991 ent->domain_name))) {
992 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
993 return False;
996 /* Free any existing group info */
998 SAFE_FREE(ent->sam_entries);
999 ent->num_sam_entries = 0;
1000 ent->got_sam_entries = True;
1002 /* Enumerate domain groups */
1004 num_entries = 0;
1006 if (!(domain = find_domain_from_name(ent->domain_name))) {
1007 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
1008 goto done;
1011 /* always get the domain global groups */
1013 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1015 if (!NT_STATUS_IS_OK(status)) {
1016 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
1017 result = False;
1018 goto done;
1021 /* Copy entries into return buffer */
1023 if (num_entries) {
1024 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
1025 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
1026 num_entries));
1027 result = False;
1028 goto done;
1030 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
1033 ent->num_sam_entries = num_entries;
1035 /* get the domain local groups if we are a member of a native win2k domain
1036 and are not using LDAP to get the groups */
1038 if ( ( lp_security() != SEC_ADS && domain->native_mode
1039 && domain->primary) || domain->internal )
1041 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
1042 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
1044 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1046 if ( !NT_STATUS_IS_OK(status) ) {
1047 DEBUG(3,("get_sam_group_entries: "
1048 "Failed to enumerate "
1049 "domain local groups with error %s!\n",
1050 nt_errstr(status)));
1051 num_entries = 0;
1053 else
1054 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
1056 /* Copy entries into return buffer */
1058 if ( num_entries ) {
1059 if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
1061 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
1062 num_entries));
1063 result = False;
1064 goto done;
1067 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
1068 num_entries * sizeof(struct acct_info) );
1071 ent->num_sam_entries += num_entries;
1075 /* Fill in remaining fields */
1077 ent->sam_entries = name_list;
1078 ent->sam_entry_index = 0;
1080 result = (ent->num_sam_entries > 0);
1082 done:
1083 talloc_destroy(mem_ctx);
1085 return result;
1088 /* Fetch next group entry from ntdom database */
1090 #define MAX_GETGRENT_GROUPS 500
1092 void winbindd_getgrent(struct winbindd_cli_state *state)
1094 struct getent_state *ent;
1095 struct winbindd_gr *group_list = NULL;
1096 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1097 char *gr_mem_list = NULL;
1099 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1101 /* Check user has enabled this */
1103 if (!lp_winbind_enum_groups()) {
1104 request_error(state);
1105 return;
1108 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1110 if (num_groups == 0) {
1111 request_error(state);
1112 return;
1115 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
1116 request_error(state);
1117 return;
1120 memset(state->response.extra_data.data, '\0',
1121 num_groups * sizeof(struct winbindd_gr) );
1123 state->response.data.num_entries = 0;
1125 group_list = (struct winbindd_gr *)state->response.extra_data.data;
1127 if (!state->getgrent_initialized)
1128 winbindd_setgrent_internal(state);
1130 if (!(ent = state->getgrent_state)) {
1131 request_error(state);
1132 return;
1135 /* Start sending back groups */
1137 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1138 struct acct_info *name_list = NULL;
1139 fstring domain_group_name;
1140 uint32 result;
1141 gid_t group_gid;
1142 size_t gr_mem_len;
1143 char *gr_mem;
1144 DOM_SID group_sid;
1145 struct winbindd_domain *domain;
1147 /* Do we need to fetch another chunk of groups? */
1149 tryagain:
1151 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1152 ent->sam_entry_index, ent->num_sam_entries));
1154 if (ent->num_sam_entries == ent->sam_entry_index) {
1156 while(ent && !get_sam_group_entries(ent)) {
1157 struct getent_state *next_ent;
1159 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
1161 /* Free state information for this domain */
1163 SAFE_FREE(ent->sam_entries);
1165 next_ent = ent->next;
1166 DLIST_REMOVE(state->getgrent_state, ent);
1168 SAFE_FREE(ent);
1169 ent = next_ent;
1172 /* No more domains */
1174 if (!ent)
1175 break;
1178 name_list = (struct acct_info *)ent->sam_entries;
1180 if (!(domain =
1181 find_domain_from_name(ent->domain_name))) {
1182 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
1183 result = False;
1184 goto done;
1187 /* Lookup group info */
1189 sid_copy(&group_sid, &domain->sid);
1190 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1192 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) {
1193 union unid_t id;
1194 enum lsa_SidType type;
1196 DEBUG(10, ("SID %s not in idmap\n",
1197 sid_string_dbg(&group_sid)));
1199 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1200 DEBUG(1, ("could not look up gid for group "
1201 "%s\n",
1202 name_list[ent->sam_entry_index].acct_name));
1203 ent->sam_entry_index++;
1204 goto tryagain;
1207 if ((type != SID_NAME_DOM_GRP) &&
1208 (type != SID_NAME_ALIAS) &&
1209 (type != SID_NAME_WKN_GRP)) {
1210 DEBUG(1, ("Group %s is a %s, not a group\n",
1211 sid_type_lookup(type),
1212 name_list[ent->sam_entry_index].acct_name));
1213 ent->sam_entry_index++;
1214 goto tryagain;
1216 group_gid = id.gid;
1219 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
1220 (unsigned long)name_list[ent->sam_entry_index].rid));
1222 /* Fill in group entry */
1224 fill_domain_username(domain_group_name, ent->domain_name,
1225 name_list[ent->sam_entry_index].acct_name, True);
1227 result = fill_grent(&group_list[group_list_ndx],
1228 ent->domain_name,
1229 name_list[ent->sam_entry_index].acct_name,
1230 group_gid);
1232 /* Fill in group membership entry */
1234 if (result) {
1235 size_t num_gr_mem = 0;
1236 DOM_SID member_sid;
1237 group_list[group_list_ndx].num_gr_mem = 0;
1238 gr_mem = NULL;
1239 gr_mem_len = 0;
1241 /* Get group membership */
1242 if (state->request.cmd == WINBINDD_GETGRLST) {
1243 result = True;
1244 } else {
1245 sid_copy(&member_sid, &domain->sid);
1246 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1247 result = fill_grent_mem(
1248 domain,
1249 NULL,
1250 &member_sid,
1251 SID_NAME_DOM_GRP,
1252 &num_gr_mem,
1253 &gr_mem, &gr_mem_len);
1255 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1259 if (result) {
1260 /* Append to group membership list */
1261 gr_mem_list = (char *)SMB_REALLOC(
1262 gr_mem_list, gr_mem_list_len + gr_mem_len);
1264 if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
1265 DEBUG(0, ("out of memory\n"));
1266 gr_mem_list_len = 0;
1267 break;
1270 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1271 gr_mem_list_len, (unsigned int)gr_mem_len));
1273 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1274 gr_mem_len);
1276 SAFE_FREE(gr_mem);
1278 group_list[group_list_ndx].gr_mem_ofs =
1279 gr_mem_list_len;
1281 gr_mem_list_len += gr_mem_len;
1284 ent->sam_entry_index++;
1286 /* Add group to return list */
1288 if (result) {
1290 DEBUG(10, ("adding group num_entries = %d\n",
1291 state->response.data.num_entries));
1293 group_list_ndx++;
1294 state->response.data.num_entries++;
1296 state->response.length +=
1297 sizeof(struct winbindd_gr);
1299 } else {
1300 DEBUG(0, ("could not lookup domain group %s\n",
1301 domain_group_name));
1305 /* Copy the list of group memberships to the end of the extra data */
1307 if (group_list_ndx == 0)
1308 goto done;
1310 state->response.extra_data.data = SMB_REALLOC(
1311 state->response.extra_data.data,
1312 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1314 if (!state->response.extra_data.data) {
1315 DEBUG(0, ("out of memory\n"));
1316 group_list_ndx = 0;
1317 SAFE_FREE(gr_mem_list);
1318 request_error(state);
1319 return;
1322 memcpy(&((char *)state->response.extra_data.data)
1323 [group_list_ndx * sizeof(struct winbindd_gr)],
1324 gr_mem_list, gr_mem_list_len);
1326 state->response.length += gr_mem_list_len;
1328 DEBUG(10, ("returning %d groups, length = %d\n",
1329 group_list_ndx, gr_mem_list_len));
1331 /* Out of domains */
1333 done:
1335 SAFE_FREE(gr_mem_list);
1337 if (group_list_ndx > 0)
1338 request_ok(state);
1339 else
1340 request_error(state);
1343 /* List domain groups without mapping to unix ids */
1344 void winbindd_list_groups(struct winbindd_cli_state *state)
1346 winbindd_list_ent(state, LIST_GROUPS);
1349 /* Get user supplementary groups. This is much quicker than trying to
1350 invert the groups database. We merge the groups from the gids and
1351 other_sids info3 fields as trusted domain, universal group
1352 memberships, and nested groups (win2k native mode only) are not
1353 returned by the getgroups RPC call but are present in the info3. */
1355 struct getgroups_state {
1356 struct winbindd_cli_state *state;
1357 struct winbindd_domain *domain;
1358 char *domname;
1359 char *username;
1360 DOM_SID user_sid;
1362 const DOM_SID *token_sids;
1363 size_t i, num_token_sids;
1365 gid_t *token_gids;
1366 size_t num_token_gids;
1369 static void getgroups_usersid_recv(void *private_data, bool success,
1370 const DOM_SID *sid, enum lsa_SidType type);
1371 static void getgroups_tokensids_recv(void *private_data, bool success,
1372 DOM_SID *token_sids, size_t num_token_sids);
1373 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1375 void winbindd_getgroups(struct winbindd_cli_state *state)
1377 struct getgroups_state *s;
1379 /* Ensure null termination */
1380 state->request.data.username
1381 [sizeof(state->request.data.username)-1]='\0';
1383 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1384 state->request.data.username));
1386 /* Parse domain and username */
1388 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1389 if (s == NULL) {
1390 DEBUG(0, ("talloc failed\n"));
1391 request_error(state);
1392 return;
1395 s->state = state;
1397 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1399 if (!parse_domain_user_talloc(state->mem_ctx,
1400 state->request.data.username,
1401 &s->domname, &s->username)) {
1402 DEBUG(5, ("Could not parse domain user: %s\n",
1403 state->request.data.username));
1405 /* error out if we do not have nested group support */
1407 if ( !lp_winbind_nested_groups() ) {
1408 request_error(state);
1409 return;
1412 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1413 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1416 /* Get info for the domain (either by short domain name or
1417 DNS name in the case of a UPN) */
1419 s->domain = find_domain_from_name_noinit(s->domname);
1420 if (!s->domain) {
1421 char *p = strchr(s->username, '@');
1423 if (p) {
1424 s->domain = find_domain_from_name_noinit(p+1);
1429 if (s->domain == NULL) {
1430 DEBUG(7, ("could not find domain entry for domain %s\n",
1431 s->domname));
1432 request_error(state);
1433 return;
1436 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1437 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1438 "getgroups() for %s\\%s.\n", s->domname,
1439 s->username));
1440 request_error(state);
1441 return;
1444 /* Get rid and name type from name. The following costs 1 packet */
1446 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1447 getgroups_usersid_recv, WINBINDD_GETGROUPS, s);
1450 static void getgroups_usersid_recv(void *private_data, bool success,
1451 const DOM_SID *sid, enum lsa_SidType type)
1453 struct getgroups_state *s =
1454 (struct getgroups_state *)private_data;
1456 if ((!success) ||
1457 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1458 request_error(s->state);
1459 return;
1462 sid_copy(&s->user_sid, sid);
1464 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1465 getgroups_tokensids_recv, s);
1468 static void getgroups_tokensids_recv(void *private_data, bool success,
1469 DOM_SID *token_sids, size_t num_token_sids)
1471 struct getgroups_state *s =
1472 (struct getgroups_state *)private_data;
1474 /* We need at least the user sid and the primary group in the token,
1475 * otherwise it's an error */
1477 if ((!success) || (num_token_sids < 2)) {
1478 request_error(s->state);
1479 return;
1482 s->token_sids = token_sids;
1483 s->num_token_sids = num_token_sids;
1484 s->i = 0;
1486 s->token_gids = NULL;
1487 s->num_token_gids = 0;
1489 getgroups_sid2gid_recv(s, False, 0);
1492 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1494 struct getgroups_state *s =
1495 (struct getgroups_state *)private_data;
1497 if (success) {
1498 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1499 &s->token_gids,
1500 &s->num_token_gids)) {
1501 return;
1505 if (s->i < s->num_token_sids) {
1506 const DOM_SID *sid = &s->token_sids[s->i];
1507 s->i += 1;
1509 if (sid_equal(sid, &s->user_sid)) {
1510 getgroups_sid2gid_recv(s, False, 0);
1511 return;
1514 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1515 getgroups_sid2gid_recv, s);
1516 return;
1519 s->state->response.data.num_entries = s->num_token_gids;
1520 if (s->num_token_gids) {
1521 /* s->token_gids are talloced */
1522 s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t));
1523 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1525 request_ok(s->state);
1528 /* Get user supplementary sids. This is equivalent to the
1529 winbindd_getgroups() function but it involves a SID->SIDs mapping
1530 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1531 idmap. This call is designed to be used with applications that need
1532 to do ACL evaluation themselves. Note that the cached info3 data is
1533 not used
1535 this function assumes that the SID that comes in is a user SID. If
1536 you pass in another type of SID then you may get unpredictable
1537 results.
1540 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1541 size_t num_sids);
1543 void winbindd_getusersids(struct winbindd_cli_state *state)
1545 DOM_SID *user_sid;
1547 /* Ensure null termination */
1548 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1550 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1551 if (user_sid == NULL) {
1552 DEBUG(1, ("talloc failed\n"));
1553 request_error(state);
1554 return;
1557 if (!string_to_sid(user_sid, state->request.data.sid)) {
1558 DEBUG(1, ("Could not get convert sid %s from string\n",
1559 state->request.data.sid));
1560 request_error(state);
1561 return;
1564 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1565 state);
1568 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1569 size_t num_sids)
1571 struct winbindd_cli_state *state =
1572 (struct winbindd_cli_state *)private_data;
1573 char *ret = NULL;
1574 unsigned ofs, ret_size = 0;
1575 size_t i;
1577 if (!success) {
1578 request_error(state);
1579 return;
1582 /* work out the response size */
1583 for (i = 0; i < num_sids; i++) {
1584 fstring s;
1585 sid_to_fstring(s, &sids[i]);
1586 ret_size += strlen(s) + 1;
1589 /* build the reply */
1590 ret = (char *)SMB_MALLOC(ret_size);
1591 if (!ret) {
1592 DEBUG(0, ("malloc failed\n"));
1593 request_error(state);
1594 return;
1596 ofs = 0;
1597 for (i = 0; i < num_sids; i++) {
1598 fstring s;
1599 sid_to_fstring(s, &sids[i]);
1600 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1601 ofs += strlen(ret+ofs) + 1;
1604 /* Send data back to client */
1605 state->response.data.num_entries = num_sids;
1606 state->response.extra_data.data = ret;
1607 state->response.length += ret_size;
1608 request_ok(state);
1611 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1613 DOM_SID user_sid;
1614 struct winbindd_domain *domain;
1616 /* Ensure null termination */
1617 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1619 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1620 DEBUG(1, ("Could not get convert sid %s from string\n",
1621 state->request.data.sid));
1622 request_error(state);
1623 return;
1626 /* Get info for the domain */
1627 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1628 DEBUG(0,("could not find domain entry for sid %s\n",
1629 sid_string_dbg(&user_sid)));
1630 request_error(state);
1631 return;
1634 sendto_domain(state, domain);
1637 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1638 struct winbindd_cli_state *state)
1640 DOM_SID user_sid;
1641 NTSTATUS status;
1643 char *sidstring;
1644 ssize_t len;
1645 DOM_SID *groups;
1646 uint32 num_groups;
1648 /* Ensure null termination */
1649 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1651 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1652 DEBUG(1, ("Could not get convert sid %s from string\n",
1653 state->request.data.sid));
1654 return WINBINDD_ERROR;
1657 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1658 &user_sid, &num_groups,
1659 &groups);
1660 if (!NT_STATUS_IS_OK(status))
1661 return WINBINDD_ERROR;
1663 if (num_groups == 0) {
1664 state->response.data.num_entries = 0;
1665 state->response.extra_data.data = NULL;
1666 return WINBINDD_OK;
1669 if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) {
1670 DEBUG(0, ("talloc failed\n"));
1671 return WINBINDD_ERROR;
1674 state->response.extra_data.data = SMB_STRDUP(sidstring);
1675 if (!state->response.extra_data.data) {
1676 return WINBINDD_ERROR;
1678 state->response.length += len+1;
1679 state->response.data.num_entries = num_groups;
1681 return WINBINDD_OK;