s4:samldb: use the code path with async ldb
[Samba/gebeck_regimport.git] / source3 / winbindd / winbindd_group.c
blobf2b6fbefb502602384bdd5a818e3704f7992dc49
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 if (domain != NULL) {
39 fill_domain_username(name, domain, user, True);
40 } else {
41 fstrcpy(name, user);
43 safe_strcat(name, ",", sizeof(name)-1);
44 string_append(pp_members, name);
45 *p_num_members += 1;
48 /**********************************************************************
49 Add member users resulting from sid. Expand if it is a domain group.
50 **********************************************************************/
52 static void add_expanded_sid(const DOM_SID *sid,
53 char **pp_members,
54 size_t *p_num_members)
56 DOM_SID dom_sid;
57 uint32 rid;
58 struct winbindd_domain *domain;
59 size_t i;
61 char *domain_name = NULL;
62 char *name = NULL;
63 enum lsa_SidType type;
65 uint32 num_names;
66 DOM_SID *sid_mem;
67 char **names;
68 uint32 *types;
70 NTSTATUS result;
72 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
74 if (mem_ctx == NULL) {
75 DEBUG(1, ("talloc_init failed\n"));
76 return;
79 sid_copy(&dom_sid, sid);
80 sid_split_rid(&dom_sid, &rid);
82 domain = find_lookup_domain_from_sid(sid);
84 if (domain == NULL) {
85 DEBUG(3, ("Could not find domain for sid %s\n",
86 sid_string_dbg(sid)));
87 goto done;
90 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
91 &domain_name, &name, &type);
93 if (!NT_STATUS_IS_OK(result)) {
94 DEBUG(3, ("sid_to_name failed for sid %s\n",
95 sid_string_dbg(sid)));
96 goto done;
99 DEBUG(10, ("Found name %s, type %d\n", name, type));
101 if (type == SID_NAME_USER) {
102 add_member(domain_name, name, pp_members, p_num_members);
103 goto done;
106 if (type != SID_NAME_DOM_GRP) {
107 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
108 name));
109 goto done;
112 /* Expand the domain group, this must be done via the target domain */
114 domain = find_domain_from_sid(sid);
116 if (domain == NULL) {
117 DEBUG(3, ("Could not find domain from SID %s\n",
118 sid_string_dbg(sid)));
119 goto done;
122 result = domain->methods->lookup_groupmem(domain, mem_ctx,
123 sid, &num_names,
124 &sid_mem, &names,
125 &types);
127 if (!NT_STATUS_IS_OK(result)) {
128 DEBUG(10, ("Could not lookup group members for %s: %s\n",
129 name, nt_errstr(result)));
130 goto done;
133 for (i=0; i<num_names; i++) {
134 DEBUG(10, ("Adding group member SID %s\n",
135 sid_string_dbg(&sid_mem[i])));
137 if (types[i] != SID_NAME_USER) {
138 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
139 "Ignoring.\n", names[i], name));
140 continue;
143 add_member(NULL, names[i], pp_members, p_num_members);
146 done:
147 talloc_destroy(mem_ctx);
148 return;
151 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
152 DOM_SID *group_sid, size_t *num_gr_mem,
153 char **gr_mem, size_t *gr_mem_len)
155 DOM_SID *members;
156 size_t i, num_members;
158 *num_gr_mem = 0;
159 *gr_mem = NULL;
160 *gr_mem_len = 0;
162 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
163 &num_members)))
164 return True;
166 for (i=0; i<num_members; i++) {
167 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
170 TALLOC_FREE(members);
172 if (*gr_mem != NULL) {
173 size_t len;
175 /* We have at least one member, strip off the last "," */
176 len = strlen(*gr_mem);
177 (*gr_mem)[len-1] = '\0';
178 *gr_mem_len = len;
181 return True;
184 /* Fill a grent structure from various other information */
186 static bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
187 const char *dom_name,
188 char *gr_name, gid_t unix_gid)
190 fstring full_group_name;
191 char *mapped_name = NULL;
192 struct winbindd_domain *domain = find_domain_from_name_noinit(dom_name);
193 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
195 nt_status = normalize_name_map(mem_ctx, domain, gr_name,
196 &mapped_name);
198 /* Basic whitespace replacement */
199 if (NT_STATUS_IS_OK(nt_status)) {
200 fill_domain_username(full_group_name, dom_name,
201 mapped_name, true);
203 /* Mapped to an aliase */
204 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
205 fstrcpy(full_group_name, mapped_name);
207 /* no change */
208 else {
209 fill_domain_username( full_group_name, dom_name,
210 gr_name, True );
213 gr->gr_gid = unix_gid;
215 /* Group name and password */
217 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
218 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
220 return True;
223 /***********************************************************************
224 If "enum users" is set to false, and the group being looked
225 up is the Domain Users SID: S-1-5-domain-513, then for the
226 list of members check if the querying user is in that group,
227 and if so only return that user as the gr_mem array.
228 We can change this to a different parameter than "enum users"
229 if neccessaey, or parameterize the group list we do this for.
230 ***********************************************************************/
232 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
233 struct winbindd_domain *domain,
234 struct winbindd_cli_state *state,
235 DOM_SID *group_sid,
236 enum lsa_SidType group_name_type,
237 size_t *num_gr_mem, char **gr_mem,
238 size_t *gr_mem_len)
240 DOM_SID querying_user_sid;
241 DOM_SID *pquerying_user_sid = NULL;
242 uint32 num_groups = 0;
243 DOM_SID *user_sids = NULL;
244 bool u_in_group = False;
245 NTSTATUS status;
246 int i;
247 unsigned int buf_len = 0;
248 char *buf = NULL;
250 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
251 domain->name ));
253 if (state) {
254 uid_t ret_uid = (uid_t)-1;
255 if (sys_getpeereid(state->sock, &ret_uid)==0) {
256 /* We know who's asking - look up their SID if
257 it's one we've mapped before. */
258 status = idmap_uid_to_sid(domain->name,
259 &querying_user_sid, ret_uid);
260 if (NT_STATUS_IS_OK(status)) {
261 pquerying_user_sid = &querying_user_sid;
262 DEBUG(10,("fill_grent_mem_domain_users: "
263 "querying uid %u -> %s\n",
264 (unsigned int)ret_uid,
265 sid_string_dbg(pquerying_user_sid)));
270 /* Only look up if it was a winbindd user in this domain. */
271 if (pquerying_user_sid &&
272 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
274 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
275 sid_string_dbg(pquerying_user_sid) ));
277 status = domain->methods->lookup_usergroups(domain,
278 mem_ctx,
279 pquerying_user_sid,
280 &num_groups,
281 &user_sids);
282 if (!NT_STATUS_IS_OK(status)) {
283 DEBUG(1, ("fill_grent_mem_domain_users: "
284 "lookup_usergroups failed "
285 "for sid %s in domain %s (error: %s)\n",
286 sid_string_dbg(pquerying_user_sid),
287 domain->name,
288 nt_errstr(status)));
289 return False;
292 for (i = 0; i < num_groups; i++) {
293 if (sid_equal(group_sid, &user_sids[i])) {
294 /* User is in Domain Users, add their name
295 as the only group member. */
296 u_in_group = True;
297 break;
302 if (u_in_group) {
303 size_t len = 0;
304 char *domainname = NULL;
305 char *username = NULL;
306 fstring name;
307 char *mapped_name = NULL;
308 enum lsa_SidType type;
309 struct winbindd_domain *target_domain = NULL;
310 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
312 DEBUG(10,("fill_grent_mem_domain_users: "
313 "sid %s in 'Domain Users' in domain %s\n",
314 sid_string_dbg(pquerying_user_sid),
315 domain->name ));
317 status = domain->methods->sid_to_name(domain, mem_ctx,
318 pquerying_user_sid,
319 &domainname,
320 &username,
321 &type);
322 if (!NT_STATUS_IS_OK(status)) {
323 DEBUG(1, ("could not lookup username for user "
324 "sid %s in domain %s (error: %s)\n",
325 sid_string_dbg(pquerying_user_sid),
326 domain->name,
327 nt_errstr(status)));
328 return False;
331 target_domain = find_domain_from_name_noinit(domainname);
332 name_map_status = normalize_name_map(mem_ctx, target_domain,
333 username, &mapped_name);
335 /* Basic whitespace replacement */
336 if (NT_STATUS_IS_OK(name_map_status)) {
337 fill_domain_username(name, domainname, mapped_name, true);
339 /* Mapped to an alias */
340 else if (NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
341 fstrcpy(name, mapped_name);
343 /* no mapping done...use original name */
344 else {
345 fill_domain_username(name, domainname, username, true);
348 len = strlen(name);
349 buf_len = len + 1;
350 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
351 DEBUG(1, ("out of memory\n"));
352 return False;
354 memcpy(buf, name, buf_len);
356 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
357 "'Domain Users' in domain %s\n",
358 name, domain->name ));
360 /* user is the only member */
361 *num_gr_mem = 1;
364 *gr_mem = buf;
365 *gr_mem_len = buf_len;
367 DEBUG(10, ("fill_grent_mem_domain_users: "
368 "num_mem = %u, len = %u, mem = %s\n",
369 (unsigned int)*num_gr_mem,
370 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
372 return True;
375 /***********************************************************************
376 Add names to a list. Assumes a canonical version of the string
377 in DOMAIN\user
378 ***********************************************************************/
380 static int namecmp( const void *a, const void *b )
382 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
385 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
386 char ***list, uint32 *n_list,
387 char **names, uint32 n_names )
389 char **new_list = NULL;
390 uint32 n_new_list = 0;
391 int i, j;
393 if ( !names || (n_names == 0) )
394 return NT_STATUS_OK;
396 /* Alloc the maximum size we'll need */
398 if ( *list == NULL ) {
399 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
400 return NT_STATUS_NO_MEMORY;
402 n_new_list = n_names;
403 } else {
404 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
405 (*n_list) + n_names );
406 if ( !new_list )
407 return NT_STATUS_NO_MEMORY;
408 n_new_list = (*n_list) + n_names;
411 /* Add all names */
413 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
414 new_list[i] = talloc_strdup( new_list, names[j] );
417 /* search for duplicates for sorting and looking for matching
418 neighbors */
420 qsort( new_list, n_new_list, sizeof(char*), QSORT_CAST namecmp );
422 for ( i=1; i<n_new_list; i++ ) {
423 if ( strcmp( new_list[i-1], new_list[i] ) == 0 ) {
424 memmove( &new_list[i-1], &new_list[i],
425 sizeof(char*)*(n_new_list-i) );
426 n_new_list--;
430 *list = new_list;
431 *n_list = n_new_list;
433 return NT_STATUS_OK;
436 /***********************************************************************
437 ***********************************************************************/
439 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
440 struct winbindd_domain *d,
441 DOM_SID *glist, uint32 n_glist,
442 DOM_SID **new_glist, uint32 *n_new_glist,
443 char ***members, uint32 *n_members )
445 int i, j;
446 NTSTATUS status = NT_STATUS_OK;
447 uint32 num_names = 0;
448 uint32 *name_types = NULL;
449 char **names = NULL;
450 DOM_SID *sid_mem = NULL;
451 TALLOC_CTX *tmp_ctx = NULL;
452 DOM_SID *new_groups = NULL;
453 size_t new_groups_size = 0;
455 *members = NULL;
456 *n_members = 0;
457 *new_glist = NULL;
458 *n_new_glist = 0;
460 for ( i=0; i<n_glist; i++ ) {
461 tmp_ctx = talloc_new( ctx );
463 /* Lookup the group membership */
465 status = d->methods->lookup_groupmem(d, tmp_ctx,
466 &glist[i], &num_names,
467 &sid_mem, &names,
468 &name_types);
469 if ( !NT_STATUS_IS_OK(status) )
470 goto out;
472 /* Separate users and groups into two lists */
474 for ( j=0; j<num_names; j++ ) {
476 /* Users */
477 if ( name_types[j] == SID_NAME_USER ||
478 name_types[j] == SID_NAME_COMPUTER )
480 status = add_names_to_list( ctx, members,
481 n_members,
482 names+j, 1 );
483 if ( !NT_STATUS_IS_OK(status) )
484 goto out;
486 continue;
489 /* Groups */
490 if ( name_types[j] == SID_NAME_DOM_GRP ||
491 name_types[j] == SID_NAME_ALIAS )
493 status = add_sid_to_array_unique(ctx,
494 &sid_mem[j],
495 &new_groups,
496 &new_groups_size);
497 if (!NT_STATUS_IS_OK(status)) {
498 goto out;
501 continue;
505 TALLOC_FREE( tmp_ctx );
508 *new_glist = new_groups;
509 *n_new_glist = (uint32)new_groups_size;
511 out:
512 TALLOC_FREE( tmp_ctx );
514 return status;
517 /***********************************************************************
518 Fill in the group membership field of a NT group given by group_sid
519 ***********************************************************************/
521 static bool fill_grent_mem(struct winbindd_domain *domain,
522 struct winbindd_cli_state *state,
523 DOM_SID *group_sid,
524 enum lsa_SidType group_name_type,
525 size_t *num_gr_mem, char **gr_mem,
526 size_t *gr_mem_len)
528 uint32 num_names = 0;
529 unsigned int buf_len = 0, buf_ndx = 0, i;
530 char **names = NULL, *buf = NULL;
531 bool result = False;
532 TALLOC_CTX *mem_ctx;
533 uint32 group_rid;
534 DOM_SID *glist = NULL;
535 DOM_SID *new_glist = NULL;
536 uint32 n_glist, n_new_glist;
537 int max_depth = lp_winbind_expand_groups();
539 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
540 return False;
542 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
544 /* Initialize with no members */
546 *num_gr_mem = 0;
548 /* HACK ALERT!! This whole routine does not cope with group members
549 * from more than one domain, ie aliases. Thus we have to work it out
550 * ourselves in a special routine. */
552 if (domain->internal) {
553 result = fill_passdb_alias_grmem(domain, group_sid,
554 num_gr_mem,
555 gr_mem, gr_mem_len);
556 goto done;
559 /* Verify name type */
561 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
562 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
564 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
565 sid_string_dbg(group_sid),
566 domain->name, group_name_type));
567 goto done;
570 /* OPTIMIZATION / HACK. See comment in
571 fill_grent_mem_domusers() */
573 sid_peek_rid( group_sid, &group_rid );
574 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
575 result = fill_grent_mem_domusers( mem_ctx, domain, state,
576 group_sid, group_name_type,
577 num_gr_mem, gr_mem,
578 gr_mem_len );
579 goto done;
582 /* Real work goes here. Create a list of group names to
583 expand startign with the initial one. Pass that to
584 expand_groups() which returns a list of more group names
585 to expand. Do this up to the max search depth. */
587 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
588 result = False;
589 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
590 goto done;
592 sid_copy( &glist[0], group_sid );
593 n_glist = 1;
595 for ( i=0; i<max_depth && glist; i++ ) {
596 uint32 n_members = 0;
597 char **members = NULL;
598 NTSTATUS nt_status;
599 int j;
601 nt_status = expand_groups( mem_ctx, domain,
602 glist, n_glist,
603 &new_glist, &n_new_glist,
604 &members, &n_members);
605 if ( !NT_STATUS_IS_OK(nt_status) ) {
606 result = False;
607 goto done;
610 /* Add new group members to list. Pass through the
611 alias mapping function */
613 for (j=0; j<n_members; j++) {
614 fstring name_domain, name_acct;
615 fstring qualified_name;
616 char *mapped_name = NULL;
617 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
618 struct winbindd_domain *target_domain = NULL;
620 if (parse_domain_user(members[j], name_domain, name_acct)) {
621 target_domain = find_domain_from_name_noinit(name_domain);
622 /* NOW WHAT ? */
624 if (!target_domain) {
625 target_domain = domain;
628 name_map_status = normalize_name_map(members, target_domain,
629 name_acct, &mapped_name);
631 /* Basic whitespace replacement */
632 if (NT_STATUS_IS_OK(name_map_status)) {
633 fill_domain_username(qualified_name, name_domain,
634 mapped_name, true);
635 mapped_name = qualified_name;
637 /* no mapping at all */
638 else if (!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
639 mapped_name = members[j];
642 nt_status = add_names_to_list( mem_ctx, &names,
643 &num_names,
644 &mapped_name, 1);
645 if ( !NT_STATUS_IS_OK(nt_status) ) {
646 result = False;
647 goto done;
651 TALLOC_FREE( members );
653 /* If we have no more groups to expand, break out
654 early */
656 if (new_glist == NULL)
657 break;
659 /* One more round */
660 TALLOC_FREE(glist);
661 glist = new_glist;
662 n_glist = n_new_glist;
664 TALLOC_FREE( glist );
666 DEBUG(10, ("looked up %d names\n", num_names));
668 again:
669 /* Add members to list */
671 for (i = 0; i < num_names; i++) {
672 int len;
674 DEBUG(10, ("processing name %s\n", names[i]));
676 len = strlen(names[i]);
678 /* Add to list or calculate buffer length */
680 if (!buf) {
681 buf_len += len + 1; /* List is comma separated */
682 (*num_gr_mem)++;
683 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
684 } else {
685 DEBUG(10, ("appending %s at ndx %d\n",
686 names[i], buf_ndx));
687 parse_add_domuser(&buf[buf_ndx], names[i], &len);
688 buf_ndx += len;
689 buf[buf_ndx] = ',';
690 buf_ndx++;
694 /* Allocate buffer */
696 if (!buf && buf_len != 0) {
697 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
698 DEBUG(1, ("out of memory\n"));
699 result = False;
700 goto done;
702 memset(buf, 0, buf_len);
703 goto again;
706 /* Now we're done */
708 if (buf && buf_ndx > 0) {
709 buf[buf_ndx - 1] = '\0';
712 *gr_mem = buf;
713 *gr_mem_len = buf_len;
715 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
716 (unsigned int)*num_gr_mem,
717 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
718 result = True;
720 done:
722 talloc_destroy(mem_ctx);
724 DEBUG(10, ("fill_grent_mem returning %d\n", result));
726 return result;
729 static void winbindd_getgrsid(struct winbindd_cli_state *state, DOM_SID group_sid);
731 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
732 enum lsa_SidType type )
734 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
736 if (!success) {
737 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
738 request_error(state);
739 return;
742 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
743 DEBUG(5,("getgrnam_recv: not a group!\n"));
744 request_error(state);
745 return;
748 winbindd_getgrsid( state, *sid );
752 /* Return a group structure from a group name */
754 void winbindd_getgrnam(struct winbindd_cli_state *state)
756 struct winbindd_domain *domain;
757 fstring name_domain, name_group;
758 char *tmp;
759 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
761 /* Ensure null termination */
762 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
764 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
765 state->request.data.groupname));
767 nt_status = normalize_name_unmap(state->mem_ctx,
768 state->request.data.groupname,
769 &tmp);
770 /* If we didn't map anything in the above call, just reset the
771 tmp pointer to the original string */
772 if (!NT_STATUS_IS_OK(nt_status) &&
773 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
775 tmp = state->request.data.groupname;
778 /* Parse domain and groupname */
780 memset(name_group, 0, sizeof(name_group));
782 name_domain[0] = '\0';
783 name_group[0] = '\0';
785 parse_domain_user(tmp, name_domain, name_group);
787 /* if no domain or our local domain and no local tdb group, default to
788 * our local domain for aliases */
790 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
791 fstrcpy(name_domain, get_global_sam_name());
794 /* Get info for the domain */
796 if ((domain = find_domain_from_name(name_domain)) == NULL) {
797 DEBUG(3, ("could not get domain sid for domain %s\n",
798 name_domain));
799 request_error(state);
800 return;
802 /* should we deal with users for our domain? */
804 if ( lp_winbind_trusted_domains_only() && domain->primary) {
805 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
806 "getgrnam() for %s\\%s.\n", name_domain, name_group));
807 request_error(state);
808 return;
811 /* Get rid and name type from name */
813 fstrcpy( name_group, tmp );
815 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
816 getgrnam_recv, WINBINDD_GETGRNAM, state );
819 struct getgrsid_state {
820 struct winbindd_cli_state *state;
821 struct winbindd_domain *domain;
822 char *group_name;
823 enum lsa_SidType group_type;
824 uid_t gid;
825 DOM_SID group_sid;
828 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
830 struct getgrsid_state *s =
831 (struct getgrsid_state *)private_data;
832 struct winbindd_domain *domain;
833 size_t gr_mem_len;
834 size_t num_gr_mem;
835 char *gr_mem;
836 fstring dom_name, group_name;
838 if (!success) {
839 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
840 request_error(s->state);
841 return;
844 s->gid = gid;
846 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
847 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
848 request_error(s->state);
849 return;
853 /* Fill in group structure */
855 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
856 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
857 request_error(s->state);
858 return;
861 if (!fill_grent(s->state->mem_ctx, &s->state->response.data.gr,
862 dom_name, group_name, gid) ||
863 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
864 &num_gr_mem, &gr_mem, &gr_mem_len))
866 request_error(s->state);
867 return;
870 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
872 /* Group membership lives at start of extra data */
874 s->state->response.data.gr.gr_mem_ofs = 0;
876 s->state->response.length += gr_mem_len;
877 s->state->response.extra_data.data = gr_mem;
879 request_ok(s->state);
882 static void getgrsid_lookupsid_recv( void *private_data, bool success,
883 const char *dom_name, const char *name,
884 enum lsa_SidType name_type )
886 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
887 char *mapped_name = NULL;
888 fstring raw_name;
889 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
891 if (!success) {
892 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
893 request_error(s->state);
894 return;
897 /* either it's a domain group, a domain local group, or a
898 local group in an internal domain */
900 if ( !( (name_type==SID_NAME_DOM_GRP) ||
901 ((name_type==SID_NAME_ALIAS) &&
902 (s->domain->primary || s->domain->internal)) ) )
904 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
905 dom_name, name, name_type));
906 request_error(s->state);
907 return;
910 /* normalize the name and ensure that we have the DOM\name
911 coming out of here */
913 fstrcpy(raw_name, name);
915 nt_status = normalize_name_unmap(s->state->mem_ctx, raw_name,
916 &mapped_name);
918 /* basiuc whitespace reversal */
919 if (NT_STATUS_IS_OK(nt_status)) {
920 s->group_name = talloc_asprintf(s->state->mem_ctx,
921 "%s%c%s",
922 dom_name,
923 *lp_winbind_separator(),
924 mapped_name);
926 /* mapped from alias */
927 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
928 s->group_name = mapped_name;
930 /* no mapping at all. use original string */
931 else {
932 s->group_name = talloc_asprintf(s->state->mem_ctx,
933 "%s%c%s",
934 dom_name,
935 *lp_winbind_separator(),
936 raw_name);
939 if (s->group_name == NULL) {
940 DEBUG(1, ("getgrsid_lookupsid_recv: group_name is NULL!\n"));
941 request_error(s->state);
942 return;
945 s->group_type = name_type;
947 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
948 getgrsid_sid2gid_recv, s);
951 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
953 struct getgrsid_state *s;
955 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
956 DEBUG(0, ("talloc failed\n"));
957 request_error(state);
958 return;
961 s->state = state;
963 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
964 DEBUG(3, ("Could not find domain for sid %s\n",
965 sid_string_dbg(&group_sid)));
966 request_error(state);
967 return;
970 sid_copy(&s->group_sid, &group_sid);
972 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
973 getgrsid_lookupsid_recv, s );
977 static void getgrgid_recv(void *private_data, bool success, const char *sid)
979 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
980 enum lsa_SidType name_type;
981 DOM_SID group_sid;
983 if (success) {
984 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
985 (unsigned long)(state->request.data.gid), sid));
987 string_to_sid(&group_sid, sid);
988 winbindd_getgrsid(state, group_sid);
989 return;
992 /* Ok, this might be "ours", i.e. an alias */
993 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
994 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
995 (name_type == SID_NAME_ALIAS)) {
996 /* Hey, got an alias */
997 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
998 (unsigned long)(state->request.data.gid), sid));
999 winbindd_getgrsid(state, group_sid);
1000 return;
1003 DEBUG(1, ("could not convert gid %lu to sid\n",
1004 (unsigned long)state->request.data.gid));
1005 request_error(state);
1008 /* Return a group structure from a gid number */
1009 void winbindd_getgrgid(struct winbindd_cli_state *state)
1011 gid_t gid = state->request.data.gid;
1013 DEBUG(3, ("[%5lu]: getgrgid %lu\n",
1014 (unsigned long)state->pid,
1015 (unsigned long)gid));
1017 /* always use the async interface */
1018 winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
1022 * set/get/endgrent functions
1025 /* "Rewind" file pointer for group database enumeration */
1027 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
1029 struct winbindd_domain *domain;
1031 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
1033 /* Check user has enabled this */
1035 if (!lp_winbind_enum_groups()) {
1036 return False;
1039 /* Free old static data if it exists */
1041 if (state->getgrent_state != NULL) {
1042 free_getent_state(state->getgrent_state);
1043 state->getgrent_state = NULL;
1046 /* Create sam pipes for each domain we know about */
1048 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1049 struct getent_state *domain_state;
1051 /* Create a state record for this domain */
1053 /* don't add our domaina if we are a PDC or if we
1054 are a member of a Samba domain */
1056 if ( lp_winbind_trusted_domains_only() && domain->primary )
1058 continue;
1061 domain_state = SMB_MALLOC_P(struct getent_state);
1062 if (!domain_state) {
1063 DEBUG(1, ("winbindd_setgrent: "
1064 "malloc failed for domain_state!\n"));
1065 return False;
1068 ZERO_STRUCTP(domain_state);
1070 fstrcpy(domain_state->domain_name, domain->name);
1072 /* Add to list of open domains */
1074 DLIST_ADD(state->getgrent_state, domain_state);
1077 state->getgrent_initialized = True;
1078 return True;
1081 void winbindd_setgrent(struct winbindd_cli_state *state)
1083 if (winbindd_setgrent_internal(state)) {
1084 request_ok(state);
1085 } else {
1086 request_error(state);
1090 /* Close file pointer to ntdom group database */
1092 void winbindd_endgrent(struct winbindd_cli_state *state)
1094 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
1096 free_getent_state(state->getgrent_state);
1097 state->getgrent_initialized = False;
1098 state->getgrent_state = NULL;
1099 request_ok(state);
1102 /* Get the list of domain groups and domain aliases for a domain. We fill in
1103 the sam_entries and num_sam_entries fields with domain group information.
1104 Return True if some groups were returned, False otherwise. */
1106 bool get_sam_group_entries(struct getent_state *ent)
1108 NTSTATUS status;
1109 uint32 num_entries;
1110 struct acct_info *name_list = NULL;
1111 TALLOC_CTX *mem_ctx;
1112 bool result = False;
1113 struct acct_info *sam_grp_entries = NULL;
1114 struct winbindd_domain *domain;
1116 if (ent->got_sam_entries)
1117 return False;
1119 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1120 ent->domain_name))) {
1121 DEBUG(1, ("get_sam_group_entries: "
1122 "could not create talloc context!\n"));
1123 return False;
1126 /* Free any existing group info */
1128 SAFE_FREE(ent->sam_entries);
1129 ent->num_sam_entries = 0;
1130 ent->got_sam_entries = True;
1132 /* Enumerate domain groups */
1134 num_entries = 0;
1136 if (!(domain = find_domain_from_name(ent->domain_name))) {
1137 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1138 ent->domain_name));
1139 goto done;
1142 /* always get the domain global groups */
1144 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1145 &sam_grp_entries);
1147 if (!NT_STATUS_IS_OK(status)) {
1148 DEBUG(3, ("get_sam_group_entries: "
1149 "could not enumerate domain groups! Error: %s\n",
1150 nt_errstr(status)));
1151 result = False;
1152 goto done;
1155 /* Copy entries into return buffer */
1157 if (num_entries) {
1158 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1159 if (!name_list) {
1160 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1161 "memory for %d domain groups!\n",
1162 num_entries));
1163 result = False;
1164 goto done;
1166 memcpy(name_list, sam_grp_entries,
1167 num_entries * sizeof(struct acct_info));
1170 ent->num_sam_entries = num_entries;
1172 /* get the domain local groups if we are a member of a native win2k
1173 * domain and are not using LDAP to get the groups */
1175 if ( ( lp_security() != SEC_ADS && domain->native_mode
1176 && domain->primary) || domain->internal )
1178 DEBUG(4,("get_sam_group_entries: %s domain; "
1179 "enumerating local groups as well\n",
1180 domain->native_mode ? "Native Mode 2k":
1181 "BUILTIN or local"));
1183 status = domain->methods->enum_local_groups(domain, mem_ctx,
1184 &num_entries,
1185 &sam_grp_entries);
1187 if ( !NT_STATUS_IS_OK(status) ) {
1188 DEBUG(3,("get_sam_group_entries: "
1189 "Failed to enumerate "
1190 "domain local groups with error %s!\n",
1191 nt_errstr(status)));
1192 num_entries = 0;
1194 else
1195 DEBUG(4,("get_sam_group_entries: "
1196 "Returned %d local groups\n",
1197 num_entries));
1199 /* Copy entries into return buffer */
1201 if ( num_entries ) {
1202 name_list = SMB_REALLOC_ARRAY(name_list,
1203 struct acct_info,
1204 ent->num_sam_entries+
1205 num_entries);
1206 if (!name_list) {
1207 DEBUG(0,("get_sam_group_entries: "
1208 "Failed to realloc more memory "
1209 "for %d local groups!\n",
1210 num_entries));
1211 result = False;
1212 goto done;
1215 memcpy(&name_list[ent->num_sam_entries],
1216 sam_grp_entries,
1217 num_entries * sizeof(struct acct_info));
1220 ent->num_sam_entries += num_entries;
1224 /* Fill in remaining fields */
1226 ent->sam_entries = name_list;
1227 ent->sam_entry_index = 0;
1229 result = (ent->num_sam_entries > 0);
1231 done:
1232 talloc_destroy(mem_ctx);
1234 return result;
1237 /* Fetch next group entry from ntdom database */
1239 #define MAX_GETGRENT_GROUPS 500
1241 void winbindd_getgrent(struct winbindd_cli_state *state)
1243 struct getent_state *ent;
1244 struct winbindd_gr *group_list = NULL;
1245 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1246 char *gr_mem_list = NULL;
1248 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1250 /* Check user has enabled this */
1252 if (!lp_winbind_enum_groups()) {
1253 request_error(state);
1254 return;
1257 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1259 if (num_groups == 0) {
1260 request_error(state);
1261 return;
1264 group_list = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups);
1265 if (!group_list) {
1266 request_error(state);
1267 return;
1269 /* will be freed by process_request() */
1270 state->response.extra_data.data = group_list;
1272 memset(state->response.extra_data.data, '\0',
1273 num_groups * sizeof(struct winbindd_gr) );
1275 state->response.data.num_entries = 0;
1277 if (!state->getgrent_initialized)
1278 winbindd_setgrent_internal(state);
1280 if (!(ent = state->getgrent_state)) {
1281 request_error(state);
1282 return;
1285 /* Start sending back groups */
1287 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1288 struct acct_info *name_list = NULL;
1289 fstring domain_group_name;
1290 uint32 result;
1291 gid_t group_gid;
1292 size_t gr_mem_len;
1293 char *gr_mem;
1294 DOM_SID group_sid;
1295 struct winbindd_domain *domain;
1297 /* Do we need to fetch another chunk of groups? */
1299 tryagain:
1301 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1302 ent->sam_entry_index, ent->num_sam_entries));
1304 if (ent->num_sam_entries == ent->sam_entry_index) {
1306 while(ent && !get_sam_group_entries(ent)) {
1307 struct getent_state *next_ent;
1309 DEBUG(10, ("freeing state info for domain %s\n",
1310 ent->domain_name));
1312 /* Free state information for this domain */
1314 SAFE_FREE(ent->sam_entries);
1316 next_ent = ent->next;
1317 DLIST_REMOVE(state->getgrent_state, ent);
1319 SAFE_FREE(ent);
1320 ent = next_ent;
1323 /* No more domains */
1325 if (!ent)
1326 break;
1329 name_list = (struct acct_info *)ent->sam_entries;
1331 if (!(domain = find_domain_from_name(ent->domain_name))) {
1332 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1333 ent->domain_name));
1334 result = False;
1335 goto done;
1338 /* Lookup group info */
1340 sid_copy(&group_sid, &domain->sid);
1341 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1343 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->name, &group_sid,
1344 &group_gid))) {
1345 union unid_t id;
1346 enum lsa_SidType type;
1348 DEBUG(10, ("SID %s not in idmap\n",
1349 sid_string_dbg(&group_sid)));
1351 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1352 DEBUG(1,("could not look up gid for group %s\n",
1353 name_list[ent->sam_entry_index].acct_name));
1354 ent->sam_entry_index++;
1355 goto tryagain;
1358 if ((type != SID_NAME_DOM_GRP) &&
1359 (type != SID_NAME_ALIAS) &&
1360 (type != SID_NAME_WKN_GRP)) {
1361 DEBUG(1, ("Group %s is a %s, not a group\n",
1362 sid_type_lookup(type),
1363 name_list[ent->sam_entry_index].acct_name));
1364 ent->sam_entry_index++;
1365 goto tryagain;
1367 group_gid = id.gid;
1370 DEBUG(10, ("got gid %lu for group %lu\n",
1371 (unsigned long)group_gid,
1372 (unsigned long)name_list[ent->sam_entry_index].rid));
1374 /* Fill in group entry */
1376 fill_domain_username(domain_group_name, ent->domain_name,
1377 name_list[ent->sam_entry_index].acct_name, True);
1379 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1380 ent->domain_name,
1381 name_list[ent->sam_entry_index].acct_name,
1382 group_gid);
1384 /* Fill in group membership entry */
1386 if (result) {
1387 size_t num_gr_mem = 0;
1388 DOM_SID member_sid;
1389 group_list[group_list_ndx].num_gr_mem = 0;
1390 gr_mem = NULL;
1391 gr_mem_len = 0;
1393 /* Get group membership */
1394 if (state->request.cmd == WINBINDD_GETGRLST) {
1395 result = True;
1396 } else {
1397 sid_copy(&member_sid, &domain->sid);
1398 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1399 result = fill_grent_mem(
1400 domain,
1401 NULL,
1402 &member_sid,
1403 SID_NAME_DOM_GRP,
1404 &num_gr_mem,
1405 &gr_mem, &gr_mem_len);
1407 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1411 if (result) {
1412 /* Append to group membership list */
1413 gr_mem_list = (char *)SMB_REALLOC(
1414 gr_mem_list, gr_mem_list_len + gr_mem_len);
1416 if (!gr_mem_list &&
1417 (group_list[group_list_ndx].num_gr_mem != 0)) {
1418 DEBUG(0, ("out of memory\n"));
1419 gr_mem_list_len = 0;
1420 break;
1423 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1424 gr_mem_list_len, (unsigned int)gr_mem_len));
1426 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1427 gr_mem_len);
1429 SAFE_FREE(gr_mem);
1431 group_list[group_list_ndx].gr_mem_ofs =
1432 gr_mem_list_len;
1434 gr_mem_list_len += gr_mem_len;
1437 ent->sam_entry_index++;
1439 /* Add group to return list */
1441 if (result) {
1443 DEBUG(10, ("adding group num_entries = %d\n",
1444 state->response.data.num_entries));
1446 group_list_ndx++;
1447 state->response.data.num_entries++;
1449 state->response.length +=
1450 sizeof(struct winbindd_gr);
1452 } else {
1453 DEBUG(0, ("could not lookup domain group %s\n",
1454 domain_group_name));
1458 /* Copy the list of group memberships to the end of the extra data */
1460 if (group_list_ndx == 0)
1461 goto done;
1463 state->response.extra_data.data = SMB_REALLOC(
1464 state->response.extra_data.data,
1465 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1467 if (!state->response.extra_data.data) {
1468 DEBUG(0, ("out of memory\n"));
1469 group_list_ndx = 0;
1470 SAFE_FREE(gr_mem_list);
1471 request_error(state);
1472 return;
1475 memcpy(&((char *)state->response.extra_data.data)
1476 [group_list_ndx * sizeof(struct winbindd_gr)],
1477 gr_mem_list, gr_mem_list_len);
1479 state->response.length += gr_mem_list_len;
1481 DEBUG(10, ("returning %d groups, length = %d\n",
1482 group_list_ndx, gr_mem_list_len));
1484 /* Out of domains */
1486 done:
1488 SAFE_FREE(gr_mem_list);
1490 if (group_list_ndx > 0)
1491 request_ok(state);
1492 else
1493 request_error(state);
1496 /* List domain groups without mapping to unix ids */
1497 void winbindd_list_groups(struct winbindd_cli_state *state)
1499 winbindd_list_ent(state, LIST_GROUPS);
1502 /* Get user supplementary groups. This is much quicker than trying to
1503 invert the groups database. We merge the groups from the gids and
1504 other_sids info3 fields as trusted domain, universal group
1505 memberships, and nested groups (win2k native mode only) are not
1506 returned by the getgroups RPC call but are present in the info3. */
1508 struct getgroups_state {
1509 struct winbindd_cli_state *state;
1510 struct winbindd_domain *domain;
1511 char *domname;
1512 char *username;
1513 DOM_SID user_sid;
1515 const DOM_SID *token_sids;
1516 size_t i, num_token_sids;
1518 gid_t *token_gids;
1519 size_t num_token_gids;
1522 static void getgroups_usersid_recv(void *private_data, bool success,
1523 const DOM_SID *sid, enum lsa_SidType type);
1524 static void getgroups_tokensids_recv(void *private_data, bool success,
1525 DOM_SID *token_sids, size_t num_token_sids);
1526 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1528 void winbindd_getgroups(struct winbindd_cli_state *state)
1530 struct getgroups_state *s;
1531 char *real_name = NULL;
1532 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1534 /* Ensure null termination */
1535 state->request.data.username
1536 [sizeof(state->request.data.username)-1]='\0';
1538 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1539 state->request.data.username));
1541 /* Parse domain and username */
1543 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1544 if (s == NULL) {
1545 DEBUG(0, ("talloc failed\n"));
1546 request_error(state);
1547 return;
1550 s->state = state;
1552 nt_status = normalize_name_unmap(state->mem_ctx,
1553 state->request.data.username,
1554 &real_name);
1556 /* Reset the real_name pointer if we didn't do anything
1557 productive in the above call */
1558 if (!NT_STATUS_IS_OK(nt_status) &&
1559 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
1561 real_name = state->request.data.username;
1564 if (!parse_domain_user_talloc(state->mem_ctx, real_name,
1565 &s->domname, &s->username)) {
1566 DEBUG(5, ("Could not parse domain user: %s\n",
1567 real_name));
1569 /* error out if we do not have nested group support */
1571 if ( !lp_winbind_nested_groups() ) {
1572 request_error(state);
1573 return;
1576 s->domname = talloc_strdup(state->mem_ctx,
1577 get_global_sam_name());
1578 s->username = talloc_strdup(state->mem_ctx,
1579 state->request.data.username);
1582 /* Get info for the domain (either by short domain name or
1583 DNS name in the case of a UPN) */
1585 s->domain = find_domain_from_name_noinit(s->domname);
1586 if (!s->domain) {
1587 char *p = strchr(s->username, '@');
1589 if (p) {
1590 s->domain = find_domain_from_name_noinit(p+1);
1595 if (s->domain == NULL) {
1596 DEBUG(7, ("could not find domain entry for domain %s\n",
1597 s->domname));
1598 request_error(state);
1599 return;
1602 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1603 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1604 "getgroups() for %s\\%s.\n", s->domname,
1605 s->username));
1606 request_error(state);
1607 return;
1610 /* Get rid and name type from name. The following costs 1 packet */
1612 winbindd_lookupname_async(state->mem_ctx,
1613 s->domname, s->username,
1614 getgroups_usersid_recv,
1615 WINBINDD_GETGROUPS, s);
1618 static void getgroups_usersid_recv(void *private_data, bool success,
1619 const DOM_SID *sid, enum lsa_SidType type)
1621 struct getgroups_state *s =
1622 (struct getgroups_state *)private_data;
1624 if ((!success) ||
1625 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1626 request_error(s->state);
1627 return;
1630 sid_copy(&s->user_sid, sid);
1632 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1633 getgroups_tokensids_recv, s);
1636 static void getgroups_tokensids_recv(void *private_data, bool success,
1637 DOM_SID *token_sids, size_t num_token_sids)
1639 struct getgroups_state *s =
1640 (struct getgroups_state *)private_data;
1642 /* We need at least the user sid and the primary group in the token,
1643 * otherwise it's an error */
1645 if ((!success) || (num_token_sids < 2)) {
1646 request_error(s->state);
1647 return;
1650 s->token_sids = token_sids;
1651 s->num_token_sids = num_token_sids;
1652 s->i = 0;
1654 s->token_gids = NULL;
1655 s->num_token_gids = 0;
1657 getgroups_sid2gid_recv(s, False, 0);
1660 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1662 struct getgroups_state *s =
1663 (struct getgroups_state *)private_data;
1665 if (success) {
1666 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1667 &s->token_gids,
1668 &s->num_token_gids)) {
1669 return;
1673 if (s->i < s->num_token_sids) {
1674 const DOM_SID *sid = &s->token_sids[s->i];
1675 s->i += 1;
1677 if (sid_equal(sid, &s->user_sid)) {
1678 getgroups_sid2gid_recv(s, False, 0);
1679 return;
1682 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1683 getgroups_sid2gid_recv, s);
1684 return;
1687 s->state->response.data.num_entries = s->num_token_gids;
1688 if (s->num_token_gids) {
1689 /* s->token_gids are talloced */
1690 s->state->response.extra_data.data =
1691 smb_xmemdup(s->token_gids,
1692 s->num_token_gids * sizeof(gid_t));
1693 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1695 request_ok(s->state);
1698 /* Get user supplementary sids. This is equivalent to the
1699 winbindd_getgroups() function but it involves a SID->SIDs mapping
1700 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1701 idmap. This call is designed to be used with applications that need
1702 to do ACL evaluation themselves. Note that the cached info3 data is
1703 not used
1705 this function assumes that the SID that comes in is a user SID. If
1706 you pass in another type of SID then you may get unpredictable
1707 results.
1710 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1711 size_t num_sids);
1713 void winbindd_getusersids(struct winbindd_cli_state *state)
1715 DOM_SID *user_sid;
1717 /* Ensure null termination */
1718 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1720 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1721 if (user_sid == NULL) {
1722 DEBUG(1, ("talloc failed\n"));
1723 request_error(state);
1724 return;
1727 if (!string_to_sid(user_sid, state->request.data.sid)) {
1728 DEBUG(1, ("Could not get convert sid %s from string\n",
1729 state->request.data.sid));
1730 request_error(state);
1731 return;
1734 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1735 state);
1738 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1739 size_t num_sids)
1741 struct winbindd_cli_state *state =
1742 (struct winbindd_cli_state *)private_data;
1743 char *ret = NULL;
1744 unsigned ofs, ret_size = 0;
1745 size_t i;
1747 if (!success) {
1748 request_error(state);
1749 return;
1752 /* work out the response size */
1753 for (i = 0; i < num_sids; i++) {
1754 fstring s;
1755 sid_to_fstring(s, &sids[i]);
1756 ret_size += strlen(s) + 1;
1759 /* build the reply */
1760 ret = (char *)SMB_MALLOC(ret_size);
1761 if (!ret) {
1762 DEBUG(0, ("malloc failed\n"));
1763 request_error(state);
1764 return;
1766 ofs = 0;
1767 for (i = 0; i < num_sids; i++) {
1768 fstring s;
1769 sid_to_fstring(s, &sids[i]);
1770 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1771 ofs += strlen(ret+ofs) + 1;
1774 /* Send data back to client */
1775 state->response.data.num_entries = num_sids;
1776 state->response.extra_data.data = ret;
1777 state->response.length += ret_size;
1778 request_ok(state);
1781 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1783 DOM_SID user_sid;
1784 struct winbindd_domain *domain;
1786 /* Ensure null termination */
1787 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1789 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1790 DEBUG(1, ("Could not get convert sid %s from string\n",
1791 state->request.data.sid));
1792 request_error(state);
1793 return;
1796 /* Get info for the domain */
1797 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1798 DEBUG(0,("could not find domain entry for sid %s\n",
1799 sid_string_dbg(&user_sid)));
1800 request_error(state);
1801 return;
1804 sendto_domain(state, domain);
1807 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1808 struct winbindd_cli_state *state)
1810 DOM_SID user_sid;
1811 NTSTATUS status;
1813 char *sidstring;
1814 ssize_t len;
1815 DOM_SID *groups;
1816 uint32 num_groups;
1818 /* Ensure null termination */
1819 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1821 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1822 DEBUG(1, ("Could not get convert sid %s from string\n",
1823 state->request.data.sid));
1824 return WINBINDD_ERROR;
1827 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1828 &user_sid, &num_groups,
1829 &groups);
1830 if (!NT_STATUS_IS_OK(status))
1831 return WINBINDD_ERROR;
1833 if (num_groups == 0) {
1834 state->response.data.num_entries = 0;
1835 state->response.extra_data.data = NULL;
1836 return WINBINDD_OK;
1839 if (!print_sidlist(state->mem_ctx,
1840 groups, num_groups,
1841 &sidstring, &len)) {
1842 DEBUG(0, ("talloc failed\n"));
1843 return WINBINDD_ERROR;
1846 state->response.extra_data.data = SMB_STRDUP(sidstring);
1847 if (!state->response.extra_data.data) {
1848 return WINBINDD_ERROR;
1850 state->response.length += len+1;
1851 state->response.data.num_entries = num_groups;
1853 return WINBINDD_OK;