idmap rewrite
[Samba.git] / source3 / winbindd / winbindd_group.c
blob21ee8951b5735a91229b8787c3781bc6ff57b30b
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,
49 char **pp_members,
50 size_t *p_num_members)
52 DOM_SID dom_sid;
53 uint32 rid;
54 struct winbindd_domain *domain;
55 size_t i;
57 char *domain_name = NULL;
58 char *name = NULL;
59 enum lsa_SidType type;
61 uint32 num_names;
62 DOM_SID *sid_mem;
63 char **names;
64 uint32 *types;
66 NTSTATUS result;
68 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
70 if (mem_ctx == NULL) {
71 DEBUG(1, ("talloc_init failed\n"));
72 return;
75 sid_copy(&dom_sid, sid);
76 sid_split_rid(&dom_sid, &rid);
78 domain = find_lookup_domain_from_sid(sid);
80 if (domain == NULL) {
81 DEBUG(3, ("Could not find domain for sid %s\n",
82 sid_string_dbg(sid)));
83 goto done;
86 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
87 &domain_name, &name, &type);
89 if (!NT_STATUS_IS_OK(result)) {
90 DEBUG(3, ("sid_to_name failed for sid %s\n",
91 sid_string_dbg(sid)));
92 goto done;
95 DEBUG(10, ("Found name %s, type %d\n", name, type));
97 if (type == SID_NAME_USER) {
98 add_member(domain_name, name, pp_members, p_num_members);
99 goto done;
102 if (type != SID_NAME_DOM_GRP) {
103 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
104 name));
105 goto done;
108 /* Expand the domain group, this must be done via the target domain */
110 domain = find_domain_from_sid(sid);
112 if (domain == NULL) {
113 DEBUG(3, ("Could not find domain from SID %s\n",
114 sid_string_dbg(sid)));
115 goto done;
118 result = domain->methods->lookup_groupmem(domain, mem_ctx,
119 sid, &num_names,
120 &sid_mem, &names,
121 &types);
123 if (!NT_STATUS_IS_OK(result)) {
124 DEBUG(10, ("Could not lookup group members for %s: %s\n",
125 name, nt_errstr(result)));
126 goto done;
129 for (i=0; i<num_names; i++) {
130 DEBUG(10, ("Adding group member SID %s\n",
131 sid_string_dbg(&sid_mem[i])));
133 if (types[i] != SID_NAME_USER) {
134 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
135 "Ignoring.\n", names[i], name));
136 continue;
139 add_member(domain->name, names[i], pp_members, p_num_members);
142 done:
143 talloc_destroy(mem_ctx);
144 return;
147 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
148 DOM_SID *group_sid, size_t *num_gr_mem,
149 char **gr_mem, size_t *gr_mem_len)
151 DOM_SID *members;
152 size_t i, num_members;
154 *num_gr_mem = 0;
155 *gr_mem = NULL;
156 *gr_mem_len = 0;
158 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
159 &num_members)))
160 return True;
162 for (i=0; i<num_members; i++) {
163 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
166 TALLOC_FREE(members);
168 if (*gr_mem != NULL) {
169 size_t len;
171 /* We have at least one member, strip off the last "," */
172 len = strlen(*gr_mem);
173 (*gr_mem)[len-1] = '\0';
174 *gr_mem_len = len;
177 return True;
180 /* Fill a grent structure from various other information */
182 static bool fill_grent(struct winbindd_gr *gr, const char *dom_name,
183 const char *gr_name, gid_t unix_gid)
185 fstring full_group_name;
187 fill_domain_username( full_group_name, dom_name, gr_name, True );
189 gr->gr_gid = unix_gid;
191 /* Group name and password */
193 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
194 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
196 return True;
199 /***********************************************************************
200 If "enum users" is set to false, and the group being looked
201 up is the Domain Users SID: S-1-5-domain-513, then for the
202 list of members check if the querying user is in that group,
203 and if so only return that user as the gr_mem array.
204 We can change this to a different parameter than "enum users"
205 if neccessaey, or parameterize the group list we do this for.
206 ***********************************************************************/
208 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
209 struct winbindd_domain *domain,
210 struct winbindd_cli_state *state,
211 DOM_SID *group_sid,
212 enum lsa_SidType group_name_type,
213 size_t *num_gr_mem, char **gr_mem,
214 size_t *gr_mem_len)
216 DOM_SID querying_user_sid;
217 DOM_SID *pquerying_user_sid = NULL;
218 uint32 num_groups = 0;
219 DOM_SID *user_sids = NULL;
220 bool u_in_group = False;
221 NTSTATUS status;
222 int i;
223 unsigned int buf_len = 0;
224 char *buf = NULL;
226 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
227 domain->name ));
229 if (state) {
230 uid_t ret_uid = (uid_t)-1;
231 if (sys_getpeereid(state->sock, &ret_uid)==0) {
232 /* We know who's asking - look up their SID if
233 it's one we've mapped before. */
234 status = idmap_uid_to_sid(domain->name,
235 &querying_user_sid, ret_uid);
236 if (NT_STATUS_IS_OK(status)) {
237 pquerying_user_sid = &querying_user_sid;
238 DEBUG(10,("fill_grent_mem_domain_users: "
239 "querying uid %u -> %s\n",
240 (unsigned int)ret_uid,
241 sid_string_dbg(pquerying_user_sid)));
246 /* Only look up if it was a winbindd user in this domain. */
247 if (pquerying_user_sid &&
248 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
250 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
251 sid_string_dbg(pquerying_user_sid) ));
253 status = domain->methods->lookup_usergroups(domain,
254 mem_ctx,
255 pquerying_user_sid,
256 &num_groups,
257 &user_sids);
258 if (!NT_STATUS_IS_OK(status)) {
259 DEBUG(1, ("fill_grent_mem_domain_users: "
260 "lookup_usergroups failed "
261 "for sid %s in domain %s (error: %s)\n",
262 sid_string_dbg(pquerying_user_sid),
263 domain->name,
264 nt_errstr(status)));
265 return False;
268 for (i = 0; i < num_groups; i++) {
269 if (sid_equal(group_sid, &user_sids[i])) {
270 /* User is in Domain Users, add their name
271 as the only group member. */
272 u_in_group = True;
273 break;
278 if (u_in_group) {
279 size_t len = 0;
280 char *domainname = NULL;
281 char *username = NULL;
282 fstring name;
283 enum lsa_SidType type;
285 DEBUG(10,("fill_grent_mem_domain_users: "
286 "sid %s in 'Domain Users' in domain %s\n",
287 sid_string_dbg(pquerying_user_sid),
288 domain->name ));
290 status = domain->methods->sid_to_name(domain, mem_ctx,
291 pquerying_user_sid,
292 &domainname,
293 &username,
294 &type);
295 if (!NT_STATUS_IS_OK(status)) {
296 DEBUG(1, ("could not lookup username for user "
297 "sid %s in domain %s (error: %s)\n",
298 sid_string_dbg(pquerying_user_sid),
299 domain->name,
300 nt_errstr(status)));
301 return False;
303 fill_domain_username(name, domain->name, username, True);
304 len = strlen(name);
305 buf_len = len + 1;
306 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
307 DEBUG(1, ("out of memory\n"));
308 return False;
310 memcpy(buf, name, buf_len);
312 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
313 "'Domain Users' in domain %s\n",
314 name, domain->name ));
316 /* user is the only member */
317 *num_gr_mem = 1;
320 *gr_mem = buf;
321 *gr_mem_len = buf_len;
323 DEBUG(10, ("fill_grent_mem_domain_users: "
324 "num_mem = %u, len = %u, mem = %s\n",
325 (unsigned int)*num_gr_mem,
326 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
328 return True;
331 /***********************************************************************
332 Add names to a list. Assumes a canonical version of the string
333 in DOMAIN\user
334 ***********************************************************************/
336 static int namecmp( const void *a, const void *b )
338 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
341 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
342 char ***list, uint32 *n_list,
343 char **names, uint32 n_names )
345 char **new_list = NULL;
346 uint32 n_new_list = 0;
347 int i, j;
349 if ( !names || (n_names == 0) )
350 return NT_STATUS_OK;
352 /* Alloc the maximum size we'll need */
354 if ( *list == NULL ) {
355 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
356 return NT_STATUS_NO_MEMORY;
358 n_new_list = n_names;
359 } else {
360 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
361 (*n_list) + n_names );
362 if ( !new_list )
363 return NT_STATUS_NO_MEMORY;
364 n_new_list = (*n_list) + n_names;
367 /* Add all names */
369 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
370 new_list[i] = talloc_strdup( new_list, names[j] );
373 /* search for duplicates for sorting and looking for matching
374 neighbors */
376 qsort( new_list, n_new_list, sizeof(char*), QSORT_CAST namecmp );
378 for ( i=1; i<n_new_list; i++ ) {
379 if ( strcmp( new_list[i-1], new_list[i] ) == 0 ) {
380 memmove( &new_list[i-1], &new_list[i],
381 sizeof(char*)*(n_new_list-i) );
382 n_new_list--;
386 *list = new_list;
387 *n_list = n_new_list;
389 return NT_STATUS_OK;
392 /***********************************************************************
393 ***********************************************************************/
395 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
396 struct winbindd_domain *d,
397 DOM_SID *glist, uint32 n_glist,
398 DOM_SID **new_glist, uint32 *n_new_glist,
399 char ***members, uint32 *n_members )
401 int i, j;
402 NTSTATUS status = NT_STATUS_OK;
403 uint32 num_names = 0;
404 uint32 *name_types = NULL;
405 char **names = NULL;
406 DOM_SID *sid_mem = NULL;
407 TALLOC_CTX *tmp_ctx = NULL;
408 DOM_SID *new_groups = NULL;
409 size_t new_groups_size = 0;
411 *members = NULL;
412 *n_members = 0;
413 *new_glist = NULL;
414 *n_new_glist = 0;
416 for ( i=0; i<n_glist; i++ ) {
417 tmp_ctx = talloc_new( ctx );
419 /* Lookup the group membership */
421 status = d->methods->lookup_groupmem(d, tmp_ctx,
422 &glist[i], &num_names,
423 &sid_mem, &names,
424 &name_types);
425 if ( !NT_STATUS_IS_OK(status) )
426 goto out;
428 /* Separate users and groups into two lists */
430 for ( j=0; j<num_names; j++ ) {
432 /* Users */
433 if ( name_types[j] == SID_NAME_USER ||
434 name_types[j] == SID_NAME_COMPUTER )
436 status = add_names_to_list( ctx, members,
437 n_members,
438 names+j, 1 );
439 if ( !NT_STATUS_IS_OK(status) )
440 goto out;
442 continue;
445 /* Groups */
446 if ( name_types[j] == SID_NAME_DOM_GRP ||
447 name_types[j] == SID_NAME_ALIAS )
449 status = add_sid_to_array_unique(ctx,
450 &sid_mem[j],
451 &new_groups,
452 &new_groups_size);
453 if (!NT_STATUS_IS_OK(status)) {
454 goto out;
457 continue;
461 TALLOC_FREE( tmp_ctx );
464 *new_glist = new_groups;
465 *n_new_glist = (uint32)new_groups_size;
467 out:
468 TALLOC_FREE( tmp_ctx );
470 return status;
473 /***********************************************************************
474 Fill in the group membership field of a NT group given by group_sid
475 ***********************************************************************/
477 static bool fill_grent_mem(struct winbindd_domain *domain,
478 struct winbindd_cli_state *state,
479 DOM_SID *group_sid,
480 enum lsa_SidType group_name_type,
481 size_t *num_gr_mem, char **gr_mem,
482 size_t *gr_mem_len)
484 uint32 num_names = 0;
485 unsigned int buf_len = 0, buf_ndx = 0, i;
486 char **names = NULL, *buf = NULL;
487 bool result = False;
488 TALLOC_CTX *mem_ctx;
489 uint32 group_rid;
490 DOM_SID *glist = NULL;
491 DOM_SID *new_glist = NULL;
492 uint32 n_glist, n_new_glist;
493 int max_depth = lp_winbind_expand_groups();
495 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
496 return False;
498 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
500 /* Initialize with no members */
502 *num_gr_mem = 0;
504 /* HACK ALERT!! This whole routine does not cope with group members
505 * from more than one domain, ie aliases. Thus we have to work it out
506 * ourselves in a special routine. */
508 if (domain->internal) {
509 result = fill_passdb_alias_grmem(domain, group_sid,
510 num_gr_mem,
511 gr_mem, gr_mem_len);
512 goto done;
515 /* Verify name type */
517 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
518 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
520 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
521 sid_string_dbg(group_sid),
522 domain->name, group_name_type));
523 goto done;
526 /* OPTIMIZATION / HACK. See comment in
527 fill_grent_mem_domusers() */
529 sid_peek_rid( group_sid, &group_rid );
530 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
531 result = fill_grent_mem_domusers( mem_ctx, domain, state,
532 group_sid, group_name_type,
533 num_gr_mem, gr_mem,
534 gr_mem_len );
535 goto done;
538 /* Real work goes here. Create a list of group names to
539 expand startign with the initial one. Pass that to
540 expand_groups() which returns a list of more group names
541 to expand. Do this up to the max search depth. */
543 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
544 result = False;
545 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
546 goto done;
548 sid_copy( &glist[0], group_sid );
549 n_glist = 1;
551 for ( i=0; i<max_depth && glist; i++ ) {
552 uint32 n_members = 0;
553 char **members = NULL;
554 NTSTATUS nt_status;
556 nt_status = expand_groups( mem_ctx, domain,
557 glist, n_glist,
558 &new_glist, &n_new_glist,
559 &members, &n_members);
560 if ( !NT_STATUS_IS_OK(nt_status) ) {
561 result = False;
562 goto done;
565 /* Add new group members to list */
567 nt_status = add_names_to_list( mem_ctx, &names, &num_names,
568 members, n_members );
569 if ( !NT_STATUS_IS_OK(nt_status) ) {
570 result = False;
571 goto done;
574 TALLOC_FREE( members );
576 /* If we have no more groups to expand, break out
577 early */
579 if (new_glist == NULL)
580 break;
582 /* One more round */
583 TALLOC_FREE(glist);
584 glist = new_glist;
585 n_glist = n_new_glist;
587 TALLOC_FREE( glist );
589 DEBUG(10, ("looked up %d names\n", num_names));
591 again:
592 /* Add members to list */
594 for (i = 0; i < num_names; i++) {
595 int len;
597 DEBUG(10, ("processing name %s\n", names[i]));
599 len = strlen(names[i]);
601 /* Add to list or calculate buffer length */
603 if (!buf) {
604 buf_len += len + 1; /* List is comma separated */
605 (*num_gr_mem)++;
606 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
607 } else {
608 DEBUG(10, ("appending %s at ndx %d\n",
609 names[i], buf_ndx));
610 safe_strcpy(&buf[buf_ndx], names[i], len);
611 buf_ndx += len;
612 buf[buf_ndx] = ',';
613 buf_ndx++;
617 /* Allocate buffer */
619 if (!buf && buf_len != 0) {
620 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
621 DEBUG(1, ("out of memory\n"));
622 result = False;
623 goto done;
625 memset(buf, 0, buf_len);
626 goto again;
629 /* Now we're done */
631 if (buf && buf_ndx > 0) {
632 buf[buf_ndx - 1] = '\0';
635 *gr_mem = buf;
636 *gr_mem_len = buf_len;
638 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
639 (unsigned int)*num_gr_mem,
640 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
641 result = True;
643 done:
645 talloc_destroy(mem_ctx);
647 DEBUG(10, ("fill_grent_mem returning %d\n", result));
649 return result;
652 static void winbindd_getgrsid(struct winbindd_cli_state *state, DOM_SID group_sid);
654 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
655 enum lsa_SidType type )
657 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
659 if (!success) {
660 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
661 request_error(state);
662 return;
665 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
666 DEBUG(5,("getgrnam_recv: not a group!\n"));
667 request_error(state);
668 return;
671 winbindd_getgrsid( state, *sid );
675 /* Return a group structure from a group name */
677 void winbindd_getgrnam(struct winbindd_cli_state *state)
679 struct winbindd_domain *domain;
680 fstring name_domain, name_group;
681 char *tmp;
683 /* Ensure null termination */
684 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
686 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
687 state->request.data.groupname));
689 /* Parse domain and groupname */
691 memset(name_group, 0, sizeof(fstring));
693 tmp = state->request.data.groupname;
695 name_domain[0] = '\0';
696 name_group[0] = '\0';
698 parse_domain_user(tmp, name_domain, name_group);
700 /* if no domain or our local domain and no local tdb group, default to
701 * our local domain for aliases */
703 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
704 fstrcpy(name_domain, get_global_sam_name());
707 /* Get info for the domain */
709 if ((domain = find_domain_from_name(name_domain)) == NULL) {
710 DEBUG(3, ("could not get domain sid for domain %s\n",
711 name_domain));
712 request_error(state);
713 return;
715 /* should we deal with users for our domain? */
717 if ( lp_winbind_trusted_domains_only() && domain->primary) {
718 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
719 "getgrnam() for %s\\%s.\n", name_domain, name_group));
720 request_error(state);
721 return;
724 /* Get rid and name type from name */
726 ws_name_replace( name_group, WB_REPLACE_CHAR );
728 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
729 getgrnam_recv, WINBINDD_GETGRNAM, state );
732 struct getgrsid_state {
733 struct winbindd_cli_state *state;
734 struct winbindd_domain *domain;
735 char *group_name;
736 enum lsa_SidType group_type;
737 uid_t gid;
738 DOM_SID group_sid;
741 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
743 struct getgrsid_state *s =
744 (struct getgrsid_state *)private_data;
745 struct winbindd_domain *domain;
746 size_t gr_mem_len;
747 size_t num_gr_mem;
748 char *gr_mem;
749 fstring dom_name, group_name;
751 if (!success) {
752 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
753 request_error(s->state);
754 return;
757 s->gid = gid;
759 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
760 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
761 request_error(s->state);
762 return;
766 /* Fill in group structure */
768 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
769 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
770 request_error(s->state);
771 return;
774 if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
775 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
776 &num_gr_mem, &gr_mem, &gr_mem_len))
778 request_error(s->state);
779 return;
782 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
784 /* Group membership lives at start of extra data */
786 s->state->response.data.gr.gr_mem_ofs = 0;
788 s->state->response.length += gr_mem_len;
789 s->state->response.extra_data.data = gr_mem;
791 request_ok(s->state);
794 static void getgrsid_lookupsid_recv( void *private_data, bool success,
795 const char *dom_name, const char *name,
796 enum lsa_SidType name_type )
798 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
800 if (!success) {
801 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
802 request_error(s->state);
803 return;
806 /* either it's a domain group, a domain local group, or a
807 local group in an internal domain */
809 if ( !( (name_type==SID_NAME_DOM_GRP) ||
810 ((name_type==SID_NAME_ALIAS) &&
811 (s->domain->primary || s->domain->internal)) ) )
813 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
814 dom_name, name, name_type));
815 request_error(s->state);
816 return;
819 if ( (s->group_name = talloc_asprintf( s->state->mem_ctx,
820 "%s%c%s",
821 dom_name,
822 *lp_winbind_separator(),
823 name)) == NULL )
825 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
826 request_error(s->state);
827 return;
830 s->group_type = name_type;
832 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
833 getgrsid_sid2gid_recv, s);
836 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
838 struct getgrsid_state *s;
840 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
841 DEBUG(0, ("talloc failed\n"));
842 request_error(state);
843 return;
846 s->state = state;
848 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
849 DEBUG(3, ("Could not find domain for sid %s\n",
850 sid_string_dbg(&group_sid)));
851 request_error(state);
852 return;
855 sid_copy(&s->group_sid, &group_sid);
857 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
858 getgrsid_lookupsid_recv, s );
862 static void getgrgid_recv(void *private_data, bool success, const char *sid)
864 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
865 enum lsa_SidType name_type;
866 DOM_SID group_sid;
868 if (success) {
869 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
870 (unsigned long)(state->request.data.gid), sid));
872 string_to_sid(&group_sid, sid);
873 winbindd_getgrsid(state, group_sid);
874 return;
877 /* Ok, this might be "ours", i.e. an alias */
878 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
879 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
880 (name_type == SID_NAME_ALIAS)) {
881 /* Hey, got an alias */
882 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
883 (unsigned long)(state->request.data.gid), sid));
884 winbindd_getgrsid(state, group_sid);
885 return;
888 DEBUG(1, ("could not convert gid %lu to sid\n",
889 (unsigned long)state->request.data.gid));
890 request_error(state);
893 /* Return a group structure from a gid number */
894 void winbindd_getgrgid(struct winbindd_cli_state *state)
896 gid_t gid = state->request.data.gid;
898 DEBUG(3, ("[%5lu]: getgrgid %lu\n",
899 (unsigned long)state->pid,
900 (unsigned long)gid));
902 /* always use the async interface */
903 winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
907 * set/get/endgrent functions
910 /* "Rewind" file pointer for group database enumeration */
912 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
914 struct winbindd_domain *domain;
916 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
918 /* Check user has enabled this */
920 if (!lp_winbind_enum_groups()) {
921 return False;
924 /* Free old static data if it exists */
926 if (state->getgrent_state != NULL) {
927 free_getent_state(state->getgrent_state);
928 state->getgrent_state = NULL;
931 /* Create sam pipes for each domain we know about */
933 for (domain = domain_list(); domain != NULL; domain = domain->next) {
934 struct getent_state *domain_state;
936 /* Create a state record for this domain */
938 /* don't add our domaina if we are a PDC or if we
939 are a member of a Samba domain */
941 if ( lp_winbind_trusted_domains_only() && domain->primary )
943 continue;
946 domain_state = SMB_MALLOC_P(struct getent_state);
947 if (!domain_state) {
948 DEBUG(1, ("winbindd_setgrent: "
949 "malloc failed for domain_state!\n"));
950 return False;
953 ZERO_STRUCTP(domain_state);
955 fstrcpy(domain_state->domain_name, domain->name);
957 /* Add to list of open domains */
959 DLIST_ADD(state->getgrent_state, domain_state);
962 state->getgrent_initialized = True;
963 return True;
966 void winbindd_setgrent(struct winbindd_cli_state *state)
968 if (winbindd_setgrent_internal(state)) {
969 request_ok(state);
970 } else {
971 request_error(state);
975 /* Close file pointer to ntdom group database */
977 void winbindd_endgrent(struct winbindd_cli_state *state)
979 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
981 free_getent_state(state->getgrent_state);
982 state->getgrent_initialized = False;
983 state->getgrent_state = NULL;
984 request_ok(state);
987 /* Get the list of domain groups and domain aliases for a domain. We fill in
988 the sam_entries and num_sam_entries fields with domain group information.
989 Return True if some groups were returned, False otherwise. */
991 bool get_sam_group_entries(struct getent_state *ent)
993 NTSTATUS status;
994 uint32 num_entries;
995 struct acct_info *name_list = NULL;
996 TALLOC_CTX *mem_ctx;
997 bool result = False;
998 struct acct_info *sam_grp_entries = NULL;
999 struct winbindd_domain *domain;
1001 if (ent->got_sam_entries)
1002 return False;
1004 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1005 ent->domain_name))) {
1006 DEBUG(1, ("get_sam_group_entries: "
1007 "could not create talloc context!\n"));
1008 return False;
1011 /* Free any existing group info */
1013 SAFE_FREE(ent->sam_entries);
1014 ent->num_sam_entries = 0;
1015 ent->got_sam_entries = True;
1017 /* Enumerate domain groups */
1019 num_entries = 0;
1021 if (!(domain = find_domain_from_name(ent->domain_name))) {
1022 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1023 ent->domain_name));
1024 goto done;
1027 /* always get the domain global groups */
1029 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1030 &sam_grp_entries);
1032 if (!NT_STATUS_IS_OK(status)) {
1033 DEBUG(3, ("get_sam_group_entries: "
1034 "could not enumerate domain groups! Error: %s\n",
1035 nt_errstr(status)));
1036 result = False;
1037 goto done;
1040 /* Copy entries into return buffer */
1042 if (num_entries) {
1043 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1044 if (!name_list) {
1045 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1046 "memory for %d domain groups!\n",
1047 num_entries));
1048 result = False;
1049 goto done;
1051 memcpy(name_list, sam_grp_entries,
1052 num_entries * sizeof(struct acct_info));
1055 ent->num_sam_entries = num_entries;
1057 /* get the domain local groups if we are a member of a native win2k
1058 * domain and are not using LDAP to get the groups */
1060 if ( ( lp_security() != SEC_ADS && domain->native_mode
1061 && domain->primary) || domain->internal )
1063 DEBUG(4,("get_sam_group_entries: %s domain; "
1064 "enumerating local groups as well\n",
1065 domain->native_mode ? "Native Mode 2k":
1066 "BUILTIN or local"));
1068 status = domain->methods->enum_local_groups(domain, mem_ctx,
1069 &num_entries,
1070 &sam_grp_entries);
1072 if ( !NT_STATUS_IS_OK(status) ) {
1073 DEBUG(3,("get_sam_group_entries: "
1074 "Failed to enumerate "
1075 "domain local groups with error %s!\n",
1076 nt_errstr(status)));
1077 num_entries = 0;
1079 else
1080 DEBUG(4,("get_sam_group_entries: "
1081 "Returned %d local groups\n",
1082 num_entries));
1084 /* Copy entries into return buffer */
1086 if ( num_entries ) {
1087 name_list = SMB_REALLOC_ARRAY(name_list,
1088 struct acct_info,
1089 ent->num_sam_entries+
1090 num_entries);
1091 if (!name_list) {
1092 DEBUG(0,("get_sam_group_entries: "
1093 "Failed to realloc more memory "
1094 "for %d local groups!\n",
1095 num_entries));
1096 result = False;
1097 goto done;
1100 memcpy(&name_list[ent->num_sam_entries],
1101 sam_grp_entries,
1102 num_entries * sizeof(struct acct_info));
1105 ent->num_sam_entries += num_entries;
1109 /* Fill in remaining fields */
1111 ent->sam_entries = name_list;
1112 ent->sam_entry_index = 0;
1114 result = (ent->num_sam_entries > 0);
1116 done:
1117 talloc_destroy(mem_ctx);
1119 return result;
1122 /* Fetch next group entry from ntdom database */
1124 #define MAX_GETGRENT_GROUPS 500
1126 void winbindd_getgrent(struct winbindd_cli_state *state)
1128 struct getent_state *ent;
1129 struct winbindd_gr *group_list = NULL;
1130 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1131 char *gr_mem_list = NULL;
1133 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1135 /* Check user has enabled this */
1137 if (!lp_winbind_enum_groups()) {
1138 request_error(state);
1139 return;
1142 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1144 if (num_groups == 0) {
1145 request_error(state);
1146 return;
1149 group_list = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups);
1150 if (!group_list) {
1151 request_error(state);
1152 return;
1154 /* will be freed by process_request() */
1155 state->response.extra_data.data = group_list;
1157 memset(state->response.extra_data.data, '\0',
1158 num_groups * sizeof(struct winbindd_gr) );
1160 state->response.data.num_entries = 0;
1162 if (!state->getgrent_initialized)
1163 winbindd_setgrent_internal(state);
1165 if (!(ent = state->getgrent_state)) {
1166 request_error(state);
1167 return;
1170 /* Start sending back groups */
1172 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1173 struct acct_info *name_list = NULL;
1174 fstring domain_group_name;
1175 uint32 result;
1176 gid_t group_gid;
1177 size_t gr_mem_len;
1178 char *gr_mem;
1179 DOM_SID group_sid;
1180 struct winbindd_domain *domain;
1182 /* Do we need to fetch another chunk of groups? */
1184 tryagain:
1186 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1187 ent->sam_entry_index, ent->num_sam_entries));
1189 if (ent->num_sam_entries == ent->sam_entry_index) {
1191 while(ent && !get_sam_group_entries(ent)) {
1192 struct getent_state *next_ent;
1194 DEBUG(10, ("freeing state info for domain %s\n",
1195 ent->domain_name));
1197 /* Free state information for this domain */
1199 SAFE_FREE(ent->sam_entries);
1201 next_ent = ent->next;
1202 DLIST_REMOVE(state->getgrent_state, ent);
1204 SAFE_FREE(ent);
1205 ent = next_ent;
1208 /* No more domains */
1210 if (!ent)
1211 break;
1214 name_list = (struct acct_info *)ent->sam_entries;
1216 if (!(domain = find_domain_from_name(ent->domain_name))) {
1217 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1218 ent->domain_name));
1219 result = False;
1220 goto done;
1223 /* Lookup group info */
1225 sid_copy(&group_sid, &domain->sid);
1226 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1228 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->name, &group_sid,
1229 &group_gid))) {
1230 union unid_t id;
1231 enum lsa_SidType type;
1233 DEBUG(10, ("SID %s not in idmap\n",
1234 sid_string_dbg(&group_sid)));
1236 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1237 DEBUG(1,("could not look up gid for group %s\n",
1238 name_list[ent->sam_entry_index].acct_name));
1239 ent->sam_entry_index++;
1240 goto tryagain;
1243 if ((type != SID_NAME_DOM_GRP) &&
1244 (type != SID_NAME_ALIAS) &&
1245 (type != SID_NAME_WKN_GRP)) {
1246 DEBUG(1, ("Group %s is a %s, not a group\n",
1247 sid_type_lookup(type),
1248 name_list[ent->sam_entry_index].acct_name));
1249 ent->sam_entry_index++;
1250 goto tryagain;
1252 group_gid = id.gid;
1255 DEBUG(10, ("got gid %lu for group %lu\n",
1256 (unsigned long)group_gid,
1257 (unsigned long)name_list[ent->sam_entry_index].rid));
1259 /* Fill in group entry */
1261 fill_domain_username(domain_group_name, ent->domain_name,
1262 name_list[ent->sam_entry_index].acct_name, True);
1264 result = fill_grent(&group_list[group_list_ndx],
1265 ent->domain_name,
1266 name_list[ent->sam_entry_index].acct_name,
1267 group_gid);
1269 /* Fill in group membership entry */
1271 if (result) {
1272 size_t num_gr_mem = 0;
1273 DOM_SID member_sid;
1274 group_list[group_list_ndx].num_gr_mem = 0;
1275 gr_mem = NULL;
1276 gr_mem_len = 0;
1278 /* Get group membership */
1279 if (state->request.cmd == WINBINDD_GETGRLST) {
1280 result = True;
1281 } else {
1282 sid_copy(&member_sid, &domain->sid);
1283 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1284 result = fill_grent_mem(
1285 domain,
1286 NULL,
1287 &member_sid,
1288 SID_NAME_DOM_GRP,
1289 &num_gr_mem,
1290 &gr_mem, &gr_mem_len);
1292 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1296 if (result) {
1297 /* Append to group membership list */
1298 gr_mem_list = (char *)SMB_REALLOC(
1299 gr_mem_list, gr_mem_list_len + gr_mem_len);
1301 if (!gr_mem_list &&
1302 (group_list[group_list_ndx].num_gr_mem != 0)) {
1303 DEBUG(0, ("out of memory\n"));
1304 gr_mem_list_len = 0;
1305 break;
1308 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1309 gr_mem_list_len, (unsigned int)gr_mem_len));
1311 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1312 gr_mem_len);
1314 SAFE_FREE(gr_mem);
1316 group_list[group_list_ndx].gr_mem_ofs =
1317 gr_mem_list_len;
1319 gr_mem_list_len += gr_mem_len;
1322 ent->sam_entry_index++;
1324 /* Add group to return list */
1326 if (result) {
1328 DEBUG(10, ("adding group num_entries = %d\n",
1329 state->response.data.num_entries));
1331 group_list_ndx++;
1332 state->response.data.num_entries++;
1334 state->response.length +=
1335 sizeof(struct winbindd_gr);
1337 } else {
1338 DEBUG(0, ("could not lookup domain group %s\n",
1339 domain_group_name));
1343 /* Copy the list of group memberships to the end of the extra data */
1345 if (group_list_ndx == 0)
1346 goto done;
1348 state->response.extra_data.data = SMB_REALLOC(
1349 state->response.extra_data.data,
1350 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1352 if (!state->response.extra_data.data) {
1353 DEBUG(0, ("out of memory\n"));
1354 group_list_ndx = 0;
1355 SAFE_FREE(gr_mem_list);
1356 request_error(state);
1357 return;
1360 memcpy(&((char *)state->response.extra_data.data)
1361 [group_list_ndx * sizeof(struct winbindd_gr)],
1362 gr_mem_list, gr_mem_list_len);
1364 state->response.length += gr_mem_list_len;
1366 DEBUG(10, ("returning %d groups, length = %d\n",
1367 group_list_ndx, gr_mem_list_len));
1369 /* Out of domains */
1371 done:
1373 SAFE_FREE(gr_mem_list);
1375 if (group_list_ndx > 0)
1376 request_ok(state);
1377 else
1378 request_error(state);
1381 /* List domain groups without mapping to unix ids */
1382 void winbindd_list_groups(struct winbindd_cli_state *state)
1384 winbindd_list_ent(state, LIST_GROUPS);
1387 /* Get user supplementary groups. This is much quicker than trying to
1388 invert the groups database. We merge the groups from the gids and
1389 other_sids info3 fields as trusted domain, universal group
1390 memberships, and nested groups (win2k native mode only) are not
1391 returned by the getgroups RPC call but are present in the info3. */
1393 struct getgroups_state {
1394 struct winbindd_cli_state *state;
1395 struct winbindd_domain *domain;
1396 char *domname;
1397 char *username;
1398 DOM_SID user_sid;
1400 const DOM_SID *token_sids;
1401 size_t i, num_token_sids;
1403 gid_t *token_gids;
1404 size_t num_token_gids;
1407 static void getgroups_usersid_recv(void *private_data, bool success,
1408 const DOM_SID *sid, enum lsa_SidType type);
1409 static void getgroups_tokensids_recv(void *private_data, bool success,
1410 DOM_SID *token_sids, size_t num_token_sids);
1411 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1413 void winbindd_getgroups(struct winbindd_cli_state *state)
1415 struct getgroups_state *s;
1417 /* Ensure null termination */
1418 state->request.data.username
1419 [sizeof(state->request.data.username)-1]='\0';
1421 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1422 state->request.data.username));
1424 /* Parse domain and username */
1426 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1427 if (s == NULL) {
1428 DEBUG(0, ("talloc failed\n"));
1429 request_error(state);
1430 return;
1433 s->state = state;
1435 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1437 if (!parse_domain_user_talloc(state->mem_ctx,
1438 state->request.data.username,
1439 &s->domname, &s->username)) {
1440 DEBUG(5, ("Could not parse domain user: %s\n",
1441 state->request.data.username));
1443 /* error out if we do not have nested group support */
1445 if ( !lp_winbind_nested_groups() ) {
1446 request_error(state);
1447 return;
1450 s->domname = talloc_strdup(state->mem_ctx,
1451 get_global_sam_name());
1452 s->username = talloc_strdup(state->mem_ctx,
1453 state->request.data.username);
1456 /* Get info for the domain (either by short domain name or
1457 DNS name in the case of a UPN) */
1459 s->domain = find_domain_from_name_noinit(s->domname);
1460 if (!s->domain) {
1461 char *p = strchr(s->username, '@');
1463 if (p) {
1464 s->domain = find_domain_from_name_noinit(p+1);
1469 if (s->domain == NULL) {
1470 DEBUG(7, ("could not find domain entry for domain %s\n",
1471 s->domname));
1472 request_error(state);
1473 return;
1476 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1477 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1478 "getgroups() for %s\\%s.\n", s->domname,
1479 s->username));
1480 request_error(state);
1481 return;
1484 /* Get rid and name type from name. The following costs 1 packet */
1486 winbindd_lookupname_async(state->mem_ctx,
1487 s->domname, s->username,
1488 getgroups_usersid_recv,
1489 WINBINDD_GETGROUPS, s);
1492 static void getgroups_usersid_recv(void *private_data, bool success,
1493 const DOM_SID *sid, enum lsa_SidType type)
1495 struct getgroups_state *s =
1496 (struct getgroups_state *)private_data;
1498 if ((!success) ||
1499 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1500 request_error(s->state);
1501 return;
1504 sid_copy(&s->user_sid, sid);
1506 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1507 getgroups_tokensids_recv, s);
1510 static void getgroups_tokensids_recv(void *private_data, bool success,
1511 DOM_SID *token_sids, size_t num_token_sids)
1513 struct getgroups_state *s =
1514 (struct getgroups_state *)private_data;
1516 /* We need at least the user sid and the primary group in the token,
1517 * otherwise it's an error */
1519 if ((!success) || (num_token_sids < 2)) {
1520 request_error(s->state);
1521 return;
1524 s->token_sids = token_sids;
1525 s->num_token_sids = num_token_sids;
1526 s->i = 0;
1528 s->token_gids = NULL;
1529 s->num_token_gids = 0;
1531 getgroups_sid2gid_recv(s, False, 0);
1534 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1536 struct getgroups_state *s =
1537 (struct getgroups_state *)private_data;
1539 if (success) {
1540 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1541 &s->token_gids,
1542 &s->num_token_gids)) {
1543 return;
1547 if (s->i < s->num_token_sids) {
1548 const DOM_SID *sid = &s->token_sids[s->i];
1549 s->i += 1;
1551 if (sid_equal(sid, &s->user_sid)) {
1552 getgroups_sid2gid_recv(s, False, 0);
1553 return;
1556 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1557 getgroups_sid2gid_recv, s);
1558 return;
1561 s->state->response.data.num_entries = s->num_token_gids;
1562 if (s->num_token_gids) {
1563 /* s->token_gids are talloced */
1564 s->state->response.extra_data.data =
1565 smb_xmemdup(s->token_gids,
1566 s->num_token_gids * sizeof(gid_t));
1567 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1569 request_ok(s->state);
1572 /* Get user supplementary sids. This is equivalent to the
1573 winbindd_getgroups() function but it involves a SID->SIDs mapping
1574 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1575 idmap. This call is designed to be used with applications that need
1576 to do ACL evaluation themselves. Note that the cached info3 data is
1577 not used
1579 this function assumes that the SID that comes in is a user SID. If
1580 you pass in another type of SID then you may get unpredictable
1581 results.
1584 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1585 size_t num_sids);
1587 void winbindd_getusersids(struct winbindd_cli_state *state)
1589 DOM_SID *user_sid;
1591 /* Ensure null termination */
1592 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1594 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1595 if (user_sid == NULL) {
1596 DEBUG(1, ("talloc failed\n"));
1597 request_error(state);
1598 return;
1601 if (!string_to_sid(user_sid, state->request.data.sid)) {
1602 DEBUG(1, ("Could not get convert sid %s from string\n",
1603 state->request.data.sid));
1604 request_error(state);
1605 return;
1608 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1609 state);
1612 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1613 size_t num_sids)
1615 struct winbindd_cli_state *state =
1616 (struct winbindd_cli_state *)private_data;
1617 char *ret = NULL;
1618 unsigned ofs, ret_size = 0;
1619 size_t i;
1621 if (!success) {
1622 request_error(state);
1623 return;
1626 /* work out the response size */
1627 for (i = 0; i < num_sids; i++) {
1628 fstring s;
1629 sid_to_fstring(s, &sids[i]);
1630 ret_size += strlen(s) + 1;
1633 /* build the reply */
1634 ret = (char *)SMB_MALLOC(ret_size);
1635 if (!ret) {
1636 DEBUG(0, ("malloc failed\n"));
1637 request_error(state);
1638 return;
1640 ofs = 0;
1641 for (i = 0; i < num_sids; i++) {
1642 fstring s;
1643 sid_to_fstring(s, &sids[i]);
1644 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1645 ofs += strlen(ret+ofs) + 1;
1648 /* Send data back to client */
1649 state->response.data.num_entries = num_sids;
1650 state->response.extra_data.data = ret;
1651 state->response.length += ret_size;
1652 request_ok(state);
1655 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1657 DOM_SID user_sid;
1658 struct winbindd_domain *domain;
1660 /* Ensure null termination */
1661 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1663 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1664 DEBUG(1, ("Could not get convert sid %s from string\n",
1665 state->request.data.sid));
1666 request_error(state);
1667 return;
1670 /* Get info for the domain */
1671 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1672 DEBUG(0,("could not find domain entry for sid %s\n",
1673 sid_string_dbg(&user_sid)));
1674 request_error(state);
1675 return;
1678 sendto_domain(state, domain);
1681 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1682 struct winbindd_cli_state *state)
1684 DOM_SID user_sid;
1685 NTSTATUS status;
1687 char *sidstring;
1688 ssize_t len;
1689 DOM_SID *groups;
1690 uint32 num_groups;
1692 /* Ensure null termination */
1693 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1695 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1696 DEBUG(1, ("Could not get convert sid %s from string\n",
1697 state->request.data.sid));
1698 return WINBINDD_ERROR;
1701 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1702 &user_sid, &num_groups,
1703 &groups);
1704 if (!NT_STATUS_IS_OK(status))
1705 return WINBINDD_ERROR;
1707 if (num_groups == 0) {
1708 state->response.data.num_entries = 0;
1709 state->response.extra_data.data = NULL;
1710 return WINBINDD_OK;
1713 if (!print_sidlist(state->mem_ctx,
1714 groups, num_groups,
1715 &sidstring, &len)) {
1716 DEBUG(0, ("talloc failed\n"));
1717 return WINBINDD_ERROR;
1720 state->response.extra_data.data = SMB_STRDUP(sidstring);
1721 if (!state->response.extra_data.data) {
1722 return WINBINDD_ERROR;
1724 state->response.length += len+1;
1725 state->response.data.num_entries = num_groups;
1727 return WINBINDD_OK;