Fix bug #7669.
[Samba.git] / source / winbindd / winbindd_group.c
bloba4b118c39a5e8264cb49d5af670db928c4f5ecbb
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 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
31 static void add_member(const char *domain, const char *user,
32 char **pp_members, size_t *p_num_members)
34 fstring name;
36 if (domain != NULL) {
37 fill_domain_username(name, domain, user, True);
38 } else {
39 fstrcpy(name, user);
41 safe_strcat(name, ",", sizeof(name)-1);
42 string_append(pp_members, name);
43 *p_num_members += 1;
46 /**********************************************************************
47 Add member users resulting from sid. Expand if it is a domain group.
48 **********************************************************************/
50 static void add_expanded_sid(const DOM_SID *sid,
51 char **pp_members,
52 size_t *p_num_members)
54 DOM_SID dom_sid;
55 uint32 rid;
56 struct winbindd_domain *domain;
57 size_t i;
59 char *domain_name = NULL;
60 char *name = NULL;
61 enum lsa_SidType type;
63 uint32 num_names;
64 DOM_SID *sid_mem;
65 char **names;
66 uint32 *types;
68 NTSTATUS result;
70 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
72 if (mem_ctx == NULL) {
73 DEBUG(1, ("talloc_init failed\n"));
74 return;
77 sid_copy(&dom_sid, sid);
78 sid_split_rid(&dom_sid, &rid);
80 domain = find_lookup_domain_from_sid(sid);
82 if (domain == NULL) {
83 DEBUG(3, ("Could not find domain for sid %s\n",
84 sid_string_dbg(sid)));
85 goto done;
88 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
89 &domain_name, &name, &type);
91 if (!NT_STATUS_IS_OK(result)) {
92 DEBUG(3, ("sid_to_name failed for sid %s\n",
93 sid_string_dbg(sid)));
94 goto done;
97 DEBUG(10, ("Found name %s, type %d\n", name, type));
99 if (type == SID_NAME_USER) {
100 add_member(domain_name, name, pp_members, p_num_members);
101 goto done;
104 if (type != SID_NAME_DOM_GRP) {
105 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
106 name));
107 goto done;
110 /* Expand the domain group, this must be done via the target domain */
112 domain = find_domain_from_sid(sid);
114 if (domain == NULL) {
115 DEBUG(3, ("Could not find domain from SID %s\n",
116 sid_string_dbg(sid)));
117 goto done;
120 result = domain->methods->lookup_groupmem(domain, mem_ctx,
121 sid, &num_names,
122 &sid_mem, &names,
123 &types);
125 if (!NT_STATUS_IS_OK(result)) {
126 DEBUG(10, ("Could not lookup group members for %s: %s\n",
127 name, nt_errstr(result)));
128 goto done;
131 for (i=0; i<num_names; i++) {
132 DEBUG(10, ("Adding group member SID %s\n",
133 sid_string_dbg(&sid_mem[i])));
135 if (types[i] != SID_NAME_USER) {
136 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
137 "Ignoring.\n", names[i], name));
138 continue;
141 add_member(NULL, names[i], pp_members, p_num_members);
144 done:
145 talloc_destroy(mem_ctx);
146 return;
149 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
150 DOM_SID *group_sid, size_t *num_gr_mem,
151 char **gr_mem, size_t *gr_mem_len)
153 DOM_SID *members;
154 size_t i, num_members;
156 *num_gr_mem = 0;
157 *gr_mem = NULL;
158 *gr_mem_len = 0;
160 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
161 &num_members)))
162 return True;
164 for (i=0; i<num_members; i++) {
165 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
168 TALLOC_FREE(members);
170 if (*gr_mem != NULL) {
171 size_t len;
173 /* We have at least one member, strip off the last "," */
174 len = strlen(*gr_mem);
175 (*gr_mem)[len-1] = '\0';
176 *gr_mem_len = len;
179 return True;
182 /* Fill a grent structure from various other information */
184 static bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
185 const char *dom_name,
186 char *gr_name, gid_t unix_gid)
188 fstring full_group_name;
189 char *mapped_name = NULL;
190 struct winbindd_domain *domain = find_domain_from_name_noinit(dom_name);
191 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
193 nt_status = normalize_name_map(mem_ctx, domain, gr_name,
194 &mapped_name);
196 /* Basic whitespace replacement */
197 if (NT_STATUS_IS_OK(nt_status)) {
198 fill_domain_username(full_group_name, dom_name,
199 mapped_name, true);
201 /* Mapped to an aliase */
202 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
203 fstrcpy(full_group_name, mapped_name);
205 /* no change */
206 else {
207 fill_domain_username( full_group_name, dom_name,
208 gr_name, True );
211 gr->gr_gid = unix_gid;
213 /* Group name and password */
215 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
216 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
218 return True;
221 /***********************************************************************
222 If "enum users" is set to false, and the group being looked
223 up is the Domain Users SID: S-1-5-domain-513, then for the
224 list of members check if the querying user is in that group,
225 and if so only return that user as the gr_mem array.
226 We can change this to a different parameter than "enum users"
227 if neccessaey, or parameterize the group list we do this for.
228 ***********************************************************************/
230 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
231 struct winbindd_domain *domain,
232 struct winbindd_cli_state *state,
233 DOM_SID *group_sid,
234 enum lsa_SidType group_name_type,
235 size_t *num_gr_mem, char **gr_mem,
236 size_t *gr_mem_len)
238 DOM_SID querying_user_sid;
239 DOM_SID *pquerying_user_sid = NULL;
240 uint32 num_groups = 0;
241 DOM_SID *user_sids = NULL;
242 bool u_in_group = False;
243 NTSTATUS status;
244 int i;
245 unsigned int buf_len = 0;
246 char *buf = NULL;
248 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
249 domain->name ));
251 if (state) {
252 uid_t ret_uid = (uid_t)-1;
253 if (sys_getpeereid(state->sock, &ret_uid)==0) {
254 /* We know who's asking - look up their SID if
255 it's one we've mapped before. */
256 status = idmap_uid_to_sid(domain->name,
257 &querying_user_sid, ret_uid);
258 if (NT_STATUS_IS_OK(status)) {
259 pquerying_user_sid = &querying_user_sid;
260 DEBUG(10,("fill_grent_mem_domain_users: "
261 "querying uid %u -> %s\n",
262 (unsigned int)ret_uid,
263 sid_string_dbg(pquerying_user_sid)));
268 /* Only look up if it was a winbindd user in this domain. */
269 if (pquerying_user_sid &&
270 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
272 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
273 sid_string_dbg(pquerying_user_sid) ));
275 status = domain->methods->lookup_usergroups(domain,
276 mem_ctx,
277 pquerying_user_sid,
278 &num_groups,
279 &user_sids);
280 if (!NT_STATUS_IS_OK(status)) {
281 DEBUG(1, ("fill_grent_mem_domain_users: "
282 "lookup_usergroups failed "
283 "for sid %s in domain %s (error: %s)\n",
284 sid_string_dbg(pquerying_user_sid),
285 domain->name,
286 nt_errstr(status)));
287 return False;
290 for (i = 0; i < num_groups; i++) {
291 if (sid_equal(group_sid, &user_sids[i])) {
292 /* User is in Domain Users, add their name
293 as the only group member. */
294 u_in_group = True;
295 break;
300 if (u_in_group) {
301 size_t len = 0;
302 char *domainname = NULL;
303 char *username = NULL;
304 fstring name;
305 char *mapped_name = NULL;
306 enum lsa_SidType type;
307 struct winbindd_domain *target_domain = NULL;
308 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
310 DEBUG(10,("fill_grent_mem_domain_users: "
311 "sid %s in 'Domain Users' in domain %s\n",
312 sid_string_dbg(pquerying_user_sid),
313 domain->name ));
315 status = domain->methods->sid_to_name(domain, mem_ctx,
316 pquerying_user_sid,
317 &domainname,
318 &username,
319 &type);
320 if (!NT_STATUS_IS_OK(status)) {
321 DEBUG(1, ("could not lookup username for user "
322 "sid %s in domain %s (error: %s)\n",
323 sid_string_dbg(pquerying_user_sid),
324 domain->name,
325 nt_errstr(status)));
326 return False;
329 target_domain = find_domain_from_name_noinit(domainname);
330 name_map_status = normalize_name_map(mem_ctx, target_domain,
331 username, &mapped_name);
333 /* Basic whitespace replacement */
334 if (NT_STATUS_IS_OK(name_map_status)) {
335 fill_domain_username(name, domainname, mapped_name, true);
337 /* Mapped to an alias */
338 else if (NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
339 fstrcpy(name, mapped_name);
341 /* no mapping done...use original name */
342 else {
343 fill_domain_username(name, domainname, username, true);
346 len = strlen(name);
347 buf_len = len + 1;
348 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
349 DEBUG(1, ("out of memory\n"));
350 return False;
352 memcpy(buf, name, buf_len);
354 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
355 "'Domain Users' in domain %s\n",
356 name, domain->name ));
358 /* user is the only member */
359 *num_gr_mem = 1;
362 *gr_mem = buf;
363 *gr_mem_len = buf_len;
365 DEBUG(10, ("fill_grent_mem_domain_users: "
366 "num_mem = %u, len = %u, mem = %s\n",
367 (unsigned int)*num_gr_mem,
368 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
370 return True;
373 /***********************************************************************
374 Add names to a list. Assumes a canonical version of the string
375 in DOMAIN\user
376 ***********************************************************************/
378 static int namecmp( const void *a, const void *b )
380 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
383 static void sort_unique_list(char ***list, uint32 *n_list)
385 uint32_t i;
387 /* search for duplicates for sorting and looking for matching
388 neighbors */
390 qsort(*list, *n_list, sizeof(char*), QSORT_CAST namecmp);
392 for (i=1; i < *n_list; i++) {
393 if (strcmp((*list)[i-1], (*list)[i]) == 0) {
394 memmove(&((*list)[i-1]), &((*list)[i]),
395 sizeof(char*)*((*n_list)-i));
396 (*n_list)--;
401 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
402 char ***list, uint32 *n_list,
403 char **names, uint32 n_names )
405 char **new_list = NULL;
406 uint32 n_new_list = 0;
407 int i, j;
409 if ( !names || (n_names == 0) )
410 return NT_STATUS_OK;
412 /* Alloc the maximum size we'll need */
414 if ( *list == NULL ) {
415 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
416 return NT_STATUS_NO_MEMORY;
418 n_new_list = n_names;
419 } else {
420 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
421 (*n_list) + n_names );
422 if ( !new_list )
423 return NT_STATUS_NO_MEMORY;
424 n_new_list = (*n_list) + n_names;
427 /* Add all names */
429 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
430 new_list[i] = talloc_strdup( new_list, names[j] );
433 *list = new_list;
434 *n_list = n_new_list;
436 return NT_STATUS_OK;
439 /***********************************************************************
440 ***********************************************************************/
442 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
443 struct winbindd_domain *d,
444 DOM_SID *glist, uint32 n_glist,
445 DOM_SID **new_glist, uint32 *n_new_glist,
446 char ***members, uint32 *n_members )
448 int i, j;
449 NTSTATUS status = NT_STATUS_OK;
450 uint32 num_names = 0;
451 uint32 *name_types = NULL;
452 char **names = NULL;
453 DOM_SID *sid_mem = NULL;
454 TALLOC_CTX *tmp_ctx = NULL;
455 DOM_SID *new_groups = NULL;
456 size_t new_groups_size = 0;
458 *members = NULL;
459 *n_members = 0;
460 *new_glist = NULL;
461 *n_new_glist = 0;
463 for ( i=0; i<n_glist; i++ ) {
464 tmp_ctx = talloc_new( ctx );
466 /* Lookup the group membership */
468 status = d->methods->lookup_groupmem(d, tmp_ctx,
469 &glist[i], &num_names,
470 &sid_mem, &names,
471 &name_types);
472 if ( !NT_STATUS_IS_OK(status) )
473 goto out;
475 /* Separate users and groups into two lists */
477 for ( j=0; j<num_names; j++ ) {
479 /* Users */
480 if ( name_types[j] == SID_NAME_USER ||
481 name_types[j] == SID_NAME_COMPUTER )
483 status = add_names_to_list( ctx, members,
484 n_members,
485 names+j, 1 );
486 if ( !NT_STATUS_IS_OK(status) )
487 goto out;
489 continue;
492 /* Groups */
493 if ( name_types[j] == SID_NAME_DOM_GRP ||
494 name_types[j] == SID_NAME_ALIAS )
496 status = add_sid_to_array_unique(ctx,
497 &sid_mem[j],
498 &new_groups,
499 &new_groups_size);
500 if (!NT_STATUS_IS_OK(status)) {
501 goto out;
504 continue;
508 TALLOC_FREE( tmp_ctx );
511 *new_glist = new_groups;
512 *n_new_glist = (uint32)new_groups_size;
514 out:
515 TALLOC_FREE( tmp_ctx );
517 return status;
520 /***********************************************************************
521 Fill in the group membership field of a NT group given by group_sid
522 ***********************************************************************/
524 static bool fill_grent_mem(struct winbindd_domain *domain,
525 struct winbindd_cli_state *state,
526 DOM_SID *group_sid,
527 enum lsa_SidType group_name_type,
528 size_t *num_gr_mem, char **gr_mem,
529 size_t *gr_mem_len)
531 uint32 num_names = 0;
532 unsigned int buf_len = 0, buf_ndx = 0, i;
533 char **names = NULL, *buf = NULL;
534 bool result = False;
535 TALLOC_CTX *mem_ctx;
536 uint32 group_rid;
537 DOM_SID *glist = NULL;
538 DOM_SID *new_glist = NULL;
539 uint32 n_glist, n_new_glist;
540 int max_depth = lp_winbind_expand_groups();
542 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
543 return False;
545 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
547 /* Initialize with no members */
549 *num_gr_mem = 0;
551 /* HACK ALERT!! This whole routine does not cope with group members
552 * from more than one domain, ie aliases. Thus we have to work it out
553 * ourselves in a special routine. */
555 if (domain->internal) {
556 result = fill_passdb_alias_grmem(domain, group_sid,
557 num_gr_mem,
558 gr_mem, gr_mem_len);
559 goto done;
562 /* Verify name type */
564 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
565 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
567 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
568 sid_string_dbg(group_sid),
569 domain->name, group_name_type));
570 goto done;
573 /* OPTIMIZATION / HACK. See comment in
574 fill_grent_mem_domusers() */
576 sid_peek_rid( group_sid, &group_rid );
577 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
578 result = fill_grent_mem_domusers( mem_ctx, domain, state,
579 group_sid, group_name_type,
580 num_gr_mem, gr_mem,
581 gr_mem_len );
582 goto done;
585 /* Real work goes here. Create a list of group names to
586 expand starting with the initial one. Pass that to
587 expand_groups() which returns a list of more group names
588 to expand. Do this up to the max search depth. */
590 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
591 result = False;
592 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
593 goto done;
595 sid_copy( &glist[0], group_sid );
596 n_glist = 1;
598 for ( i=0; i<max_depth && glist; i++ ) {
599 uint32 n_members = 0;
600 char **members = NULL;
601 NTSTATUS nt_status;
602 int j;
604 nt_status = expand_groups( mem_ctx, domain,
605 glist, n_glist,
606 &new_glist, &n_new_glist,
607 &members, &n_members);
608 if ( !NT_STATUS_IS_OK(nt_status) ) {
609 result = False;
610 goto done;
613 /* Add new group members to list. Pass through the
614 alias mapping function */
616 for (j=0; j<n_members; j++) {
617 fstring name_domain, name_acct;
618 fstring qualified_name;
619 char *mapped_name = NULL;
620 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
621 struct winbindd_domain *target_domain = NULL;
623 if (parse_domain_user(members[j], name_domain, name_acct)) {
624 target_domain = find_domain_from_name_noinit(name_domain);
625 /* NOW WHAT ? */
627 if (!target_domain) {
628 target_domain = domain;
631 name_map_status = normalize_name_map(members, target_domain,
632 name_acct, &mapped_name);
634 /* Basic whitespace replacement */
635 if (NT_STATUS_IS_OK(name_map_status)) {
636 fill_domain_username(qualified_name, name_domain,
637 mapped_name, true);
638 mapped_name = qualified_name;
640 /* no mapping at all */
641 else if (!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
642 mapped_name = members[j];
645 nt_status = add_names_to_list( mem_ctx, &names,
646 &num_names,
647 &mapped_name, 1);
648 if ( !NT_STATUS_IS_OK(nt_status) ) {
649 result = False;
650 goto done;
654 TALLOC_FREE( members );
656 /* If we have no more groups to expand, break out
657 early */
659 if (new_glist == NULL)
660 break;
662 /* One more round */
663 TALLOC_FREE(glist);
664 glist = new_glist;
665 n_glist = n_new_glist;
667 TALLOC_FREE( glist );
669 sort_unique_list(&names, &num_names);
671 DEBUG(10, ("looked up %d names\n", num_names));
673 again:
674 /* Add members to list */
676 for (i = 0; i < num_names; i++) {
677 int len;
679 DEBUG(10, ("processing name %s\n", names[i]));
681 len = strlen(names[i]);
683 /* Add to list or calculate buffer length */
685 if (!buf) {
686 buf_len += len + 1; /* List is comma separated */
687 (*num_gr_mem)++;
688 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
689 } else {
690 DEBUG(10, ("appending %s at ndx %d\n",
691 names[i], buf_ndx));
692 parse_add_domuser(&buf[buf_ndx], names[i], &len);
693 buf_ndx += len;
694 buf[buf_ndx] = ',';
695 buf_ndx++;
699 /* Allocate buffer */
701 if (!buf && buf_len != 0) {
702 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
703 DEBUG(1, ("out of memory\n"));
704 result = False;
705 goto done;
707 memset(buf, 0, buf_len);
708 goto again;
711 /* Now we're done */
713 if (buf && buf_ndx > 0) {
714 buf[buf_ndx - 1] = '\0';
717 *gr_mem = buf;
718 *gr_mem_len = buf_len;
720 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
721 (unsigned int)*num_gr_mem,
722 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
723 result = True;
725 done:
727 talloc_destroy(mem_ctx);
729 DEBUG(10, ("fill_grent_mem returning %d\n", result));
731 return result;
734 static void winbindd_getgrsid(struct winbindd_cli_state *state, DOM_SID group_sid);
736 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
737 enum lsa_SidType type )
739 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
741 if (!success) {
742 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
743 request_error(state);
744 return;
747 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
748 DEBUG(5,("getgrnam_recv: not a group!\n"));
749 request_error(state);
750 return;
753 winbindd_getgrsid( state, *sid );
757 /* Return a group structure from a group name */
759 void winbindd_getgrnam(struct winbindd_cli_state *state)
761 struct winbindd_domain *domain;
762 fstring name_domain, name_group;
763 char *tmp;
764 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
766 /* Ensure null termination */
767 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
769 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
770 state->request.data.groupname));
772 nt_status = normalize_name_unmap(state->mem_ctx,
773 state->request.data.groupname,
774 &tmp);
775 /* If we didn't map anything in the above call, just reset the
776 tmp pointer to the original string */
777 if (!NT_STATUS_IS_OK(nt_status) &&
778 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
780 tmp = state->request.data.groupname;
783 /* Parse domain and groupname */
785 memset(name_group, 0, sizeof(name_group));
787 name_domain[0] = '\0';
788 name_group[0] = '\0';
790 parse_domain_user(tmp, name_domain, name_group);
792 /* if no domain or our local domain and no local tdb group, default to
793 * our local domain for aliases */
795 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
796 fstrcpy(name_domain, get_global_sam_name());
799 /* Get info for the domain */
801 if ((domain = find_domain_from_name_noinit(name_domain)) == NULL) {
802 DEBUG(3, ("could not get domain sid for domain %s\n",
803 name_domain));
804 request_error(state);
805 return;
807 /* should we deal with users for our domain? */
809 if ( lp_winbind_trusted_domains_only() && domain->primary) {
810 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
811 "getgrnam() for %s\\%s.\n", name_domain, name_group));
812 request_error(state);
813 return;
816 /* Get rid and name type from name */
818 fstrcpy( name_group, tmp );
820 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
821 getgrnam_recv, WINBINDD_GETGRNAM, state );
824 struct getgrsid_state {
825 struct winbindd_cli_state *state;
826 struct winbindd_domain *domain;
827 char *group_name;
828 enum lsa_SidType group_type;
829 uid_t gid;
830 DOM_SID group_sid;
833 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
835 struct getgrsid_state *s =
836 (struct getgrsid_state *)private_data;
837 struct winbindd_domain *domain;
838 size_t gr_mem_len;
839 size_t num_gr_mem;
840 char *gr_mem;
841 fstring dom_name, group_name;
843 if (!success) {
844 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
845 request_error(s->state);
846 return;
849 s->gid = gid;
851 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
852 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
853 request_error(s->state);
854 return;
858 /* Fill in group structure */
860 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
861 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
862 request_error(s->state);
863 return;
866 if (!fill_grent(s->state->mem_ctx, &s->state->response.data.gr,
867 dom_name, group_name, gid) ||
868 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
869 &num_gr_mem, &gr_mem, &gr_mem_len))
871 request_error(s->state);
872 return;
875 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
877 /* Group membership lives at start of extra data */
879 s->state->response.data.gr.gr_mem_ofs = 0;
881 s->state->response.length += gr_mem_len;
882 s->state->response.extra_data.data = gr_mem;
884 request_ok(s->state);
887 static void getgrsid_lookupsid_recv( void *private_data, bool success,
888 const char *dom_name, const char *name,
889 enum lsa_SidType name_type )
891 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
892 char *mapped_name = NULL;
893 fstring raw_name;
894 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
896 if (!success) {
897 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
898 request_error(s->state);
899 return;
902 /* either it's a domain group, a domain local group, or a
903 local group in an internal domain */
905 if ( !( (name_type==SID_NAME_DOM_GRP) ||
906 ((name_type==SID_NAME_ALIAS) &&
907 (s->domain->primary || s->domain->internal)) ) )
909 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
910 dom_name, name, name_type));
911 request_error(s->state);
912 return;
915 /* normalize the name and ensure that we have the DOM\name
916 coming out of here */
918 fstrcpy(raw_name, name);
920 nt_status = normalize_name_unmap(s->state->mem_ctx, raw_name,
921 &mapped_name);
923 /* basic whitespace reversal */
924 if (NT_STATUS_IS_OK(nt_status)) {
925 s->group_name = talloc_asprintf(s->state->mem_ctx,
926 "%s%c%s",
927 dom_name,
928 *lp_winbind_separator(),
929 mapped_name);
931 /* mapped from alias */
932 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
933 s->group_name = mapped_name;
935 /* no mapping at all. use original string */
936 else {
937 s->group_name = talloc_asprintf(s->state->mem_ctx,
938 "%s%c%s",
939 dom_name,
940 *lp_winbind_separator(),
941 raw_name);
944 if (s->group_name == NULL) {
945 DEBUG(1, ("getgrsid_lookupsid_recv: group_name is NULL!\n"));
946 request_error(s->state);
947 return;
950 s->group_type = name_type;
952 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
953 getgrsid_sid2gid_recv, s);
956 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
958 struct getgrsid_state *s;
960 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
961 DEBUG(0, ("talloc failed\n"));
962 request_error(state);
963 return;
966 s->state = state;
968 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
969 DEBUG(3, ("Could not find domain for sid %s\n",
970 sid_string_dbg(&group_sid)));
971 request_error(state);
972 return;
975 sid_copy(&s->group_sid, &group_sid);
977 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
978 getgrsid_lookupsid_recv, s );
982 static void getgrgid_recv(void *private_data, bool success, const char *sid)
984 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
985 enum lsa_SidType name_type;
986 DOM_SID group_sid;
988 if (success) {
989 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
990 (unsigned long)(state->request.data.gid), sid));
992 if (!string_to_sid(&group_sid, sid)) {
993 DEBUG(1,("getgrgid_recv: Could not convert sid %s "
994 "from string\n", sid));
995 request_error(state);
996 return;
999 winbindd_getgrsid(state, group_sid);
1000 return;
1003 /* Ok, this might be "ours", i.e. an alias */
1004 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
1005 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
1006 (name_type == SID_NAME_ALIAS)) {
1007 /* Hey, got an alias */
1008 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
1009 (unsigned long)(state->request.data.gid), sid));
1010 winbindd_getgrsid(state, group_sid);
1011 return;
1014 DEBUG(1, ("could not convert gid %lu to sid\n",
1015 (unsigned long)state->request.data.gid));
1016 request_error(state);
1019 /* Return a group structure from a gid number */
1020 void winbindd_getgrgid(struct winbindd_cli_state *state)
1022 gid_t gid = state->request.data.gid;
1024 DEBUG(3, ("[%5lu]: getgrgid %lu\n",
1025 (unsigned long)state->pid,
1026 (unsigned long)gid));
1028 /* always use the async interface */
1029 winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
1033 * set/get/endgrent functions
1036 /* "Rewind" file pointer for group database enumeration */
1038 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
1040 struct winbindd_domain *domain;
1042 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
1044 /* Check user has enabled this */
1046 if (!lp_winbind_enum_groups()) {
1047 return False;
1050 /* Free old static data if it exists */
1052 if (state->getgrent_state != NULL) {
1053 free_getent_state(state->getgrent_state);
1054 state->getgrent_state = NULL;
1057 /* Create sam pipes for each domain we know about */
1059 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1060 struct getent_state *domain_state;
1062 /* Create a state record for this domain */
1064 /* don't add our domaina if we are a PDC or if we
1065 are a member of a Samba domain */
1067 if ( lp_winbind_trusted_domains_only() && domain->primary )
1069 continue;
1072 domain_state = SMB_MALLOC_P(struct getent_state);
1073 if (!domain_state) {
1074 DEBUG(1, ("winbindd_setgrent: "
1075 "malloc failed for domain_state!\n"));
1076 return False;
1079 ZERO_STRUCTP(domain_state);
1081 fstrcpy(domain_state->domain_name, domain->name);
1083 /* Add to list of open domains */
1085 DLIST_ADD(state->getgrent_state, domain_state);
1088 state->getgrent_initialized = True;
1089 return True;
1092 void winbindd_setgrent(struct winbindd_cli_state *state)
1094 if (winbindd_setgrent_internal(state)) {
1095 request_ok(state);
1096 } else {
1097 request_error(state);
1101 /* Close file pointer to ntdom group database */
1103 void winbindd_endgrent(struct winbindd_cli_state *state)
1105 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
1107 free_getent_state(state->getgrent_state);
1108 state->getgrent_initialized = False;
1109 state->getgrent_state = NULL;
1110 request_ok(state);
1113 /* Get the list of domain groups and domain aliases for a domain. We fill in
1114 the sam_entries and num_sam_entries fields with domain group information.
1115 Return True if some groups were returned, False otherwise. */
1117 bool get_sam_group_entries(struct getent_state *ent)
1119 NTSTATUS status;
1120 uint32 num_entries;
1121 struct acct_info *name_list = NULL;
1122 TALLOC_CTX *mem_ctx;
1123 bool result = False;
1124 struct acct_info *sam_grp_entries = NULL;
1125 struct winbindd_domain *domain;
1127 if (ent->got_sam_entries)
1128 return False;
1130 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1131 ent->domain_name))) {
1132 DEBUG(1, ("get_sam_group_entries: "
1133 "could not create talloc context!\n"));
1134 return False;
1137 /* Free any existing group info */
1139 SAFE_FREE(ent->sam_entries);
1140 ent->num_sam_entries = 0;
1141 ent->got_sam_entries = True;
1143 /* Enumerate domain groups */
1145 num_entries = 0;
1147 if (!(domain = find_domain_from_name(ent->domain_name))) {
1148 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1149 ent->domain_name));
1150 goto done;
1153 /* always get the domain global groups */
1155 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1156 &sam_grp_entries);
1158 if (!NT_STATUS_IS_OK(status)) {
1159 DEBUG(3, ("get_sam_group_entries: "
1160 "could not enumerate domain groups! Error: %s\n",
1161 nt_errstr(status)));
1162 result = False;
1163 goto done;
1166 /* Copy entries into return buffer */
1168 if (num_entries) {
1169 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1170 if (!name_list) {
1171 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1172 "memory for %d domain groups!\n",
1173 num_entries));
1174 result = False;
1175 goto done;
1177 memcpy(name_list, sam_grp_entries,
1178 num_entries * sizeof(struct acct_info));
1181 ent->num_sam_entries = num_entries;
1183 /* get the domain local groups if we are a member of a native win2k
1184 * domain and are not using LDAP to get the groups */
1186 if ( ( lp_security() != SEC_ADS && domain->native_mode
1187 && domain->primary) || domain->internal )
1189 DEBUG(4,("get_sam_group_entries: %s domain; "
1190 "enumerating local groups as well\n",
1191 domain->native_mode ? "Native Mode 2k":
1192 "BUILTIN or local"));
1194 status = domain->methods->enum_local_groups(domain, mem_ctx,
1195 &num_entries,
1196 &sam_grp_entries);
1198 if ( !NT_STATUS_IS_OK(status) ) {
1199 DEBUG(3,("get_sam_group_entries: "
1200 "Failed to enumerate "
1201 "domain local groups with error %s!\n",
1202 nt_errstr(status)));
1203 num_entries = 0;
1205 else
1206 DEBUG(4,("get_sam_group_entries: "
1207 "Returned %d local groups\n",
1208 num_entries));
1210 /* Copy entries into return buffer */
1212 if ( num_entries ) {
1213 name_list = SMB_REALLOC_ARRAY(name_list,
1214 struct acct_info,
1215 ent->num_sam_entries+
1216 num_entries);
1217 if (!name_list) {
1218 DEBUG(0,("get_sam_group_entries: "
1219 "Failed to realloc more memory "
1220 "for %d local groups!\n",
1221 num_entries));
1222 result = False;
1223 goto done;
1226 memcpy(&name_list[ent->num_sam_entries],
1227 sam_grp_entries,
1228 num_entries * sizeof(struct acct_info));
1231 ent->num_sam_entries += num_entries;
1235 /* Fill in remaining fields */
1237 ent->sam_entries = name_list;
1238 ent->sam_entry_index = 0;
1240 result = (ent->num_sam_entries > 0);
1242 done:
1243 talloc_destroy(mem_ctx);
1245 return result;
1248 /* Fetch next group entry from ntdom database */
1250 #define MAX_GETGRENT_GROUPS 500
1252 void winbindd_getgrent(struct winbindd_cli_state *state)
1254 struct getent_state *ent;
1255 struct winbindd_gr *group_list = NULL;
1256 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1257 char *gr_mem_list = NULL;
1259 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1261 /* Check user has enabled this */
1263 if (!lp_winbind_enum_groups()) {
1264 request_error(state);
1265 return;
1268 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1270 if (num_groups == 0) {
1271 request_error(state);
1272 return;
1275 group_list = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups);
1276 if (!group_list) {
1277 request_error(state);
1278 return;
1280 /* will be freed by process_request() */
1281 state->response.extra_data.data = group_list;
1283 memset(state->response.extra_data.data, '\0',
1284 num_groups * sizeof(struct winbindd_gr) );
1286 state->response.data.num_entries = 0;
1288 if (!state->getgrent_initialized)
1289 winbindd_setgrent_internal(state);
1291 if (!(ent = state->getgrent_state)) {
1292 request_error(state);
1293 return;
1296 /* Start sending back groups */
1298 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1299 struct acct_info *name_list = NULL;
1300 fstring domain_group_name;
1301 uint32 result;
1302 gid_t group_gid;
1303 size_t gr_mem_len;
1304 char *gr_mem;
1305 DOM_SID group_sid;
1306 struct winbindd_domain *domain;
1308 /* Do we need to fetch another chunk of groups? */
1310 tryagain:
1312 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1313 ent->sam_entry_index, ent->num_sam_entries));
1315 if (ent->num_sam_entries == ent->sam_entry_index) {
1317 while(ent && !get_sam_group_entries(ent)) {
1318 struct getent_state *next_ent;
1320 DEBUG(10, ("freeing state info for domain %s\n",
1321 ent->domain_name));
1323 /* Free state information for this domain */
1325 SAFE_FREE(ent->sam_entries);
1327 next_ent = ent->next;
1328 DLIST_REMOVE(state->getgrent_state, ent);
1330 SAFE_FREE(ent);
1331 ent = next_ent;
1334 /* No more domains */
1336 if (!ent)
1337 break;
1340 name_list = (struct acct_info *)ent->sam_entries;
1342 if (!(domain = find_domain_from_name(ent->domain_name))) {
1343 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1344 ent->domain_name));
1345 result = False;
1346 goto done;
1349 /* Lookup group info */
1351 sid_copy(&group_sid, &domain->sid);
1352 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1354 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->have_idmap_config
1355 ? domain->name : "",
1356 &group_sid, &group_gid)))
1358 union unid_t id;
1359 enum lsa_SidType type;
1361 DEBUG(10, ("SID %s not in idmap\n",
1362 sid_string_dbg(&group_sid)));
1364 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1365 DEBUG(1,("could not look up gid for group %s\n",
1366 name_list[ent->sam_entry_index].acct_name));
1367 ent->sam_entry_index++;
1368 goto tryagain;
1371 if ((type != SID_NAME_DOM_GRP) &&
1372 (type != SID_NAME_ALIAS) &&
1373 (type != SID_NAME_WKN_GRP)) {
1374 DEBUG(1, ("Group %s is a %s, not a group\n",
1375 sid_type_lookup(type),
1376 name_list[ent->sam_entry_index].acct_name));
1377 ent->sam_entry_index++;
1378 goto tryagain;
1380 group_gid = id.gid;
1383 DEBUG(10, ("got gid %lu for group %lu\n",
1384 (unsigned long)group_gid,
1385 (unsigned long)name_list[ent->sam_entry_index].rid));
1387 /* Fill in group entry */
1389 fill_domain_username(domain_group_name, ent->domain_name,
1390 name_list[ent->sam_entry_index].acct_name, True);
1392 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1393 ent->domain_name,
1394 name_list[ent->sam_entry_index].acct_name,
1395 group_gid);
1397 /* Fill in group membership entry */
1399 if (result) {
1400 size_t num_gr_mem = 0;
1401 DOM_SID member_sid;
1402 group_list[group_list_ndx].num_gr_mem = 0;
1403 gr_mem = NULL;
1404 gr_mem_len = 0;
1406 /* Get group membership */
1407 if (state->request.cmd == WINBINDD_GETGRLST) {
1408 result = True;
1409 } else {
1410 sid_copy(&member_sid, &domain->sid);
1411 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1412 result = fill_grent_mem(
1413 domain,
1414 NULL,
1415 &member_sid,
1416 SID_NAME_DOM_GRP,
1417 &num_gr_mem,
1418 &gr_mem, &gr_mem_len);
1420 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1424 if (result) {
1425 /* Append to group membership list */
1426 gr_mem_list = (char *)SMB_REALLOC(
1427 gr_mem_list, gr_mem_list_len + gr_mem_len);
1429 if (!gr_mem_list &&
1430 (group_list[group_list_ndx].num_gr_mem != 0)) {
1431 DEBUG(0, ("out of memory\n"));
1432 gr_mem_list_len = 0;
1433 break;
1436 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1437 gr_mem_list_len, (unsigned int)gr_mem_len));
1439 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1440 gr_mem_len);
1442 SAFE_FREE(gr_mem);
1444 group_list[group_list_ndx].gr_mem_ofs =
1445 gr_mem_list_len;
1447 gr_mem_list_len += gr_mem_len;
1450 ent->sam_entry_index++;
1452 /* Add group to return list */
1454 if (result) {
1456 DEBUG(10, ("adding group num_entries = %d\n",
1457 state->response.data.num_entries));
1459 group_list_ndx++;
1460 state->response.data.num_entries++;
1462 state->response.length +=
1463 sizeof(struct winbindd_gr);
1465 } else {
1466 DEBUG(0, ("could not lookup domain group %s\n",
1467 domain_group_name));
1471 /* Copy the list of group memberships to the end of the extra data */
1473 if (group_list_ndx == 0)
1474 goto done;
1476 state->response.extra_data.data = SMB_REALLOC(
1477 state->response.extra_data.data,
1478 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1480 if (!state->response.extra_data.data) {
1481 DEBUG(0, ("out of memory\n"));
1482 group_list_ndx = 0;
1483 SAFE_FREE(gr_mem_list);
1484 request_error(state);
1485 return;
1488 memcpy(&((char *)state->response.extra_data.data)
1489 [group_list_ndx * sizeof(struct winbindd_gr)],
1490 gr_mem_list, gr_mem_list_len);
1492 state->response.length += gr_mem_list_len;
1494 DEBUG(10, ("returning %d groups, length = %d\n",
1495 group_list_ndx, gr_mem_list_len));
1497 /* Out of domains */
1499 done:
1501 SAFE_FREE(gr_mem_list);
1503 if (group_list_ndx > 0)
1504 request_ok(state);
1505 else
1506 request_error(state);
1509 /* List domain groups without mapping to unix ids */
1510 void winbindd_list_groups(struct winbindd_cli_state *state)
1512 winbindd_list_ent(state, LIST_GROUPS);
1515 /* Get user supplementary groups. This is much quicker than trying to
1516 invert the groups database. We merge the groups from the gids and
1517 other_sids info3 fields as trusted domain, universal group
1518 memberships, and nested groups (win2k native mode only) are not
1519 returned by the getgroups RPC call but are present in the info3. */
1521 struct getgroups_state {
1522 struct winbindd_cli_state *state;
1523 struct winbindd_domain *domain;
1524 char *domname;
1525 char *username;
1526 DOM_SID user_sid;
1528 const DOM_SID *token_sids;
1529 size_t i, num_token_sids;
1531 gid_t *token_gids;
1532 size_t num_token_gids;
1535 static void getgroups_usersid_recv(void *private_data, bool success,
1536 const DOM_SID *sid, enum lsa_SidType type);
1537 static void getgroups_tokensids_recv(void *private_data, bool success,
1538 DOM_SID *token_sids, size_t num_token_sids);
1539 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1541 void winbindd_getgroups(struct winbindd_cli_state *state)
1543 struct getgroups_state *s;
1544 char *real_name = NULL;
1545 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1547 /* Ensure null termination */
1548 state->request.data.username
1549 [sizeof(state->request.data.username)-1]='\0';
1551 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1552 state->request.data.username));
1554 /* Parse domain and username */
1556 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1557 if (s == NULL) {
1558 DEBUG(0, ("talloc failed\n"));
1559 request_error(state);
1560 return;
1563 s->state = state;
1565 nt_status = normalize_name_unmap(state->mem_ctx,
1566 state->request.data.username,
1567 &real_name);
1569 /* Reset the real_name pointer if we didn't do anything
1570 productive in the above call */
1571 if (!NT_STATUS_IS_OK(nt_status) &&
1572 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
1574 real_name = state->request.data.username;
1577 if (!parse_domain_user_talloc(state->mem_ctx, real_name,
1578 &s->domname, &s->username)) {
1579 DEBUG(5, ("Could not parse domain user: %s\n",
1580 real_name));
1582 /* error out if we do not have nested group support */
1584 if ( !lp_winbind_nested_groups() ) {
1585 request_error(state);
1586 return;
1589 s->domname = talloc_strdup(state->mem_ctx,
1590 get_global_sam_name());
1591 s->username = talloc_strdup(state->mem_ctx,
1592 state->request.data.username);
1595 /* Get info for the domain (either by short domain name or
1596 DNS name in the case of a UPN) */
1598 s->domain = find_domain_from_name_noinit(s->domname);
1599 if (!s->domain) {
1600 char *p = strchr(s->username, '@');
1602 if (p) {
1603 s->domain = find_domain_from_name_noinit(p+1);
1608 if (s->domain == NULL) {
1609 DEBUG(7, ("could not find domain entry for domain %s\n",
1610 s->domname));
1611 request_error(state);
1612 return;
1615 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1616 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1617 "getgroups() for %s\\%s.\n", s->domname,
1618 s->username));
1619 request_error(state);
1620 return;
1623 /* Get rid and name type from name. The following costs 1 packet */
1625 winbindd_lookupname_async(state->mem_ctx,
1626 s->domname, s->username,
1627 getgroups_usersid_recv,
1628 WINBINDD_GETGROUPS, s);
1631 static void getgroups_usersid_recv(void *private_data, bool success,
1632 const DOM_SID *sid, enum lsa_SidType type)
1634 struct getgroups_state *s =
1635 (struct getgroups_state *)private_data;
1637 if ((!success) ||
1638 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1639 request_error(s->state);
1640 return;
1643 sid_copy(&s->user_sid, sid);
1645 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1646 getgroups_tokensids_recv, s);
1649 static void getgroups_tokensids_recv(void *private_data, bool success,
1650 DOM_SID *token_sids, size_t num_token_sids)
1652 struct getgroups_state *s =
1653 (struct getgroups_state *)private_data;
1655 /* We need at least the user sid and the primary group in the token,
1656 * otherwise it's an error */
1658 if ((!success) || (num_token_sids < 2)) {
1659 request_error(s->state);
1660 return;
1663 s->token_sids = token_sids;
1664 s->num_token_sids = num_token_sids;
1665 s->i = 0;
1667 s->token_gids = NULL;
1668 s->num_token_gids = 0;
1670 getgroups_sid2gid_recv(s, False, 0);
1673 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1675 struct getgroups_state *s =
1676 (struct getgroups_state *)private_data;
1678 if (success) {
1679 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1680 &s->token_gids,
1681 &s->num_token_gids)) {
1682 return;
1686 if (s->i < s->num_token_sids) {
1687 const DOM_SID *sid = &s->token_sids[s->i];
1688 s->i += 1;
1690 if (sid_equal(sid, &s->user_sid)) {
1691 getgroups_sid2gid_recv(s, False, 0);
1692 return;
1695 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1696 getgroups_sid2gid_recv, s);
1697 return;
1700 s->state->response.data.num_entries = s->num_token_gids;
1701 if (s->num_token_gids) {
1702 /* s->token_gids are talloced */
1703 s->state->response.extra_data.data =
1704 smb_xmemdup(s->token_gids,
1705 s->num_token_gids * sizeof(gid_t));
1706 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1708 request_ok(s->state);
1711 /* Get user supplementary sids. This is equivalent to the
1712 winbindd_getgroups() function but it involves a SID->SIDs mapping
1713 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1714 idmap. This call is designed to be used with applications that need
1715 to do ACL evaluation themselves. Note that the cached info3 data is
1716 not used
1718 this function assumes that the SID that comes in is a user SID. If
1719 you pass in another type of SID then you may get unpredictable
1720 results.
1723 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1724 size_t num_sids);
1726 void winbindd_getusersids(struct winbindd_cli_state *state)
1728 DOM_SID *user_sid;
1730 /* Ensure null termination */
1731 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1733 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1734 if (user_sid == NULL) {
1735 DEBUG(1, ("talloc failed\n"));
1736 request_error(state);
1737 return;
1740 if (!string_to_sid(user_sid, state->request.data.sid)) {
1741 DEBUG(1, ("Could not get convert sid %s from string\n",
1742 state->request.data.sid));
1743 request_error(state);
1744 return;
1747 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1748 state);
1751 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1752 size_t num_sids)
1754 struct winbindd_cli_state *state =
1755 (struct winbindd_cli_state *)private_data;
1756 char *ret = NULL;
1757 unsigned ofs, ret_size = 0;
1758 size_t i;
1760 if (!success) {
1761 request_error(state);
1762 return;
1765 /* work out the response size */
1766 for (i = 0; i < num_sids; i++) {
1767 fstring s;
1768 sid_to_fstring(s, &sids[i]);
1769 ret_size += strlen(s) + 1;
1772 /* build the reply */
1773 ret = (char *)SMB_MALLOC(ret_size);
1774 if (!ret) {
1775 DEBUG(0, ("malloc failed\n"));
1776 request_error(state);
1777 return;
1779 ofs = 0;
1780 for (i = 0; i < num_sids; i++) {
1781 fstring s;
1782 sid_to_fstring(s, &sids[i]);
1783 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1784 ofs += strlen(ret+ofs) + 1;
1787 /* Send data back to client */
1788 state->response.data.num_entries = num_sids;
1789 state->response.extra_data.data = ret;
1790 state->response.length += ret_size;
1791 request_ok(state);
1794 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1796 DOM_SID user_sid;
1797 struct winbindd_domain *domain;
1799 /* Ensure null termination */
1800 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1802 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1803 DEBUG(1, ("Could not get convert sid %s from string\n",
1804 state->request.data.sid));
1805 request_error(state);
1806 return;
1809 /* Get info for the domain */
1810 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1811 DEBUG(0,("could not find domain entry for sid %s\n",
1812 sid_string_dbg(&user_sid)));
1813 request_error(state);
1814 return;
1817 sendto_domain(state, domain);
1820 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1821 struct winbindd_cli_state *state)
1823 DOM_SID user_sid;
1824 NTSTATUS status;
1826 char *sidstring;
1827 ssize_t len;
1828 DOM_SID *groups;
1829 uint32 num_groups;
1831 /* Ensure null termination */
1832 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1834 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1835 DEBUG(1, ("Could not get convert sid %s from string\n",
1836 state->request.data.sid));
1837 return WINBINDD_ERROR;
1840 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1841 &user_sid, &num_groups,
1842 &groups);
1843 if (!NT_STATUS_IS_OK(status))
1844 return WINBINDD_ERROR;
1846 if (num_groups == 0) {
1847 state->response.data.num_entries = 0;
1848 state->response.extra_data.data = NULL;
1849 return WINBINDD_OK;
1852 if (!print_sidlist(state->mem_ctx,
1853 groups, num_groups,
1854 &sidstring, &len)) {
1855 DEBUG(0, ("talloc failed\n"));
1856 return WINBINDD_ERROR;
1859 state->response.extra_data.data = SMB_STRDUP(sidstring);
1860 if (!state->response.extra_data.data) {
1861 return WINBINDD_ERROR;
1863 state->response.length += len+1;
1864 state->response.data.num_entries = num_groups;
1866 return WINBINDD_OK;