build: use $(LIBTALLOC_LIBS) instead of @LIBTALLOC_LIBS@ in rules.
[Samba.git] / source / winbindd / winbindd_group.c
blob20b90e3283dec05dabba834f997746a9625351c0
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", names[i], buf_ndx));
600 safe_strcpy(&buf[buf_ndx], names[i], len);
601 buf_ndx += len;
602 buf[buf_ndx] = ',';
603 buf_ndx++;
607 /* Allocate buffer */
609 if (!buf && buf_len != 0) {
610 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
611 DEBUG(1, ("out of memory\n"));
612 result = False;
613 goto done;
615 memset(buf, 0, buf_len);
616 goto again;
619 /* Now we're done */
621 if (buf && buf_ndx > 0) {
622 buf[buf_ndx - 1] = '\0';
625 *gr_mem = buf;
626 *gr_mem_len = buf_len;
628 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
629 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
630 result = True;
632 done:
634 talloc_destroy(mem_ctx);
636 DEBUG(10, ("fill_grent_mem returning %d\n", result));
638 return result;
641 static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid );
643 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
644 enum lsa_SidType type )
646 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
648 if (!success) {
649 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
650 request_error(state);
651 return;
654 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
655 DEBUG(5,("getgrnam_recv: not a group!\n"));
656 request_error(state);
657 return;
660 winbindd_getgrsid( state, *sid );
664 /* Return a group structure from a group name */
666 void winbindd_getgrnam(struct winbindd_cli_state *state)
668 struct winbindd_domain *domain;
669 fstring name_domain, name_group;
670 char *tmp;
672 /* Ensure null termination */
673 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
675 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
676 state->request.data.groupname));
678 /* Parse domain and groupname */
680 memset(name_group, 0, sizeof(fstring));
682 tmp = state->request.data.groupname;
684 name_domain[0] = '\0';
685 name_group[0] = '\0';
687 parse_domain_user(tmp, name_domain, name_group);
689 /* if no domain or our local domain and no local tdb group, default to
690 * our local domain for aliases */
692 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
693 fstrcpy(name_domain, get_global_sam_name());
696 /* Get info for the domain */
698 if ((domain = find_domain_from_name(name_domain)) == NULL) {
699 DEBUG(3, ("could not get domain sid for domain %s\n",
700 name_domain));
701 request_error(state);
702 return;
704 /* should we deal with users for our domain? */
706 if ( lp_winbind_trusted_domains_only() && domain->primary) {
707 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
708 "getgrnam() for %s\\%s.\n", name_domain, name_group));
709 request_error(state);
710 return;
713 /* Get rid and name type from name */
715 ws_name_replace( name_group, WB_REPLACE_CHAR );
717 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
718 getgrnam_recv, WINBINDD_GETGRNAM, state );
721 struct getgrsid_state {
722 struct winbindd_cli_state *state;
723 struct winbindd_domain *domain;
724 char *group_name;
725 enum lsa_SidType group_type;
726 uid_t gid;
727 DOM_SID group_sid;
730 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
732 struct getgrsid_state *s =
733 (struct getgrsid_state *)private_data;
734 struct winbindd_domain *domain;
735 size_t gr_mem_len;
736 size_t num_gr_mem;
737 char *gr_mem;
738 fstring dom_name, group_name;
740 if (!success) {
741 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
742 request_error(s->state);
743 return;
746 s->gid = gid;
748 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
749 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
750 request_error(s->state);
751 return;
755 /* Fill in group structure */
757 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
758 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
759 request_error(s->state);
760 return;
763 if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
764 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
765 &num_gr_mem, &gr_mem, &gr_mem_len))
767 request_error(s->state);
768 return;
771 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
773 /* Group membership lives at start of extra data */
775 s->state->response.data.gr.gr_mem_ofs = 0;
777 s->state->response.length += gr_mem_len;
778 s->state->response.extra_data.data = gr_mem;
780 request_ok(s->state);
783 static void getgrsid_lookupsid_recv( void *private_data, bool success,
784 const char *dom_name, const char *name,
785 enum lsa_SidType name_type )
787 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
789 if (!success) {
790 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
791 request_error(s->state);
792 return;
795 /* either it's a domain group, a domain local group, or a
796 local group in an internal domain */
798 if ( !( (name_type==SID_NAME_DOM_GRP) ||
799 ((name_type==SID_NAME_ALIAS) &&
800 (s->domain->primary || s->domain->internal)) ) )
802 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
803 dom_name, name, name_type));
804 request_error(s->state);
805 return;
808 if ( (s->group_name = talloc_asprintf( s->state->mem_ctx,
809 "%s%c%s",
810 dom_name,
811 *lp_winbind_separator(),
812 name)) == NULL )
814 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
815 request_error(s->state);
816 return;
819 s->group_type = name_type;
821 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
822 getgrsid_sid2gid_recv, s);
825 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
827 struct getgrsid_state *s;
829 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
830 DEBUG(0, ("talloc failed\n"));
831 request_error(state);
832 return;
835 s->state = state;
837 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
838 DEBUG(3, ("Could not find domain for sid %s\n",
839 sid_string_dbg(&group_sid)));
840 request_error(state);
841 return;
844 sid_copy(&s->group_sid, &group_sid);
846 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
847 getgrsid_lookupsid_recv, s );
851 static void getgrgid_recv(void *private_data, bool success, const char *sid)
853 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
854 enum lsa_SidType name_type;
855 DOM_SID group_sid;
857 if (success) {
858 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
859 (unsigned long)(state->request.data.gid), sid));
861 string_to_sid(&group_sid, sid);
862 winbindd_getgrsid(state, group_sid);
863 return;
866 /* Ok, this might be "ours", i.e. an alias */
867 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
868 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
869 (name_type == SID_NAME_ALIAS)) {
870 /* Hey, got an alias */
871 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
872 (unsigned long)(state->request.data.gid), sid));
873 winbindd_getgrsid(state, group_sid);
874 return;
877 DEBUG(1, ("could not convert gid %lu to sid\n",
878 (unsigned long)state->request.data.gid));
879 request_error(state);
882 /* Return a group structure from a gid number */
883 void winbindd_getgrgid(struct winbindd_cli_state *state)
885 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
886 (unsigned long)state->request.data.gid));
888 /* always use the async interface */
889 winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
893 * set/get/endgrent functions
896 /* "Rewind" file pointer for group database enumeration */
898 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
900 struct winbindd_domain *domain;
902 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
904 /* Check user has enabled this */
906 if (!lp_winbind_enum_groups()) {
907 return False;
910 /* Free old static data if it exists */
912 if (state->getgrent_state != NULL) {
913 free_getent_state(state->getgrent_state);
914 state->getgrent_state = NULL;
917 /* Create sam pipes for each domain we know about */
919 for (domain = domain_list(); domain != NULL; domain = domain->next) {
920 struct getent_state *domain_state;
922 /* Create a state record for this domain */
924 /* don't add our domaina if we are a PDC or if we
925 are a member of a Samba domain */
927 if ( lp_winbind_trusted_domains_only() && domain->primary )
929 continue;
933 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
934 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
935 return False;
938 ZERO_STRUCTP(domain_state);
940 fstrcpy(domain_state->domain_name, domain->name);
942 /* Add to list of open domains */
944 DLIST_ADD(state->getgrent_state, domain_state);
947 state->getgrent_initialized = True;
948 return True;
951 void winbindd_setgrent(struct winbindd_cli_state *state)
953 if (winbindd_setgrent_internal(state)) {
954 request_ok(state);
955 } else {
956 request_error(state);
960 /* Close file pointer to ntdom group database */
962 void winbindd_endgrent(struct winbindd_cli_state *state)
964 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
966 free_getent_state(state->getgrent_state);
967 state->getgrent_initialized = False;
968 state->getgrent_state = NULL;
969 request_ok(state);
972 /* Get the list of domain groups and domain aliases for a domain. We fill in
973 the sam_entries and num_sam_entries fields with domain group information.
974 Return True if some groups were returned, False otherwise. */
976 bool get_sam_group_entries(struct getent_state *ent)
978 NTSTATUS status;
979 uint32 num_entries;
980 struct acct_info *name_list = NULL;
981 TALLOC_CTX *mem_ctx;
982 bool result = False;
983 struct acct_info *sam_grp_entries = NULL;
984 struct winbindd_domain *domain;
986 if (ent->got_sam_entries)
987 return False;
989 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
990 ent->domain_name))) {
991 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
992 return False;
995 /* Free any existing group info */
997 SAFE_FREE(ent->sam_entries);
998 ent->num_sam_entries = 0;
999 ent->got_sam_entries = True;
1001 /* Enumerate domain groups */
1003 num_entries = 0;
1005 if (!(domain = find_domain_from_name(ent->domain_name))) {
1006 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
1007 goto done;
1010 /* always get the domain global groups */
1012 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1014 if (!NT_STATUS_IS_OK(status)) {
1015 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
1016 result = False;
1017 goto done;
1020 /* Copy entries into return buffer */
1022 if (num_entries) {
1023 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
1024 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
1025 num_entries));
1026 result = False;
1027 goto done;
1029 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
1032 ent->num_sam_entries = num_entries;
1034 /* get the domain local groups if we are a member of a native win2k domain
1035 and are not using LDAP to get the groups */
1037 if ( ( lp_security() != SEC_ADS && domain->native_mode
1038 && domain->primary) || domain->internal )
1040 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
1041 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
1043 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1045 if ( !NT_STATUS_IS_OK(status) ) {
1046 DEBUG(3,("get_sam_group_entries: "
1047 "Failed to enumerate "
1048 "domain local groups with error %s!\n",
1049 nt_errstr(status)));
1050 num_entries = 0;
1052 else
1053 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
1055 /* Copy entries into return buffer */
1057 if ( num_entries ) {
1058 if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
1060 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
1061 num_entries));
1062 result = False;
1063 goto done;
1066 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
1067 num_entries * sizeof(struct acct_info) );
1070 ent->num_sam_entries += num_entries;
1074 /* Fill in remaining fields */
1076 ent->sam_entries = name_list;
1077 ent->sam_entry_index = 0;
1079 result = (ent->num_sam_entries > 0);
1081 done:
1082 talloc_destroy(mem_ctx);
1084 return result;
1087 /* Fetch next group entry from ntdom database */
1089 #define MAX_GETGRENT_GROUPS 500
1091 void winbindd_getgrent(struct winbindd_cli_state *state)
1093 struct getent_state *ent;
1094 struct winbindd_gr *group_list = NULL;
1095 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1096 char *gr_mem_list = NULL;
1098 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1100 /* Check user has enabled this */
1102 if (!lp_winbind_enum_groups()) {
1103 request_error(state);
1104 return;
1107 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1109 if (num_groups == 0) {
1110 request_error(state);
1111 return;
1114 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
1115 request_error(state);
1116 return;
1119 memset(state->response.extra_data.data, '\0',
1120 num_groups * sizeof(struct winbindd_gr) );
1122 state->response.data.num_entries = 0;
1124 group_list = (struct winbindd_gr *)state->response.extra_data.data;
1126 if (!state->getgrent_initialized)
1127 winbindd_setgrent_internal(state);
1129 if (!(ent = state->getgrent_state)) {
1130 request_error(state);
1131 return;
1134 /* Start sending back groups */
1136 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1137 struct acct_info *name_list = NULL;
1138 fstring domain_group_name;
1139 uint32 result;
1140 gid_t group_gid;
1141 size_t gr_mem_len;
1142 char *gr_mem;
1143 DOM_SID group_sid;
1144 struct winbindd_domain *domain;
1146 /* Do we need to fetch another chunk of groups? */
1148 tryagain:
1150 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1151 ent->sam_entry_index, ent->num_sam_entries));
1153 if (ent->num_sam_entries == ent->sam_entry_index) {
1155 while(ent && !get_sam_group_entries(ent)) {
1156 struct getent_state *next_ent;
1158 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
1160 /* Free state information for this domain */
1162 SAFE_FREE(ent->sam_entries);
1164 next_ent = ent->next;
1165 DLIST_REMOVE(state->getgrent_state, ent);
1167 SAFE_FREE(ent);
1168 ent = next_ent;
1171 /* No more domains */
1173 if (!ent)
1174 break;
1177 name_list = (struct acct_info *)ent->sam_entries;
1179 if (!(domain =
1180 find_domain_from_name(ent->domain_name))) {
1181 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
1182 result = False;
1183 goto done;
1186 /* Lookup group info */
1188 sid_copy(&group_sid, &domain->sid);
1189 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1191 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) {
1192 union unid_t id;
1193 enum lsa_SidType type;
1195 DEBUG(10, ("SID %s not in idmap\n",
1196 sid_string_dbg(&group_sid)));
1198 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1199 DEBUG(1, ("could not look up gid for group "
1200 "%s\n",
1201 name_list[ent->sam_entry_index].acct_name));
1202 ent->sam_entry_index++;
1203 goto tryagain;
1206 if ((type != SID_NAME_DOM_GRP) &&
1207 (type != SID_NAME_ALIAS) &&
1208 (type != SID_NAME_WKN_GRP)) {
1209 DEBUG(1, ("Group %s is a %s, not a group\n",
1210 sid_type_lookup(type),
1211 name_list[ent->sam_entry_index].acct_name));
1212 ent->sam_entry_index++;
1213 goto tryagain;
1215 group_gid = id.gid;
1218 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
1219 (unsigned long)name_list[ent->sam_entry_index].rid));
1221 /* Fill in group entry */
1223 fill_domain_username(domain_group_name, ent->domain_name,
1224 name_list[ent->sam_entry_index].acct_name, True);
1226 result = fill_grent(&group_list[group_list_ndx],
1227 ent->domain_name,
1228 name_list[ent->sam_entry_index].acct_name,
1229 group_gid);
1231 /* Fill in group membership entry */
1233 if (result) {
1234 size_t num_gr_mem = 0;
1235 DOM_SID member_sid;
1236 group_list[group_list_ndx].num_gr_mem = 0;
1237 gr_mem = NULL;
1238 gr_mem_len = 0;
1240 /* Get group membership */
1241 if (state->request.cmd == WINBINDD_GETGRLST) {
1242 result = True;
1243 } else {
1244 sid_copy(&member_sid, &domain->sid);
1245 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1246 result = fill_grent_mem(
1247 domain,
1248 NULL,
1249 &member_sid,
1250 SID_NAME_DOM_GRP,
1251 &num_gr_mem,
1252 &gr_mem, &gr_mem_len);
1254 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1258 if (result) {
1259 /* Append to group membership list */
1260 gr_mem_list = (char *)SMB_REALLOC(
1261 gr_mem_list, gr_mem_list_len + gr_mem_len);
1263 if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
1264 DEBUG(0, ("out of memory\n"));
1265 gr_mem_list_len = 0;
1266 break;
1269 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1270 gr_mem_list_len, (unsigned int)gr_mem_len));
1272 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1273 gr_mem_len);
1275 SAFE_FREE(gr_mem);
1277 group_list[group_list_ndx].gr_mem_ofs =
1278 gr_mem_list_len;
1280 gr_mem_list_len += gr_mem_len;
1283 ent->sam_entry_index++;
1285 /* Add group to return list */
1287 if (result) {
1289 DEBUG(10, ("adding group num_entries = %d\n",
1290 state->response.data.num_entries));
1292 group_list_ndx++;
1293 state->response.data.num_entries++;
1295 state->response.length +=
1296 sizeof(struct winbindd_gr);
1298 } else {
1299 DEBUG(0, ("could not lookup domain group %s\n",
1300 domain_group_name));
1304 /* Copy the list of group memberships to the end of the extra data */
1306 if (group_list_ndx == 0)
1307 goto done;
1309 state->response.extra_data.data = SMB_REALLOC(
1310 state->response.extra_data.data,
1311 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1313 if (!state->response.extra_data.data) {
1314 DEBUG(0, ("out of memory\n"));
1315 group_list_ndx = 0;
1316 SAFE_FREE(gr_mem_list);
1317 request_error(state);
1318 return;
1321 memcpy(&((char *)state->response.extra_data.data)
1322 [group_list_ndx * sizeof(struct winbindd_gr)],
1323 gr_mem_list, gr_mem_list_len);
1325 state->response.length += gr_mem_list_len;
1327 DEBUG(10, ("returning %d groups, length = %d\n",
1328 group_list_ndx, gr_mem_list_len));
1330 /* Out of domains */
1332 done:
1334 SAFE_FREE(gr_mem_list);
1336 if (group_list_ndx > 0)
1337 request_ok(state);
1338 else
1339 request_error(state);
1342 /* List domain groups without mapping to unix ids */
1343 void winbindd_list_groups(struct winbindd_cli_state *state)
1345 winbindd_list_ent(state, LIST_GROUPS);
1348 /* Get user supplementary groups. This is much quicker than trying to
1349 invert the groups database. We merge the groups from the gids and
1350 other_sids info3 fields as trusted domain, universal group
1351 memberships, and nested groups (win2k native mode only) are not
1352 returned by the getgroups RPC call but are present in the info3. */
1354 struct getgroups_state {
1355 struct winbindd_cli_state *state;
1356 struct winbindd_domain *domain;
1357 char *domname;
1358 char *username;
1359 DOM_SID user_sid;
1361 const DOM_SID *token_sids;
1362 size_t i, num_token_sids;
1364 gid_t *token_gids;
1365 size_t num_token_gids;
1368 static void getgroups_usersid_recv(void *private_data, bool success,
1369 const DOM_SID *sid, enum lsa_SidType type);
1370 static void getgroups_tokensids_recv(void *private_data, bool success,
1371 DOM_SID *token_sids, size_t num_token_sids);
1372 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1374 void winbindd_getgroups(struct winbindd_cli_state *state)
1376 struct getgroups_state *s;
1378 /* Ensure null termination */
1379 state->request.data.username
1380 [sizeof(state->request.data.username)-1]='\0';
1382 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1383 state->request.data.username));
1385 /* Parse domain and username */
1387 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1388 if (s == NULL) {
1389 DEBUG(0, ("talloc failed\n"));
1390 request_error(state);
1391 return;
1394 s->state = state;
1396 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1398 if (!parse_domain_user_talloc(state->mem_ctx,
1399 state->request.data.username,
1400 &s->domname, &s->username)) {
1401 DEBUG(5, ("Could not parse domain user: %s\n",
1402 state->request.data.username));
1404 /* error out if we do not have nested group support */
1406 if ( !lp_winbind_nested_groups() ) {
1407 request_error(state);
1408 return;
1411 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1412 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1415 /* Get info for the domain (either by short domain name or
1416 DNS name in the case of a UPN) */
1418 s->domain = find_domain_from_name_noinit(s->domname);
1419 if (!s->domain) {
1420 char *p = strchr(s->username, '@');
1422 if (p) {
1423 s->domain = find_domain_from_name_noinit(p+1);
1428 if (s->domain == NULL) {
1429 DEBUG(7, ("could not find domain entry for domain %s\n",
1430 s->domname));
1431 request_error(state);
1432 return;
1435 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1436 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1437 "getgroups() for %s\\%s.\n", s->domname,
1438 s->username));
1439 request_error(state);
1440 return;
1443 /* Get rid and name type from name. The following costs 1 packet */
1445 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1446 getgroups_usersid_recv, WINBINDD_GETGROUPS, s);
1449 static void getgroups_usersid_recv(void *private_data, bool success,
1450 const DOM_SID *sid, enum lsa_SidType type)
1452 struct getgroups_state *s =
1453 (struct getgroups_state *)private_data;
1455 if ((!success) ||
1456 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1457 request_error(s->state);
1458 return;
1461 sid_copy(&s->user_sid, sid);
1463 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1464 getgroups_tokensids_recv, s);
1467 static void getgroups_tokensids_recv(void *private_data, bool success,
1468 DOM_SID *token_sids, size_t num_token_sids)
1470 struct getgroups_state *s =
1471 (struct getgroups_state *)private_data;
1473 /* We need at least the user sid and the primary group in the token,
1474 * otherwise it's an error */
1476 if ((!success) || (num_token_sids < 2)) {
1477 request_error(s->state);
1478 return;
1481 s->token_sids = token_sids;
1482 s->num_token_sids = num_token_sids;
1483 s->i = 0;
1485 s->token_gids = NULL;
1486 s->num_token_gids = 0;
1488 getgroups_sid2gid_recv(s, False, 0);
1491 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1493 struct getgroups_state *s =
1494 (struct getgroups_state *)private_data;
1496 if (success) {
1497 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1498 &s->token_gids,
1499 &s->num_token_gids)) {
1500 return;
1504 if (s->i < s->num_token_sids) {
1505 const DOM_SID *sid = &s->token_sids[s->i];
1506 s->i += 1;
1508 if (sid_equal(sid, &s->user_sid)) {
1509 getgroups_sid2gid_recv(s, False, 0);
1510 return;
1513 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1514 getgroups_sid2gid_recv, s);
1515 return;
1518 s->state->response.data.num_entries = s->num_token_gids;
1519 if (s->num_token_gids) {
1520 /* s->token_gids are talloced */
1521 s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t));
1522 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1524 request_ok(s->state);
1527 /* Get user supplementary sids. This is equivalent to the
1528 winbindd_getgroups() function but it involves a SID->SIDs mapping
1529 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1530 idmap. This call is designed to be used with applications that need
1531 to do ACL evaluation themselves. Note that the cached info3 data is
1532 not used
1534 this function assumes that the SID that comes in is a user SID. If
1535 you pass in another type of SID then you may get unpredictable
1536 results.
1539 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1540 size_t num_sids);
1542 void winbindd_getusersids(struct winbindd_cli_state *state)
1544 DOM_SID *user_sid;
1546 /* Ensure null termination */
1547 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1549 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1550 if (user_sid == NULL) {
1551 DEBUG(1, ("talloc failed\n"));
1552 request_error(state);
1553 return;
1556 if (!string_to_sid(user_sid, state->request.data.sid)) {
1557 DEBUG(1, ("Could not get convert sid %s from string\n",
1558 state->request.data.sid));
1559 request_error(state);
1560 return;
1563 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1564 state);
1567 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1568 size_t num_sids)
1570 struct winbindd_cli_state *state =
1571 (struct winbindd_cli_state *)private_data;
1572 char *ret = NULL;
1573 unsigned ofs, ret_size = 0;
1574 size_t i;
1576 if (!success) {
1577 request_error(state);
1578 return;
1581 /* work out the response size */
1582 for (i = 0; i < num_sids; i++) {
1583 fstring s;
1584 sid_to_fstring(s, &sids[i]);
1585 ret_size += strlen(s) + 1;
1588 /* build the reply */
1589 ret = (char *)SMB_MALLOC(ret_size);
1590 if (!ret) {
1591 DEBUG(0, ("malloc failed\n"));
1592 request_error(state);
1593 return;
1595 ofs = 0;
1596 for (i = 0; i < num_sids; i++) {
1597 fstring s;
1598 sid_to_fstring(s, &sids[i]);
1599 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1600 ofs += strlen(ret+ofs) + 1;
1603 /* Send data back to client */
1604 state->response.data.num_entries = num_sids;
1605 state->response.extra_data.data = ret;
1606 state->response.length += ret_size;
1607 request_ok(state);
1610 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1612 DOM_SID user_sid;
1613 struct winbindd_domain *domain;
1615 /* Ensure null termination */
1616 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1618 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1619 DEBUG(1, ("Could not get convert sid %s from string\n",
1620 state->request.data.sid));
1621 request_error(state);
1622 return;
1625 /* Get info for the domain */
1626 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1627 DEBUG(0,("could not find domain entry for sid %s\n",
1628 sid_string_dbg(&user_sid)));
1629 request_error(state);
1630 return;
1633 sendto_domain(state, domain);
1636 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1637 struct winbindd_cli_state *state)
1639 DOM_SID user_sid;
1640 NTSTATUS status;
1642 char *sidstring;
1643 ssize_t len;
1644 DOM_SID *groups;
1645 uint32 num_groups;
1647 /* Ensure null termination */
1648 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1650 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1651 DEBUG(1, ("Could not get convert sid %s from string\n",
1652 state->request.data.sid));
1653 return WINBINDD_ERROR;
1656 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1657 &user_sid, &num_groups,
1658 &groups);
1659 if (!NT_STATUS_IS_OK(status))
1660 return WINBINDD_ERROR;
1662 if (num_groups == 0) {
1663 state->response.data.num_entries = 0;
1664 state->response.extra_data.data = NULL;
1665 return WINBINDD_OK;
1668 if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) {
1669 DEBUG(0, ("talloc failed\n"));
1670 return WINBINDD_ERROR;
1673 state->response.extra_data.data = SMB_STRDUP(sidstring);
1674 if (!state->response.extra_data.data) {
1675 return WINBINDD_ERROR;
1677 state->response.length += len+1;
1678 state->response.data.num_entries = num_groups;
1680 return WINBINDD_OK;