TEMP: Rollup of lots of changes so they don't get lost
[Samba/vfs_proxy.git] / source3 / winbindd / winbindd_group.c
blob7432bda451f9aa84950fb3bfa64eac855b88b6fa
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 void sort_unique_list(char ***list, uint32 *n_list)
387 uint32_t i;
389 /* search for duplicates for sorting and looking for matching
390 neighbors */
392 qsort(*list, *n_list, sizeof(char*), QSORT_CAST namecmp);
394 for (i=1; i < *n_list; i++) {
395 if (strcmp((*list)[i-1], (*list)[i]) == 0) {
396 memmove(&((*list)[i-1]), &((*list)[i]),
397 sizeof(char*)*((*n_list)-i));
398 (*n_list)--;
403 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
404 char ***list, uint32 *n_list,
405 char **names, uint32 n_names )
407 char **new_list = NULL;
408 uint32 n_new_list = 0;
409 int i, j;
411 if ( !names || (n_names == 0) )
412 return NT_STATUS_OK;
414 /* Alloc the maximum size we'll need */
416 if ( *list == NULL ) {
417 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
418 return NT_STATUS_NO_MEMORY;
420 n_new_list = n_names;
421 } else {
422 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
423 (*n_list) + n_names );
424 if ( !new_list )
425 return NT_STATUS_NO_MEMORY;
426 n_new_list = (*n_list) + n_names;
429 /* Add all names */
431 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
432 new_list[i] = talloc_strdup( new_list, names[j] );
435 *list = new_list;
436 *n_list = n_new_list;
438 return NT_STATUS_OK;
441 /***********************************************************************
442 ***********************************************************************/
444 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
445 struct winbindd_domain *d,
446 DOM_SID *glist, uint32 n_glist,
447 DOM_SID **new_glist, uint32 *n_new_glist,
448 char ***members, uint32 *n_members )
450 int i, j;
451 NTSTATUS status = NT_STATUS_OK;
452 uint32 num_names = 0;
453 uint32 *name_types = NULL;
454 char **names = NULL;
455 DOM_SID *sid_mem = NULL;
456 TALLOC_CTX *tmp_ctx = NULL;
457 DOM_SID *new_groups = NULL;
458 size_t new_groups_size = 0;
460 *members = NULL;
461 *n_members = 0;
462 *new_glist = NULL;
463 *n_new_glist = 0;
465 for ( i=0; i<n_glist; i++ ) {
466 tmp_ctx = talloc_new( ctx );
468 /* Lookup the group membership */
470 status = d->methods->lookup_groupmem(d, tmp_ctx,
471 &glist[i], &num_names,
472 &sid_mem, &names,
473 &name_types);
474 if ( !NT_STATUS_IS_OK(status) )
475 goto out;
477 /* Separate users and groups into two lists */
479 for ( j=0; j<num_names; j++ ) {
481 /* Users */
482 if ( name_types[j] == SID_NAME_USER ||
483 name_types[j] == SID_NAME_COMPUTER )
485 status = add_names_to_list( ctx, members,
486 n_members,
487 names+j, 1 );
488 if ( !NT_STATUS_IS_OK(status) )
489 goto out;
491 continue;
494 /* Groups */
495 if ( name_types[j] == SID_NAME_DOM_GRP ||
496 name_types[j] == SID_NAME_ALIAS )
498 status = add_sid_to_array_unique(ctx,
499 &sid_mem[j],
500 &new_groups,
501 &new_groups_size);
502 if (!NT_STATUS_IS_OK(status)) {
503 goto out;
506 continue;
510 TALLOC_FREE( tmp_ctx );
513 *new_glist = new_groups;
514 *n_new_glist = (uint32)new_groups_size;
516 out:
517 TALLOC_FREE( tmp_ctx );
519 return status;
522 /***********************************************************************
523 Fill in the group membership field of a NT group given by group_sid
524 ***********************************************************************/
526 static bool fill_grent_mem(struct winbindd_domain *domain,
527 struct winbindd_cli_state *state,
528 DOM_SID *group_sid,
529 enum lsa_SidType group_name_type,
530 size_t *num_gr_mem, char **gr_mem,
531 size_t *gr_mem_len)
533 uint32 num_names = 0;
534 unsigned int buf_len = 0, buf_ndx = 0, i;
535 char **names = NULL, *buf = NULL;
536 bool result = False;
537 TALLOC_CTX *mem_ctx;
538 uint32 group_rid;
539 DOM_SID *glist = NULL;
540 DOM_SID *new_glist = NULL;
541 uint32 n_glist, n_new_glist;
542 int max_depth = lp_winbind_expand_groups();
544 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
545 return False;
547 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
549 /* Initialize with no members */
551 *num_gr_mem = 0;
553 /* HACK ALERT!! This whole routine does not cope with group members
554 * from more than one domain, ie aliases. Thus we have to work it out
555 * ourselves in a special routine. */
557 if (domain->internal) {
558 result = fill_passdb_alias_grmem(domain, group_sid,
559 num_gr_mem,
560 gr_mem, gr_mem_len);
561 goto done;
564 /* Verify name type */
566 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
567 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
569 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
570 sid_string_dbg(group_sid),
571 domain->name, group_name_type));
572 goto done;
575 /* OPTIMIZATION / HACK. See comment in
576 fill_grent_mem_domusers() */
578 sid_peek_rid( group_sid, &group_rid );
579 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
580 result = fill_grent_mem_domusers( mem_ctx, domain, state,
581 group_sid, group_name_type,
582 num_gr_mem, gr_mem,
583 gr_mem_len );
584 goto done;
587 /* Real work goes here. Create a list of group names to
588 expand starting with the initial one. Pass that to
589 expand_groups() which returns a list of more group names
590 to expand. Do this up to the max search depth. */
592 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
593 result = False;
594 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
595 goto done;
597 sid_copy( &glist[0], group_sid );
598 n_glist = 1;
600 for ( i=0; i<max_depth && glist; i++ ) {
601 uint32 n_members = 0;
602 char **members = NULL;
603 NTSTATUS nt_status;
604 int j;
606 nt_status = expand_groups( mem_ctx, domain,
607 glist, n_glist,
608 &new_glist, &n_new_glist,
609 &members, &n_members);
610 if ( !NT_STATUS_IS_OK(nt_status) ) {
611 result = False;
612 goto done;
615 /* Add new group members to list. Pass through the
616 alias mapping function */
618 for (j=0; j<n_members; j++) {
619 fstring name_domain, name_acct;
620 fstring qualified_name;
621 char *mapped_name = NULL;
622 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
623 struct winbindd_domain *target_domain = NULL;
625 if (parse_domain_user(members[j], name_domain, name_acct)) {
626 target_domain = find_domain_from_name_noinit(name_domain);
627 /* NOW WHAT ? */
629 if (!target_domain) {
630 target_domain = domain;
633 name_map_status = normalize_name_map(members, target_domain,
634 name_acct, &mapped_name);
636 /* Basic whitespace replacement */
637 if (NT_STATUS_IS_OK(name_map_status)) {
638 fill_domain_username(qualified_name, name_domain,
639 mapped_name, true);
640 mapped_name = qualified_name;
642 /* no mapping at all */
643 else if (!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
644 mapped_name = members[j];
647 nt_status = add_names_to_list( mem_ctx, &names,
648 &num_names,
649 &mapped_name, 1);
650 if ( !NT_STATUS_IS_OK(nt_status) ) {
651 result = False;
652 goto done;
656 TALLOC_FREE( members );
658 /* If we have no more groups to expand, break out
659 early */
661 if (new_glist == NULL)
662 break;
664 /* One more round */
665 TALLOC_FREE(glist);
666 glist = new_glist;
667 n_glist = n_new_glist;
669 TALLOC_FREE( glist );
671 sort_unique_list(&names, &num_names);
673 DEBUG(10, ("looked up %d names\n", num_names));
675 again:
676 /* Add members to list */
678 for (i = 0; i < num_names; i++) {
679 int len;
681 DEBUG(10, ("processing name %s\n", names[i]));
683 len = strlen(names[i]);
685 /* Add to list or calculate buffer length */
687 if (!buf) {
688 buf_len += len + 1; /* List is comma separated */
689 (*num_gr_mem)++;
690 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
691 } else {
692 DEBUG(10, ("appending %s at ndx %d\n",
693 names[i], buf_ndx));
694 parse_add_domuser(&buf[buf_ndx], names[i], &len);
695 buf_ndx += len;
696 buf[buf_ndx] = ',';
697 buf_ndx++;
701 /* Allocate buffer */
703 if (!buf && buf_len != 0) {
704 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
705 DEBUG(1, ("out of memory\n"));
706 result = False;
707 goto done;
709 memset(buf, 0, buf_len);
710 goto again;
713 /* Now we're done */
715 if (buf && buf_ndx > 0) {
716 buf[buf_ndx - 1] = '\0';
719 *gr_mem = buf;
720 *gr_mem_len = buf_len;
722 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
723 (unsigned int)*num_gr_mem,
724 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
725 result = True;
727 done:
729 talloc_destroy(mem_ctx);
731 DEBUG(10, ("fill_grent_mem returning %d\n", result));
733 return result;
736 static void winbindd_getgrsid(struct winbindd_cli_state *state, DOM_SID group_sid);
738 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
739 enum lsa_SidType type )
741 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
743 if (!success) {
744 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
745 request_error(state);
746 return;
749 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
750 DEBUG(5,("getgrnam_recv: not a group!\n"));
751 request_error(state);
752 return;
755 winbindd_getgrsid( state, *sid );
759 /* Return a group structure from a group name */
761 void winbindd_getgrnam(struct winbindd_cli_state *state)
763 struct winbindd_domain *domain;
764 fstring name_domain, name_group;
765 char *tmp;
766 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
768 /* Ensure null termination */
769 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
771 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
772 state->request.data.groupname));
774 nt_status = normalize_name_unmap(state->mem_ctx,
775 state->request.data.groupname,
776 &tmp);
777 /* If we didn't map anything in the above call, just reset the
778 tmp pointer to the original string */
779 if (!NT_STATUS_IS_OK(nt_status) &&
780 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
782 tmp = state->request.data.groupname;
785 /* Parse domain and groupname */
787 memset(name_group, 0, sizeof(name_group));
789 name_domain[0] = '\0';
790 name_group[0] = '\0';
792 parse_domain_user(tmp, name_domain, name_group);
794 /* if no domain or our local domain and no local tdb group, default to
795 * our local domain for aliases */
797 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
798 fstrcpy(name_domain, get_global_sam_name());
801 /* Get info for the domain */
803 if ((domain = find_domain_from_name_noinit(name_domain)) == NULL) {
804 DEBUG(3, ("could not get domain sid for domain %s\n",
805 name_domain));
806 request_error(state);
807 return;
809 /* should we deal with users for our domain? */
811 if ( lp_winbind_trusted_domains_only() && domain->primary) {
812 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
813 "getgrnam() for %s\\%s.\n", name_domain, name_group));
814 request_error(state);
815 return;
818 /* Get rid and name type from name */
820 fstrcpy( name_group, tmp );
822 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
823 getgrnam_recv, WINBINDD_GETGRNAM, state );
826 struct getgrsid_state {
827 struct winbindd_cli_state *state;
828 struct winbindd_domain *domain;
829 char *group_name;
830 enum lsa_SidType group_type;
831 uid_t gid;
832 DOM_SID group_sid;
835 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
837 struct getgrsid_state *s =
838 (struct getgrsid_state *)private_data;
839 struct winbindd_domain *domain;
840 size_t gr_mem_len;
841 size_t num_gr_mem;
842 char *gr_mem;
843 fstring dom_name, group_name;
845 if (!success) {
846 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
847 request_error(s->state);
848 return;
851 s->gid = gid;
853 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
854 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
855 request_error(s->state);
856 return;
860 /* Fill in group structure */
862 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
863 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
864 request_error(s->state);
865 return;
868 if (!fill_grent(s->state->mem_ctx, &s->state->response.data.gr,
869 dom_name, group_name, gid) ||
870 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
871 &num_gr_mem, &gr_mem, &gr_mem_len))
873 request_error(s->state);
874 return;
877 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
879 /* Group membership lives at start of extra data */
881 s->state->response.data.gr.gr_mem_ofs = 0;
883 s->state->response.length += gr_mem_len;
884 s->state->response.extra_data.data = gr_mem;
886 request_ok(s->state);
889 static void getgrsid_lookupsid_recv( void *private_data, bool success,
890 const char *dom_name, const char *name,
891 enum lsa_SidType name_type )
893 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
894 char *mapped_name = NULL;
895 fstring raw_name;
896 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
898 if (!success) {
899 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
900 request_error(s->state);
901 return;
904 /* either it's a domain group, a domain local group, or a
905 local group in an internal domain */
907 if ( !( (name_type==SID_NAME_DOM_GRP) ||
908 ((name_type==SID_NAME_ALIAS) &&
909 (s->domain->primary || s->domain->internal)) ) )
911 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
912 dom_name, name, name_type));
913 request_error(s->state);
914 return;
917 /* normalize the name and ensure that we have the DOM\name
918 coming out of here */
920 fstrcpy(raw_name, name);
922 nt_status = normalize_name_unmap(s->state->mem_ctx, raw_name,
923 &mapped_name);
925 /* basic whitespace reversal */
926 if (NT_STATUS_IS_OK(nt_status)) {
927 s->group_name = talloc_asprintf(s->state->mem_ctx,
928 "%s%c%s",
929 dom_name,
930 *lp_winbind_separator(),
931 mapped_name);
933 /* mapped from alias */
934 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
935 s->group_name = mapped_name;
937 /* no mapping at all. use original string */
938 else {
939 s->group_name = talloc_asprintf(s->state->mem_ctx,
940 "%s%c%s",
941 dom_name,
942 *lp_winbind_separator(),
943 raw_name);
946 if (s->group_name == NULL) {
947 DEBUG(1, ("getgrsid_lookupsid_recv: group_name is NULL!\n"));
948 request_error(s->state);
949 return;
952 s->group_type = name_type;
954 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
955 getgrsid_sid2gid_recv, s);
958 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
960 struct getgrsid_state *s;
962 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
963 DEBUG(0, ("talloc failed\n"));
964 request_error(state);
965 return;
968 s->state = state;
970 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
971 DEBUG(3, ("Could not find domain for sid %s\n",
972 sid_string_dbg(&group_sid)));
973 request_error(state);
974 return;
977 sid_copy(&s->group_sid, &group_sid);
979 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
980 getgrsid_lookupsid_recv, s );
984 static void getgrgid_recv(void *private_data, bool success, const char *sid)
986 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
987 enum lsa_SidType name_type;
988 DOM_SID group_sid;
990 if (success) {
991 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
992 (unsigned long)(state->request.data.gid), sid));
994 string_to_sid(&group_sid, sid);
995 winbindd_getgrsid(state, group_sid);
996 return;
999 /* Ok, this might be "ours", i.e. an alias */
1000 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
1001 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
1002 (name_type == SID_NAME_ALIAS)) {
1003 /* Hey, got an alias */
1004 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
1005 (unsigned long)(state->request.data.gid), sid));
1006 winbindd_getgrsid(state, group_sid);
1007 return;
1010 DEBUG(1, ("could not convert gid %lu to sid\n",
1011 (unsigned long)state->request.data.gid));
1012 request_error(state);
1015 /* Return a group structure from a gid number */
1016 void winbindd_getgrgid(struct winbindd_cli_state *state)
1018 gid_t gid = state->request.data.gid;
1020 DEBUG(3, ("[%5lu]: getgrgid %lu\n",
1021 (unsigned long)state->pid,
1022 (unsigned long)gid));
1024 /* always use the async interface */
1025 winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
1029 * set/get/endgrent functions
1032 /* "Rewind" file pointer for group database enumeration */
1034 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
1036 struct winbindd_domain *domain;
1038 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
1040 /* Check user has enabled this */
1042 if (!lp_winbind_enum_groups()) {
1043 return False;
1046 /* Free old static data if it exists */
1048 if (state->getgrent_state != NULL) {
1049 free_getent_state(state->getgrent_state);
1050 state->getgrent_state = NULL;
1053 /* Create sam pipes for each domain we know about */
1055 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1056 struct getent_state *domain_state;
1058 /* Create a state record for this domain */
1060 /* don't add our domaina if we are a PDC or if we
1061 are a member of a Samba domain */
1063 if ( lp_winbind_trusted_domains_only() && domain->primary )
1065 continue;
1068 domain_state = SMB_MALLOC_P(struct getent_state);
1069 if (!domain_state) {
1070 DEBUG(1, ("winbindd_setgrent: "
1071 "malloc failed for domain_state!\n"));
1072 return False;
1075 ZERO_STRUCTP(domain_state);
1077 fstrcpy(domain_state->domain_name, domain->name);
1079 /* Add to list of open domains */
1081 DLIST_ADD(state->getgrent_state, domain_state);
1084 state->getgrent_initialized = True;
1085 return True;
1088 void winbindd_setgrent(struct winbindd_cli_state *state)
1090 if (winbindd_setgrent_internal(state)) {
1091 request_ok(state);
1092 } else {
1093 request_error(state);
1097 /* Close file pointer to ntdom group database */
1099 void winbindd_endgrent(struct winbindd_cli_state *state)
1101 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
1103 free_getent_state(state->getgrent_state);
1104 state->getgrent_initialized = False;
1105 state->getgrent_state = NULL;
1106 request_ok(state);
1109 /* Get the list of domain groups and domain aliases for a domain. We fill in
1110 the sam_entries and num_sam_entries fields with domain group information.
1111 Return True if some groups were returned, False otherwise. */
1113 bool get_sam_group_entries(struct getent_state *ent)
1115 NTSTATUS status;
1116 uint32 num_entries;
1117 struct acct_info *name_list = NULL;
1118 TALLOC_CTX *mem_ctx;
1119 bool result = False;
1120 struct acct_info *sam_grp_entries = NULL;
1121 struct winbindd_domain *domain;
1123 if (ent->got_sam_entries)
1124 return False;
1126 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1127 ent->domain_name))) {
1128 DEBUG(1, ("get_sam_group_entries: "
1129 "could not create talloc context!\n"));
1130 return False;
1133 /* Free any existing group info */
1135 SAFE_FREE(ent->sam_entries);
1136 ent->num_sam_entries = 0;
1137 ent->got_sam_entries = True;
1139 /* Enumerate domain groups */
1141 num_entries = 0;
1143 if (!(domain = find_domain_from_name(ent->domain_name))) {
1144 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1145 ent->domain_name));
1146 goto done;
1149 /* always get the domain global groups */
1151 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1152 &sam_grp_entries);
1154 if (!NT_STATUS_IS_OK(status)) {
1155 DEBUG(3, ("get_sam_group_entries: "
1156 "could not enumerate domain groups! Error: %s\n",
1157 nt_errstr(status)));
1158 result = False;
1159 goto done;
1162 /* Copy entries into return buffer */
1164 if (num_entries) {
1165 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1166 if (!name_list) {
1167 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1168 "memory for %d domain groups!\n",
1169 num_entries));
1170 result = False;
1171 goto done;
1173 memcpy(name_list, sam_grp_entries,
1174 num_entries * sizeof(struct acct_info));
1177 ent->num_sam_entries = num_entries;
1179 /* get the domain local groups if we are a member of a native win2k
1180 * domain and are not using LDAP to get the groups */
1182 if ( ( lp_security() != SEC_ADS && domain->native_mode
1183 && domain->primary) || domain->internal )
1185 DEBUG(4,("get_sam_group_entries: %s domain; "
1186 "enumerating local groups as well\n",
1187 domain->native_mode ? "Native Mode 2k":
1188 "BUILTIN or local"));
1190 status = domain->methods->enum_local_groups(domain, mem_ctx,
1191 &num_entries,
1192 &sam_grp_entries);
1194 if ( !NT_STATUS_IS_OK(status) ) {
1195 DEBUG(3,("get_sam_group_entries: "
1196 "Failed to enumerate "
1197 "domain local groups with error %s!\n",
1198 nt_errstr(status)));
1199 num_entries = 0;
1201 else
1202 DEBUG(4,("get_sam_group_entries: "
1203 "Returned %d local groups\n",
1204 num_entries));
1206 /* Copy entries into return buffer */
1208 if ( num_entries ) {
1209 name_list = SMB_REALLOC_ARRAY(name_list,
1210 struct acct_info,
1211 ent->num_sam_entries+
1212 num_entries);
1213 if (!name_list) {
1214 DEBUG(0,("get_sam_group_entries: "
1215 "Failed to realloc more memory "
1216 "for %d local groups!\n",
1217 num_entries));
1218 result = False;
1219 goto done;
1222 memcpy(&name_list[ent->num_sam_entries],
1223 sam_grp_entries,
1224 num_entries * sizeof(struct acct_info));
1227 ent->num_sam_entries += num_entries;
1231 /* Fill in remaining fields */
1233 ent->sam_entries = name_list;
1234 ent->sam_entry_index = 0;
1236 result = (ent->num_sam_entries > 0);
1238 done:
1239 talloc_destroy(mem_ctx);
1241 return result;
1244 /* Fetch next group entry from ntdom database */
1246 #define MAX_GETGRENT_GROUPS 500
1248 void winbindd_getgrent(struct winbindd_cli_state *state)
1250 struct getent_state *ent;
1251 struct winbindd_gr *group_list = NULL;
1252 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1253 char *gr_mem_list = NULL;
1255 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1257 /* Check user has enabled this */
1259 if (!lp_winbind_enum_groups()) {
1260 request_error(state);
1261 return;
1264 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1266 if (num_groups == 0) {
1267 request_error(state);
1268 return;
1271 group_list = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups);
1272 if (!group_list) {
1273 request_error(state);
1274 return;
1276 /* will be freed by process_request() */
1277 state->response.extra_data.data = group_list;
1279 memset(state->response.extra_data.data, '\0',
1280 num_groups * sizeof(struct winbindd_gr) );
1282 state->response.data.num_entries = 0;
1284 if (!state->getgrent_initialized)
1285 winbindd_setgrent_internal(state);
1287 if (!(ent = state->getgrent_state)) {
1288 request_error(state);
1289 return;
1292 /* Start sending back groups */
1294 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1295 struct acct_info *name_list = NULL;
1296 fstring domain_group_name;
1297 uint32 result;
1298 gid_t group_gid;
1299 size_t gr_mem_len;
1300 char *gr_mem;
1301 DOM_SID group_sid;
1302 struct winbindd_domain *domain;
1304 /* Do we need to fetch another chunk of groups? */
1306 tryagain:
1308 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1309 ent->sam_entry_index, ent->num_sam_entries));
1311 if (ent->num_sam_entries == ent->sam_entry_index) {
1313 while(ent && !get_sam_group_entries(ent)) {
1314 struct getent_state *next_ent;
1316 DEBUG(10, ("freeing state info for domain %s\n",
1317 ent->domain_name));
1319 /* Free state information for this domain */
1321 SAFE_FREE(ent->sam_entries);
1323 next_ent = ent->next;
1324 DLIST_REMOVE(state->getgrent_state, ent);
1326 SAFE_FREE(ent);
1327 ent = next_ent;
1330 /* No more domains */
1332 if (!ent)
1333 break;
1336 name_list = (struct acct_info *)ent->sam_entries;
1338 if (!(domain = find_domain_from_name(ent->domain_name))) {
1339 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1340 ent->domain_name));
1341 result = False;
1342 goto done;
1345 /* Lookup group info */
1347 sid_copy(&group_sid, &domain->sid);
1348 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1350 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->name, &group_sid,
1351 &group_gid))) {
1352 union unid_t id;
1353 enum lsa_SidType type;
1355 DEBUG(10, ("SID %s not in idmap\n",
1356 sid_string_dbg(&group_sid)));
1358 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1359 DEBUG(1,("could not look up gid for group %s\n",
1360 name_list[ent->sam_entry_index].acct_name));
1361 ent->sam_entry_index++;
1362 goto tryagain;
1365 if ((type != SID_NAME_DOM_GRP) &&
1366 (type != SID_NAME_ALIAS) &&
1367 (type != SID_NAME_WKN_GRP)) {
1368 DEBUG(1, ("Group %s is a %s, not a group\n",
1369 sid_type_lookup(type),
1370 name_list[ent->sam_entry_index].acct_name));
1371 ent->sam_entry_index++;
1372 goto tryagain;
1374 group_gid = id.gid;
1377 DEBUG(10, ("got gid %lu for group %lu\n",
1378 (unsigned long)group_gid,
1379 (unsigned long)name_list[ent->sam_entry_index].rid));
1381 /* Fill in group entry */
1383 fill_domain_username(domain_group_name, ent->domain_name,
1384 name_list[ent->sam_entry_index].acct_name, True);
1386 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1387 ent->domain_name,
1388 name_list[ent->sam_entry_index].acct_name,
1389 group_gid);
1391 /* Fill in group membership entry */
1393 if (result) {
1394 size_t num_gr_mem = 0;
1395 DOM_SID member_sid;
1396 group_list[group_list_ndx].num_gr_mem = 0;
1397 gr_mem = NULL;
1398 gr_mem_len = 0;
1400 /* Get group membership */
1401 if (state->request.cmd == WINBINDD_GETGRLST) {
1402 result = True;
1403 } else {
1404 sid_copy(&member_sid, &domain->sid);
1405 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1406 result = fill_grent_mem(
1407 domain,
1408 NULL,
1409 &member_sid,
1410 SID_NAME_DOM_GRP,
1411 &num_gr_mem,
1412 &gr_mem, &gr_mem_len);
1414 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1418 if (result) {
1419 /* Append to group membership list */
1420 gr_mem_list = (char *)SMB_REALLOC(
1421 gr_mem_list, gr_mem_list_len + gr_mem_len);
1423 if (!gr_mem_list &&
1424 (group_list[group_list_ndx].num_gr_mem != 0)) {
1425 DEBUG(0, ("out of memory\n"));
1426 gr_mem_list_len = 0;
1427 break;
1430 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1431 gr_mem_list_len, (unsigned int)gr_mem_len));
1433 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1434 gr_mem_len);
1436 SAFE_FREE(gr_mem);
1438 group_list[group_list_ndx].gr_mem_ofs =
1439 gr_mem_list_len;
1441 gr_mem_list_len += gr_mem_len;
1444 ent->sam_entry_index++;
1446 /* Add group to return list */
1448 if (result) {
1450 DEBUG(10, ("adding group num_entries = %d\n",
1451 state->response.data.num_entries));
1453 group_list_ndx++;
1454 state->response.data.num_entries++;
1456 state->response.length +=
1457 sizeof(struct winbindd_gr);
1459 } else {
1460 DEBUG(0, ("could not lookup domain group %s\n",
1461 domain_group_name));
1465 /* Copy the list of group memberships to the end of the extra data */
1467 if (group_list_ndx == 0)
1468 goto done;
1470 state->response.extra_data.data = SMB_REALLOC(
1471 state->response.extra_data.data,
1472 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1474 if (!state->response.extra_data.data) {
1475 DEBUG(0, ("out of memory\n"));
1476 group_list_ndx = 0;
1477 SAFE_FREE(gr_mem_list);
1478 request_error(state);
1479 return;
1482 memcpy(&((char *)state->response.extra_data.data)
1483 [group_list_ndx * sizeof(struct winbindd_gr)],
1484 gr_mem_list, gr_mem_list_len);
1486 state->response.length += gr_mem_list_len;
1488 DEBUG(10, ("returning %d groups, length = %d\n",
1489 group_list_ndx, gr_mem_list_len));
1491 /* Out of domains */
1493 done:
1495 SAFE_FREE(gr_mem_list);
1497 if (group_list_ndx > 0)
1498 request_ok(state);
1499 else
1500 request_error(state);
1503 /* List domain groups without mapping to unix ids */
1504 void winbindd_list_groups(struct winbindd_cli_state *state)
1506 winbindd_list_ent(state, LIST_GROUPS);
1509 /* Get user supplementary groups. This is much quicker than trying to
1510 invert the groups database. We merge the groups from the gids and
1511 other_sids info3 fields as trusted domain, universal group
1512 memberships, and nested groups (win2k native mode only) are not
1513 returned by the getgroups RPC call but are present in the info3. */
1515 struct getgroups_state {
1516 struct winbindd_cli_state *state;
1517 struct winbindd_domain *domain;
1518 char *domname;
1519 char *username;
1520 DOM_SID user_sid;
1522 const DOM_SID *token_sids;
1523 size_t i, num_token_sids;
1525 gid_t *token_gids;
1526 size_t num_token_gids;
1529 static void getgroups_usersid_recv(void *private_data, bool success,
1530 const DOM_SID *sid, enum lsa_SidType type);
1531 static void getgroups_tokensids_recv(void *private_data, bool success,
1532 DOM_SID *token_sids, size_t num_token_sids);
1533 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1535 void winbindd_getgroups(struct winbindd_cli_state *state)
1537 struct getgroups_state *s;
1538 char *real_name = NULL;
1539 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1541 /* Ensure null termination */
1542 state->request.data.username
1543 [sizeof(state->request.data.username)-1]='\0';
1545 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1546 state->request.data.username));
1548 /* Parse domain and username */
1550 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1551 if (s == NULL) {
1552 DEBUG(0, ("talloc failed\n"));
1553 request_error(state);
1554 return;
1557 s->state = state;
1559 nt_status = normalize_name_unmap(state->mem_ctx,
1560 state->request.data.username,
1561 &real_name);
1563 /* Reset the real_name pointer if we didn't do anything
1564 productive in the above call */
1565 if (!NT_STATUS_IS_OK(nt_status) &&
1566 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
1568 real_name = state->request.data.username;
1571 if (!parse_domain_user_talloc(state->mem_ctx, real_name,
1572 &s->domname, &s->username)) {
1573 DEBUG(5, ("Could not parse domain user: %s\n",
1574 real_name));
1576 /* error out if we do not have nested group support */
1578 if ( !lp_winbind_nested_groups() ) {
1579 request_error(state);
1580 return;
1583 s->domname = talloc_strdup(state->mem_ctx,
1584 get_global_sam_name());
1585 s->username = talloc_strdup(state->mem_ctx,
1586 state->request.data.username);
1589 /* Get info for the domain (either by short domain name or
1590 DNS name in the case of a UPN) */
1592 s->domain = find_domain_from_name_noinit(s->domname);
1593 if (!s->domain) {
1594 char *p = strchr(s->username, '@');
1596 if (p) {
1597 s->domain = find_domain_from_name_noinit(p+1);
1602 if (s->domain == NULL) {
1603 DEBUG(7, ("could not find domain entry for domain %s\n",
1604 s->domname));
1605 request_error(state);
1606 return;
1609 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1610 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1611 "getgroups() for %s\\%s.\n", s->domname,
1612 s->username));
1613 request_error(state);
1614 return;
1617 /* Get rid and name type from name. The following costs 1 packet */
1619 winbindd_lookupname_async(state->mem_ctx,
1620 s->domname, s->username,
1621 getgroups_usersid_recv,
1622 WINBINDD_GETGROUPS, s);
1625 static void getgroups_usersid_recv(void *private_data, bool success,
1626 const DOM_SID *sid, enum lsa_SidType type)
1628 struct getgroups_state *s =
1629 (struct getgroups_state *)private_data;
1631 if ((!success) ||
1632 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1633 request_error(s->state);
1634 return;
1637 sid_copy(&s->user_sid, sid);
1639 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1640 getgroups_tokensids_recv, s);
1643 static void getgroups_tokensids_recv(void *private_data, bool success,
1644 DOM_SID *token_sids, size_t num_token_sids)
1646 struct getgroups_state *s =
1647 (struct getgroups_state *)private_data;
1649 /* We need at least the user sid and the primary group in the token,
1650 * otherwise it's an error */
1652 if ((!success) || (num_token_sids < 2)) {
1653 request_error(s->state);
1654 return;
1657 s->token_sids = token_sids;
1658 s->num_token_sids = num_token_sids;
1659 s->i = 0;
1661 s->token_gids = NULL;
1662 s->num_token_gids = 0;
1664 getgroups_sid2gid_recv(s, False, 0);
1667 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1669 struct getgroups_state *s =
1670 (struct getgroups_state *)private_data;
1672 if (success) {
1673 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1674 &s->token_gids,
1675 &s->num_token_gids)) {
1676 return;
1680 if (s->i < s->num_token_sids) {
1681 const DOM_SID *sid = &s->token_sids[s->i];
1682 s->i += 1;
1684 if (sid_equal(sid, &s->user_sid)) {
1685 getgroups_sid2gid_recv(s, False, 0);
1686 return;
1689 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1690 getgroups_sid2gid_recv, s);
1691 return;
1694 s->state->response.data.num_entries = s->num_token_gids;
1695 if (s->num_token_gids) {
1696 /* s->token_gids are talloced */
1697 s->state->response.extra_data.data =
1698 smb_xmemdup(s->token_gids,
1699 s->num_token_gids * sizeof(gid_t));
1700 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1702 request_ok(s->state);
1705 /* Get user supplementary sids. This is equivalent to the
1706 winbindd_getgroups() function but it involves a SID->SIDs mapping
1707 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1708 idmap. This call is designed to be used with applications that need
1709 to do ACL evaluation themselves. Note that the cached info3 data is
1710 not used
1712 this function assumes that the SID that comes in is a user SID. If
1713 you pass in another type of SID then you may get unpredictable
1714 results.
1717 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1718 size_t num_sids);
1720 void winbindd_getusersids(struct winbindd_cli_state *state)
1722 DOM_SID *user_sid;
1724 /* Ensure null termination */
1725 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1727 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1728 if (user_sid == NULL) {
1729 DEBUG(1, ("talloc failed\n"));
1730 request_error(state);
1731 return;
1734 if (!string_to_sid(user_sid, state->request.data.sid)) {
1735 DEBUG(1, ("Could not get convert sid %s from string\n",
1736 state->request.data.sid));
1737 request_error(state);
1738 return;
1741 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1742 state);
1745 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1746 size_t num_sids)
1748 struct winbindd_cli_state *state =
1749 (struct winbindd_cli_state *)private_data;
1750 char *ret = NULL;
1751 unsigned ofs, ret_size = 0;
1752 size_t i;
1754 if (!success) {
1755 request_error(state);
1756 return;
1759 /* work out the response size */
1760 for (i = 0; i < num_sids; i++) {
1761 fstring s;
1762 sid_to_fstring(s, &sids[i]);
1763 ret_size += strlen(s) + 1;
1766 /* build the reply */
1767 ret = (char *)SMB_MALLOC(ret_size);
1768 if (!ret) {
1769 DEBUG(0, ("malloc failed\n"));
1770 request_error(state);
1771 return;
1773 ofs = 0;
1774 for (i = 0; i < num_sids; i++) {
1775 fstring s;
1776 sid_to_fstring(s, &sids[i]);
1777 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1778 ofs += strlen(ret+ofs) + 1;
1781 /* Send data back to client */
1782 state->response.data.num_entries = num_sids;
1783 state->response.extra_data.data = ret;
1784 state->response.length += ret_size;
1785 request_ok(state);
1788 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1790 DOM_SID user_sid;
1791 struct winbindd_domain *domain;
1793 /* Ensure null termination */
1794 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1796 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1797 DEBUG(1, ("Could not get convert sid %s from string\n",
1798 state->request.data.sid));
1799 request_error(state);
1800 return;
1803 /* Get info for the domain */
1804 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1805 DEBUG(0,("could not find domain entry for sid %s\n",
1806 sid_string_dbg(&user_sid)));
1807 request_error(state);
1808 return;
1811 sendto_domain(state, domain);
1814 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1815 struct winbindd_cli_state *state)
1817 DOM_SID user_sid;
1818 NTSTATUS status;
1820 char *sidstring;
1821 ssize_t len;
1822 DOM_SID *groups;
1823 uint32 num_groups;
1825 /* Ensure null termination */
1826 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1828 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1829 DEBUG(1, ("Could not get convert sid %s from string\n",
1830 state->request.data.sid));
1831 return WINBINDD_ERROR;
1834 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1835 &user_sid, &num_groups,
1836 &groups);
1837 if (!NT_STATUS_IS_OK(status))
1838 return WINBINDD_ERROR;
1840 if (num_groups == 0) {
1841 state->response.data.num_entries = 0;
1842 state->response.extra_data.data = NULL;
1843 return WINBINDD_OK;
1846 if (!print_sidlist(state->mem_ctx,
1847 groups, num_groups,
1848 &sidstring, &len)) {
1849 DEBUG(0, ("talloc failed\n"));
1850 return WINBINDD_ERROR;
1853 state->response.extra_data.data = SMB_STRDUP(sidstring);
1854 if (!state->response.extra_data.data) {
1855 return WINBINDD_ERROR;
1857 state->response.length += len+1;
1858 state->response.data.num_entries = num_groups;
1860 return WINBINDD_OK;