winbindd: Update the calls to ws_name_XX() to reflect API changes.
[Samba.git] / source / winbindd / winbindd_group.c
blob088f946877824093a2c6896a795411e43b701cd8
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
9 Copyright (C) Volker Lendecke 2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "includes.h"
26 #include "winbindd.h"
28 extern bool opt_nocache;
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 static void add_member(const char *domain, const char *user,
34 char **pp_members, size_t *p_num_members)
36 fstring name;
38 fill_domain_username(name, domain, user, True);
39 safe_strcat(name, ",", sizeof(name)-1);
40 string_append(pp_members, name);
41 *p_num_members += 1;
44 /**********************************************************************
45 Add member users resulting from sid. Expand if it is a domain group.
46 **********************************************************************/
48 static void add_expanded_sid(const DOM_SID *sid,
49 char **pp_members,
50 size_t *p_num_members)
52 DOM_SID dom_sid;
53 uint32 rid;
54 struct winbindd_domain *domain;
55 size_t i;
57 char *domain_name = NULL;
58 char *name = NULL;
59 enum lsa_SidType type;
61 uint32 num_names;
62 DOM_SID *sid_mem;
63 char **names;
64 uint32 *types;
66 NTSTATUS result;
68 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
70 if (mem_ctx == NULL) {
71 DEBUG(1, ("talloc_init failed\n"));
72 return;
75 sid_copy(&dom_sid, sid);
76 sid_split_rid(&dom_sid, &rid);
78 domain = find_lookup_domain_from_sid(sid);
80 if (domain == NULL) {
81 DEBUG(3, ("Could not find domain for sid %s\n",
82 sid_string_dbg(sid)));
83 goto done;
86 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
87 &domain_name, &name, &type);
89 if (!NT_STATUS_IS_OK(result)) {
90 DEBUG(3, ("sid_to_name failed for sid %s\n",
91 sid_string_dbg(sid)));
92 goto done;
95 DEBUG(10, ("Found name %s, type %d\n", name, type));
97 if (type == SID_NAME_USER) {
98 add_member(domain_name, name, pp_members, p_num_members);
99 goto done;
102 if (type != SID_NAME_DOM_GRP) {
103 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
104 name));
105 goto done;
108 /* Expand the domain group, this must be done via the target domain */
110 domain = find_domain_from_sid(sid);
112 if (domain == NULL) {
113 DEBUG(3, ("Could not find domain from SID %s\n",
114 sid_string_dbg(sid)));
115 goto done;
118 result = domain->methods->lookup_groupmem(domain, mem_ctx,
119 sid, &num_names,
120 &sid_mem, &names,
121 &types);
123 if (!NT_STATUS_IS_OK(result)) {
124 DEBUG(10, ("Could not lookup group members for %s: %s\n",
125 name, nt_errstr(result)));
126 goto done;
129 for (i=0; i<num_names; i++) {
130 DEBUG(10, ("Adding group member SID %s\n",
131 sid_string_dbg(&sid_mem[i])));
133 if (types[i] != SID_NAME_USER) {
134 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
135 "Ignoring.\n", names[i], name));
136 continue;
139 add_member(domain->name, names[i], pp_members, p_num_members);
142 done:
143 talloc_destroy(mem_ctx);
144 return;
147 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
148 DOM_SID *group_sid, size_t *num_gr_mem,
149 char **gr_mem, size_t *gr_mem_len)
151 DOM_SID *members;
152 size_t i, num_members;
154 *num_gr_mem = 0;
155 *gr_mem = NULL;
156 *gr_mem_len = 0;
158 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
159 &num_members)))
160 return True;
162 for (i=0; i<num_members; i++) {
163 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
166 TALLOC_FREE(members);
168 if (*gr_mem != NULL) {
169 size_t len;
171 /* We have at least one member, strip off the last "," */
172 len = strlen(*gr_mem);
173 (*gr_mem)[len-1] = '\0';
174 *gr_mem_len = len;
177 return True;
180 /* Fill a grent structure from various other information */
182 static bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
183 const char *dom_name,
184 char *gr_name, gid_t unix_gid)
186 fstring full_group_name;
187 char *mapped_name = NULL;
188 struct winbindd_domain *domain = find_domain_from_name_noinit(dom_name);
189 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
191 nt_status = normalize_name_map(mem_ctx, domain, gr_name,
192 &mapped_name);
194 /* Basic whitespace replacement */
195 if (NT_STATUS_IS_OK(nt_status)) {
196 fill_domain_username(full_group_name, dom_name,
197 mapped_name, true);
199 /* Mapped to an aliase */
200 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
201 fstrcpy(full_group_name, mapped_name);
203 /* no change */
204 else {
205 fill_domain_username( full_group_name, dom_name,
206 gr_name, True );
209 gr->gr_gid = unix_gid;
211 /* Group name and password */
213 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
214 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
216 return True;
219 /***********************************************************************
220 If "enum users" is set to false, and the group being looked
221 up is the Domain Users SID: S-1-5-domain-513, then for the
222 list of members check if the querying user is in that group,
223 and if so only return that user as the gr_mem array.
224 We can change this to a different parameter than "enum users"
225 if neccessaey, or parameterize the group list we do this for.
226 ***********************************************************************/
228 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
229 struct winbindd_domain *domain,
230 struct winbindd_cli_state *state,
231 DOM_SID *group_sid,
232 enum lsa_SidType group_name_type,
233 size_t *num_gr_mem, char **gr_mem,
234 size_t *gr_mem_len)
236 DOM_SID querying_user_sid;
237 DOM_SID *pquerying_user_sid = NULL;
238 uint32 num_groups = 0;
239 DOM_SID *user_sids = NULL;
240 bool u_in_group = False;
241 NTSTATUS status;
242 int i;
243 unsigned int buf_len = 0;
244 char *buf = NULL;
246 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
247 domain->name ));
249 if (state) {
250 uid_t ret_uid = (uid_t)-1;
251 if (sys_getpeereid(state->sock, &ret_uid)==0) {
252 /* We know who's asking - look up their SID if
253 it's one we've mapped before. */
254 status = idmap_uid_to_sid(domain->name,
255 &querying_user_sid, ret_uid);
256 if (NT_STATUS_IS_OK(status)) {
257 pquerying_user_sid = &querying_user_sid;
258 DEBUG(10,("fill_grent_mem_domain_users: "
259 "querying uid %u -> %s\n",
260 (unsigned int)ret_uid,
261 sid_string_dbg(pquerying_user_sid)));
266 /* Only look up if it was a winbindd user in this domain. */
267 if (pquerying_user_sid &&
268 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
270 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
271 sid_string_dbg(pquerying_user_sid) ));
273 status = domain->methods->lookup_usergroups(domain,
274 mem_ctx,
275 pquerying_user_sid,
276 &num_groups,
277 &user_sids);
278 if (!NT_STATUS_IS_OK(status)) {
279 DEBUG(1, ("fill_grent_mem_domain_users: "
280 "lookup_usergroups failed "
281 "for sid %s in domain %s (error: %s)\n",
282 sid_string_dbg(pquerying_user_sid),
283 domain->name,
284 nt_errstr(status)));
285 return False;
288 for (i = 0; i < num_groups; i++) {
289 if (sid_equal(group_sid, &user_sids[i])) {
290 /* User is in Domain Users, add their name
291 as the only group member. */
292 u_in_group = True;
293 break;
298 if (u_in_group) {
299 size_t len = 0;
300 char *domainname = NULL;
301 char *username = NULL;
302 fstring name;
303 char *mapped_name = NULL;
304 enum lsa_SidType type;
305 struct winbindd_domain *target_domain = NULL;
306 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
308 DEBUG(10,("fill_grent_mem_domain_users: "
309 "sid %s in 'Domain Users' in domain %s\n",
310 sid_string_dbg(pquerying_user_sid),
311 domain->name ));
313 status = domain->methods->sid_to_name(domain, mem_ctx,
314 pquerying_user_sid,
315 &domainname,
316 &username,
317 &type);
318 if (!NT_STATUS_IS_OK(status)) {
319 DEBUG(1, ("could not lookup username for user "
320 "sid %s in domain %s (error: %s)\n",
321 sid_string_dbg(pquerying_user_sid),
322 domain->name,
323 nt_errstr(status)));
324 return False;
327 target_domain = find_domain_from_name_noinit(domainname);
328 name_map_status = normalize_name_map(mem_ctx, target_domain,
329 username, &mapped_name);
331 /* Basic whitespace replacement */
332 if (NT_STATUS_IS_OK(name_map_status)) {
333 fill_domain_username(name, domainname, mapped_name, true);
335 /* Mapped to an alias */
336 else if (NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
337 fstrcpy(name, mapped_name);
339 /* no mapping done...use original name */
340 else {
341 fill_domain_username(name, domainname, username, true);
344 len = strlen(name);
345 buf_len = len + 1;
346 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
347 DEBUG(1, ("out of memory\n"));
348 return False;
350 memcpy(buf, name, buf_len);
352 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
353 "'Domain Users' in domain %s\n",
354 name, domain->name ));
356 /* user is the only member */
357 *num_gr_mem = 1;
360 *gr_mem = buf;
361 *gr_mem_len = buf_len;
363 DEBUG(10, ("fill_grent_mem_domain_users: "
364 "num_mem = %u, len = %u, mem = %s\n",
365 (unsigned int)*num_gr_mem,
366 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
368 return True;
371 /***********************************************************************
372 Add names to a list. Assumes a canonical version of the string
373 in DOMAIN\user
374 ***********************************************************************/
376 static int namecmp( const void *a, const void *b )
378 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
381 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
382 char ***list, uint32 *n_list,
383 char **names, uint32 n_names )
385 char **new_list = NULL;
386 uint32 n_new_list = 0;
387 int i, j;
389 if ( !names || (n_names == 0) )
390 return NT_STATUS_OK;
392 /* Alloc the maximum size we'll need */
394 if ( *list == NULL ) {
395 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
396 return NT_STATUS_NO_MEMORY;
398 n_new_list = n_names;
399 } else {
400 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
401 (*n_list) + n_names );
402 if ( !new_list )
403 return NT_STATUS_NO_MEMORY;
404 n_new_list = (*n_list) + n_names;
407 /* Add all names */
409 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
410 new_list[i] = talloc_strdup( new_list, names[j] );
413 /* search for duplicates for sorting and looking for matching
414 neighbors */
416 qsort( new_list, n_new_list, sizeof(char*), QSORT_CAST namecmp );
418 for ( i=1; i<n_new_list; i++ ) {
419 if ( strcmp( new_list[i-1], new_list[i] ) == 0 ) {
420 memmove( &new_list[i-1], &new_list[i],
421 sizeof(char*)*(n_new_list-i) );
422 n_new_list--;
426 *list = new_list;
427 *n_list = n_new_list;
429 return NT_STATUS_OK;
432 /***********************************************************************
433 ***********************************************************************/
435 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
436 struct winbindd_domain *d,
437 DOM_SID *glist, uint32 n_glist,
438 DOM_SID **new_glist, uint32 *n_new_glist,
439 char ***members, uint32 *n_members )
441 int i, j;
442 NTSTATUS status = NT_STATUS_OK;
443 uint32 num_names = 0;
444 uint32 *name_types = NULL;
445 char **names = NULL;
446 DOM_SID *sid_mem = NULL;
447 TALLOC_CTX *tmp_ctx = NULL;
448 DOM_SID *new_groups = NULL;
449 size_t new_groups_size = 0;
451 *members = NULL;
452 *n_members = 0;
453 *new_glist = NULL;
454 *n_new_glist = 0;
456 for ( i=0; i<n_glist; i++ ) {
457 tmp_ctx = talloc_new( ctx );
459 /* Lookup the group membership */
461 status = d->methods->lookup_groupmem(d, tmp_ctx,
462 &glist[i], &num_names,
463 &sid_mem, &names,
464 &name_types);
465 if ( !NT_STATUS_IS_OK(status) )
466 goto out;
468 /* Separate users and groups into two lists */
470 for ( j=0; j<num_names; j++ ) {
472 /* Users */
473 if ( name_types[j] == SID_NAME_USER ||
474 name_types[j] == SID_NAME_COMPUTER )
476 status = add_names_to_list( ctx, members,
477 n_members,
478 names+j, 1 );
479 if ( !NT_STATUS_IS_OK(status) )
480 goto out;
482 continue;
485 /* Groups */
486 if ( name_types[j] == SID_NAME_DOM_GRP ||
487 name_types[j] == SID_NAME_ALIAS )
489 status = add_sid_to_array_unique(ctx,
490 &sid_mem[j],
491 &new_groups,
492 &new_groups_size);
493 if (!NT_STATUS_IS_OK(status)) {
494 goto out;
497 continue;
501 TALLOC_FREE( tmp_ctx );
504 *new_glist = new_groups;
505 *n_new_glist = (uint32)new_groups_size;
507 out:
508 TALLOC_FREE( tmp_ctx );
510 return status;
513 /***********************************************************************
514 Fill in the group membership field of a NT group given by group_sid
515 ***********************************************************************/
517 static bool fill_grent_mem(struct winbindd_domain *domain,
518 struct winbindd_cli_state *state,
519 DOM_SID *group_sid,
520 enum lsa_SidType group_name_type,
521 size_t *num_gr_mem, char **gr_mem,
522 size_t *gr_mem_len)
524 uint32 num_names = 0;
525 unsigned int buf_len = 0, buf_ndx = 0, i;
526 char **names = NULL, *buf = NULL;
527 bool result = False;
528 TALLOC_CTX *mem_ctx;
529 uint32 group_rid;
530 DOM_SID *glist = NULL;
531 DOM_SID *new_glist = NULL;
532 uint32 n_glist, n_new_glist;
533 int max_depth = lp_winbind_expand_groups();
535 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
536 return False;
538 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
540 /* Initialize with no members */
542 *num_gr_mem = 0;
544 /* HACK ALERT!! This whole routine does not cope with group members
545 * from more than one domain, ie aliases. Thus we have to work it out
546 * ourselves in a special routine. */
548 if (domain->internal) {
549 result = fill_passdb_alias_grmem(domain, group_sid,
550 num_gr_mem,
551 gr_mem, gr_mem_len);
552 goto done;
555 /* Verify name type */
557 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
558 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
560 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
561 sid_string_dbg(group_sid),
562 domain->name, group_name_type));
563 goto done;
566 /* OPTIMIZATION / HACK. See comment in
567 fill_grent_mem_domusers() */
569 sid_peek_rid( group_sid, &group_rid );
570 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
571 result = fill_grent_mem_domusers( mem_ctx, domain, state,
572 group_sid, group_name_type,
573 num_gr_mem, gr_mem,
574 gr_mem_len );
575 goto done;
578 /* Real work goes here. Create a list of group names to
579 expand startign with the initial one. Pass that to
580 expand_groups() which returns a list of more group names
581 to expand. Do this up to the max search depth. */
583 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
584 result = False;
585 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
586 goto done;
588 sid_copy( &glist[0], group_sid );
589 n_glist = 1;
591 for ( i=0; i<max_depth && glist; i++ ) {
592 uint32 n_members = 0;
593 char **members = NULL;
594 NTSTATUS nt_status;
595 int j;
597 nt_status = expand_groups( mem_ctx, domain,
598 glist, n_glist,
599 &new_glist, &n_new_glist,
600 &members, &n_members);
601 if ( !NT_STATUS_IS_OK(nt_status) ) {
602 result = False;
603 goto done;
606 /* Add new group members to list. Pass through the
607 alias mapping function */
609 for (j=0; j<n_members; j++) {
610 fstring name_domain, name_acct;
611 fstring qualified_name;
612 char *mapped_name = NULL;
613 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
614 struct winbindd_domain *target_domain = NULL;
616 if (parse_domain_user(members[j], name_domain, name_acct)) {
617 target_domain = find_domain_from_name_noinit(name_domain);
618 /* NOW WHAT ? */
620 if (!target_domain) {
621 target_domain = domain;
624 name_map_status = normalize_name_map(members, target_domain,
625 name_acct, &mapped_name);
627 /* Basic whitespace replacement */
628 if (NT_STATUS_IS_OK(name_map_status)) {
629 fill_domain_username(qualified_name, name_domain,
630 mapped_name, true);
631 mapped_name = qualified_name;
633 /* no mapping at all */
634 else if (!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
635 mapped_name = members[j];
638 nt_status = add_names_to_list( mem_ctx, &names,
639 &num_names,
640 &mapped_name, 1);
641 if ( !NT_STATUS_IS_OK(nt_status) ) {
642 result = False;
643 goto done;
647 TALLOC_FREE( members );
649 /* If we have no more groups to expand, break out
650 early */
652 if (new_glist == NULL)
653 break;
655 /* One more round */
656 TALLOC_FREE(glist);
657 glist = new_glist;
658 n_glist = n_new_glist;
660 TALLOC_FREE( glist );
662 DEBUG(10, ("looked up %d names\n", num_names));
664 again:
665 /* Add members to list */
667 for (i = 0; i < num_names; i++) {
668 int len;
670 DEBUG(10, ("processing name %s\n", names[i]));
672 len = strlen(names[i]);
674 /* Add to list or calculate buffer length */
676 if (!buf) {
677 buf_len += len + 1; /* List is comma separated */
678 (*num_gr_mem)++;
679 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
680 } else {
681 DEBUG(10, ("appending %s at ndx %d\n",
682 names[i], buf_ndx));
683 parse_add_domuser(&buf[buf_ndx], names[i], &len);
684 buf_ndx += len;
685 buf[buf_ndx] = ',';
686 buf_ndx++;
690 /* Allocate buffer */
692 if (!buf && buf_len != 0) {
693 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
694 DEBUG(1, ("out of memory\n"));
695 result = False;
696 goto done;
698 memset(buf, 0, buf_len);
699 goto again;
702 /* Now we're done */
704 if (buf && buf_ndx > 0) {
705 buf[buf_ndx - 1] = '\0';
708 *gr_mem = buf;
709 *gr_mem_len = buf_len;
711 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
712 (unsigned int)*num_gr_mem,
713 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
714 result = True;
716 done:
718 talloc_destroy(mem_ctx);
720 DEBUG(10, ("fill_grent_mem returning %d\n", result));
722 return result;
725 static void winbindd_getgrsid(struct winbindd_cli_state *state, DOM_SID group_sid);
727 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
728 enum lsa_SidType type )
730 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
732 if (!success) {
733 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
734 request_error(state);
735 return;
738 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
739 DEBUG(5,("getgrnam_recv: not a group!\n"));
740 request_error(state);
741 return;
744 winbindd_getgrsid( state, *sid );
748 /* Return a group structure from a group name */
750 void winbindd_getgrnam(struct winbindd_cli_state *state)
752 struct winbindd_domain *domain;
753 fstring name_domain, name_group;
754 char *tmp;
755 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
757 /* Ensure null termination */
758 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
760 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
761 state->request.data.groupname));
763 nt_status = normalize_name_unmap(state->mem_ctx,
764 state->request.data.groupname,
765 &tmp);
766 /* If we didn't map anything in the above call, just reset the
767 tmp pointer to the original string */
768 if (!NT_STATUS_IS_OK(nt_status) &&
769 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
771 tmp = state->request.data.groupname;
774 /* Parse domain and groupname */
776 memset(name_group, 0, sizeof(name_group));
778 name_domain[0] = '\0';
779 name_group[0] = '\0';
781 parse_domain_user(tmp, name_domain, name_group);
783 /* if no domain or our local domain and no local tdb group, default to
784 * our local domain for aliases */
786 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
787 fstrcpy(name_domain, get_global_sam_name());
790 /* Get info for the domain */
792 if ((domain = find_domain_from_name(name_domain)) == NULL) {
793 DEBUG(3, ("could not get domain sid for domain %s\n",
794 name_domain));
795 request_error(state);
796 return;
798 /* should we deal with users for our domain? */
800 if ( lp_winbind_trusted_domains_only() && domain->primary) {
801 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
802 "getgrnam() for %s\\%s.\n", name_domain, name_group));
803 request_error(state);
804 return;
807 /* Get rid and name type from name */
809 fstrcpy( name_group, tmp );
811 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
812 getgrnam_recv, WINBINDD_GETGRNAM, state );
815 struct getgrsid_state {
816 struct winbindd_cli_state *state;
817 struct winbindd_domain *domain;
818 char *group_name;
819 enum lsa_SidType group_type;
820 uid_t gid;
821 DOM_SID group_sid;
824 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
826 struct getgrsid_state *s =
827 (struct getgrsid_state *)private_data;
828 struct winbindd_domain *domain;
829 size_t gr_mem_len;
830 size_t num_gr_mem;
831 char *gr_mem;
832 fstring dom_name, group_name;
834 if (!success) {
835 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
836 request_error(s->state);
837 return;
840 s->gid = gid;
842 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
843 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
844 request_error(s->state);
845 return;
849 /* Fill in group structure */
851 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
852 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
853 request_error(s->state);
854 return;
857 if (!fill_grent(s->state->mem_ctx, &s->state->response.data.gr,
858 dom_name, group_name, gid) ||
859 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
860 &num_gr_mem, &gr_mem, &gr_mem_len))
862 request_error(s->state);
863 return;
866 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
868 /* Group membership lives at start of extra data */
870 s->state->response.data.gr.gr_mem_ofs = 0;
872 s->state->response.length += gr_mem_len;
873 s->state->response.extra_data.data = gr_mem;
875 request_ok(s->state);
878 static void getgrsid_lookupsid_recv( void *private_data, bool success,
879 const char *dom_name, const char *name,
880 enum lsa_SidType name_type )
882 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
883 char *mapped_name = NULL;
884 fstring raw_name;
885 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
887 if (!success) {
888 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
889 request_error(s->state);
890 return;
893 /* either it's a domain group, a domain local group, or a
894 local group in an internal domain */
896 if ( !( (name_type==SID_NAME_DOM_GRP) ||
897 ((name_type==SID_NAME_ALIAS) &&
898 (s->domain->primary || s->domain->internal)) ) )
900 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
901 dom_name, name, name_type));
902 request_error(s->state);
903 return;
906 /* normalize the name and ensure that we have the DOM\name
907 coming out of here */
909 fstrcpy(raw_name, name);
911 nt_status = normalize_name_unmap(s->state->mem_ctx, raw_name,
912 &mapped_name);
914 /* basiuc whitespace reversal */
915 if (NT_STATUS_IS_OK(nt_status)) {
916 s->group_name = talloc_asprintf(s->state->mem_ctx,
917 "%s%c%s",
918 dom_name,
919 *lp_winbind_separator(),
920 mapped_name);
922 /* mapped from alias */
923 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
924 s->group_name = mapped_name;
926 /* no mapping at all. use original string */
927 else {
928 s->group_name = talloc_asprintf(s->state->mem_ctx,
929 "%s%c%s",
930 dom_name,
931 *lp_winbind_separator(),
932 raw_name);
935 if (s->group_name == NULL) {
936 DEBUG(1, ("getgrsid_lookupsid_recv: group_name is NULL!\n"));
937 request_error(s->state);
938 return;
941 s->group_type = name_type;
943 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
944 getgrsid_sid2gid_recv, s);
947 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
949 struct getgrsid_state *s;
951 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
952 DEBUG(0, ("talloc failed\n"));
953 request_error(state);
954 return;
957 s->state = state;
959 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
960 DEBUG(3, ("Could not find domain for sid %s\n",
961 sid_string_dbg(&group_sid)));
962 request_error(state);
963 return;
966 sid_copy(&s->group_sid, &group_sid);
968 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
969 getgrsid_lookupsid_recv, s );
973 static void getgrgid_recv(void *private_data, bool success, const char *sid)
975 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
976 enum lsa_SidType name_type;
977 DOM_SID group_sid;
979 if (success) {
980 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
981 (unsigned long)(state->request.data.gid), sid));
983 string_to_sid(&group_sid, sid);
984 winbindd_getgrsid(state, group_sid);
985 return;
988 /* Ok, this might be "ours", i.e. an alias */
989 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
990 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
991 (name_type == SID_NAME_ALIAS)) {
992 /* Hey, got an alias */
993 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
994 (unsigned long)(state->request.data.gid), sid));
995 winbindd_getgrsid(state, group_sid);
996 return;
999 DEBUG(1, ("could not convert gid %lu to sid\n",
1000 (unsigned long)state->request.data.gid));
1001 request_error(state);
1004 /* Return a group structure from a gid number */
1005 void winbindd_getgrgid(struct winbindd_cli_state *state)
1007 gid_t gid = state->request.data.gid;
1009 DEBUG(3, ("[%5lu]: getgrgid %lu\n",
1010 (unsigned long)state->pid,
1011 (unsigned long)gid));
1013 /* always use the async interface */
1014 winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
1018 * set/get/endgrent functions
1021 /* "Rewind" file pointer for group database enumeration */
1023 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
1025 struct winbindd_domain *domain;
1027 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
1029 /* Check user has enabled this */
1031 if (!lp_winbind_enum_groups()) {
1032 return False;
1035 /* Free old static data if it exists */
1037 if (state->getgrent_state != NULL) {
1038 free_getent_state(state->getgrent_state);
1039 state->getgrent_state = NULL;
1042 /* Create sam pipes for each domain we know about */
1044 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1045 struct getent_state *domain_state;
1047 /* Create a state record for this domain */
1049 /* don't add our domaina if we are a PDC or if we
1050 are a member of a Samba domain */
1052 if ( lp_winbind_trusted_domains_only() && domain->primary )
1054 continue;
1057 domain_state = SMB_MALLOC_P(struct getent_state);
1058 if (!domain_state) {
1059 DEBUG(1, ("winbindd_setgrent: "
1060 "malloc failed for domain_state!\n"));
1061 return False;
1064 ZERO_STRUCTP(domain_state);
1066 fstrcpy(domain_state->domain_name, domain->name);
1068 /* Add to list of open domains */
1070 DLIST_ADD(state->getgrent_state, domain_state);
1073 state->getgrent_initialized = True;
1074 return True;
1077 void winbindd_setgrent(struct winbindd_cli_state *state)
1079 if (winbindd_setgrent_internal(state)) {
1080 request_ok(state);
1081 } else {
1082 request_error(state);
1086 /* Close file pointer to ntdom group database */
1088 void winbindd_endgrent(struct winbindd_cli_state *state)
1090 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
1092 free_getent_state(state->getgrent_state);
1093 state->getgrent_initialized = False;
1094 state->getgrent_state = NULL;
1095 request_ok(state);
1098 /* Get the list of domain groups and domain aliases for a domain. We fill in
1099 the sam_entries and num_sam_entries fields with domain group information.
1100 Return True if some groups were returned, False otherwise. */
1102 bool get_sam_group_entries(struct getent_state *ent)
1104 NTSTATUS status;
1105 uint32 num_entries;
1106 struct acct_info *name_list = NULL;
1107 TALLOC_CTX *mem_ctx;
1108 bool result = False;
1109 struct acct_info *sam_grp_entries = NULL;
1110 struct winbindd_domain *domain;
1112 if (ent->got_sam_entries)
1113 return False;
1115 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1116 ent->domain_name))) {
1117 DEBUG(1, ("get_sam_group_entries: "
1118 "could not create talloc context!\n"));
1119 return False;
1122 /* Free any existing group info */
1124 SAFE_FREE(ent->sam_entries);
1125 ent->num_sam_entries = 0;
1126 ent->got_sam_entries = True;
1128 /* Enumerate domain groups */
1130 num_entries = 0;
1132 if (!(domain = find_domain_from_name(ent->domain_name))) {
1133 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1134 ent->domain_name));
1135 goto done;
1138 /* always get the domain global groups */
1140 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1141 &sam_grp_entries);
1143 if (!NT_STATUS_IS_OK(status)) {
1144 DEBUG(3, ("get_sam_group_entries: "
1145 "could not enumerate domain groups! Error: %s\n",
1146 nt_errstr(status)));
1147 result = False;
1148 goto done;
1151 /* Copy entries into return buffer */
1153 if (num_entries) {
1154 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1155 if (!name_list) {
1156 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1157 "memory for %d domain groups!\n",
1158 num_entries));
1159 result = False;
1160 goto done;
1162 memcpy(name_list, sam_grp_entries,
1163 num_entries * sizeof(struct acct_info));
1166 ent->num_sam_entries = num_entries;
1168 /* get the domain local groups if we are a member of a native win2k
1169 * domain and are not using LDAP to get the groups */
1171 if ( ( lp_security() != SEC_ADS && domain->native_mode
1172 && domain->primary) || domain->internal )
1174 DEBUG(4,("get_sam_group_entries: %s domain; "
1175 "enumerating local groups as well\n",
1176 domain->native_mode ? "Native Mode 2k":
1177 "BUILTIN or local"));
1179 status = domain->methods->enum_local_groups(domain, mem_ctx,
1180 &num_entries,
1181 &sam_grp_entries);
1183 if ( !NT_STATUS_IS_OK(status) ) {
1184 DEBUG(3,("get_sam_group_entries: "
1185 "Failed to enumerate "
1186 "domain local groups with error %s!\n",
1187 nt_errstr(status)));
1188 num_entries = 0;
1190 else
1191 DEBUG(4,("get_sam_group_entries: "
1192 "Returned %d local groups\n",
1193 num_entries));
1195 /* Copy entries into return buffer */
1197 if ( num_entries ) {
1198 name_list = SMB_REALLOC_ARRAY(name_list,
1199 struct acct_info,
1200 ent->num_sam_entries+
1201 num_entries);
1202 if (!name_list) {
1203 DEBUG(0,("get_sam_group_entries: "
1204 "Failed to realloc more memory "
1205 "for %d local groups!\n",
1206 num_entries));
1207 result = False;
1208 goto done;
1211 memcpy(&name_list[ent->num_sam_entries],
1212 sam_grp_entries,
1213 num_entries * sizeof(struct acct_info));
1216 ent->num_sam_entries += num_entries;
1220 /* Fill in remaining fields */
1222 ent->sam_entries = name_list;
1223 ent->sam_entry_index = 0;
1225 result = (ent->num_sam_entries > 0);
1227 done:
1228 talloc_destroy(mem_ctx);
1230 return result;
1233 /* Fetch next group entry from ntdom database */
1235 #define MAX_GETGRENT_GROUPS 500
1237 void winbindd_getgrent(struct winbindd_cli_state *state)
1239 struct getent_state *ent;
1240 struct winbindd_gr *group_list = NULL;
1241 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1242 char *gr_mem_list = NULL;
1244 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1246 /* Check user has enabled this */
1248 if (!lp_winbind_enum_groups()) {
1249 request_error(state);
1250 return;
1253 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1255 if (num_groups == 0) {
1256 request_error(state);
1257 return;
1260 group_list = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups);
1261 if (!group_list) {
1262 request_error(state);
1263 return;
1265 /* will be freed by process_request() */
1266 state->response.extra_data.data = group_list;
1268 memset(state->response.extra_data.data, '\0',
1269 num_groups * sizeof(struct winbindd_gr) );
1271 state->response.data.num_entries = 0;
1273 if (!state->getgrent_initialized)
1274 winbindd_setgrent_internal(state);
1276 if (!(ent = state->getgrent_state)) {
1277 request_error(state);
1278 return;
1281 /* Start sending back groups */
1283 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1284 struct acct_info *name_list = NULL;
1285 fstring domain_group_name;
1286 uint32 result;
1287 gid_t group_gid;
1288 size_t gr_mem_len;
1289 char *gr_mem;
1290 DOM_SID group_sid;
1291 struct winbindd_domain *domain;
1293 /* Do we need to fetch another chunk of groups? */
1295 tryagain:
1297 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1298 ent->sam_entry_index, ent->num_sam_entries));
1300 if (ent->num_sam_entries == ent->sam_entry_index) {
1302 while(ent && !get_sam_group_entries(ent)) {
1303 struct getent_state *next_ent;
1305 DEBUG(10, ("freeing state info for domain %s\n",
1306 ent->domain_name));
1308 /* Free state information for this domain */
1310 SAFE_FREE(ent->sam_entries);
1312 next_ent = ent->next;
1313 DLIST_REMOVE(state->getgrent_state, ent);
1315 SAFE_FREE(ent);
1316 ent = next_ent;
1319 /* No more domains */
1321 if (!ent)
1322 break;
1325 name_list = (struct acct_info *)ent->sam_entries;
1327 if (!(domain = find_domain_from_name(ent->domain_name))) {
1328 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1329 ent->domain_name));
1330 result = False;
1331 goto done;
1334 /* Lookup group info */
1336 sid_copy(&group_sid, &domain->sid);
1337 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1339 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->name, &group_sid,
1340 &group_gid))) {
1341 union unid_t id;
1342 enum lsa_SidType type;
1344 DEBUG(10, ("SID %s not in idmap\n",
1345 sid_string_dbg(&group_sid)));
1347 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1348 DEBUG(1,("could not look up gid for group %s\n",
1349 name_list[ent->sam_entry_index].acct_name));
1350 ent->sam_entry_index++;
1351 goto tryagain;
1354 if ((type != SID_NAME_DOM_GRP) &&
1355 (type != SID_NAME_ALIAS) &&
1356 (type != SID_NAME_WKN_GRP)) {
1357 DEBUG(1, ("Group %s is a %s, not a group\n",
1358 sid_type_lookup(type),
1359 name_list[ent->sam_entry_index].acct_name));
1360 ent->sam_entry_index++;
1361 goto tryagain;
1363 group_gid = id.gid;
1366 DEBUG(10, ("got gid %lu for group %lu\n",
1367 (unsigned long)group_gid,
1368 (unsigned long)name_list[ent->sam_entry_index].rid));
1370 /* Fill in group entry */
1372 fill_domain_username(domain_group_name, ent->domain_name,
1373 name_list[ent->sam_entry_index].acct_name, True);
1375 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1376 ent->domain_name,
1377 name_list[ent->sam_entry_index].acct_name,
1378 group_gid);
1380 /* Fill in group membership entry */
1382 if (result) {
1383 size_t num_gr_mem = 0;
1384 DOM_SID member_sid;
1385 group_list[group_list_ndx].num_gr_mem = 0;
1386 gr_mem = NULL;
1387 gr_mem_len = 0;
1389 /* Get group membership */
1390 if (state->request.cmd == WINBINDD_GETGRLST) {
1391 result = True;
1392 } else {
1393 sid_copy(&member_sid, &domain->sid);
1394 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1395 result = fill_grent_mem(
1396 domain,
1397 NULL,
1398 &member_sid,
1399 SID_NAME_DOM_GRP,
1400 &num_gr_mem,
1401 &gr_mem, &gr_mem_len);
1403 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1407 if (result) {
1408 /* Append to group membership list */
1409 gr_mem_list = (char *)SMB_REALLOC(
1410 gr_mem_list, gr_mem_list_len + gr_mem_len);
1412 if (!gr_mem_list &&
1413 (group_list[group_list_ndx].num_gr_mem != 0)) {
1414 DEBUG(0, ("out of memory\n"));
1415 gr_mem_list_len = 0;
1416 break;
1419 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1420 gr_mem_list_len, (unsigned int)gr_mem_len));
1422 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1423 gr_mem_len);
1425 SAFE_FREE(gr_mem);
1427 group_list[group_list_ndx].gr_mem_ofs =
1428 gr_mem_list_len;
1430 gr_mem_list_len += gr_mem_len;
1433 ent->sam_entry_index++;
1435 /* Add group to return list */
1437 if (result) {
1439 DEBUG(10, ("adding group num_entries = %d\n",
1440 state->response.data.num_entries));
1442 group_list_ndx++;
1443 state->response.data.num_entries++;
1445 state->response.length +=
1446 sizeof(struct winbindd_gr);
1448 } else {
1449 DEBUG(0, ("could not lookup domain group %s\n",
1450 domain_group_name));
1454 /* Copy the list of group memberships to the end of the extra data */
1456 if (group_list_ndx == 0)
1457 goto done;
1459 state->response.extra_data.data = SMB_REALLOC(
1460 state->response.extra_data.data,
1461 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1463 if (!state->response.extra_data.data) {
1464 DEBUG(0, ("out of memory\n"));
1465 group_list_ndx = 0;
1466 SAFE_FREE(gr_mem_list);
1467 request_error(state);
1468 return;
1471 memcpy(&((char *)state->response.extra_data.data)
1472 [group_list_ndx * sizeof(struct winbindd_gr)],
1473 gr_mem_list, gr_mem_list_len);
1475 state->response.length += gr_mem_list_len;
1477 DEBUG(10, ("returning %d groups, length = %d\n",
1478 group_list_ndx, gr_mem_list_len));
1480 /* Out of domains */
1482 done:
1484 SAFE_FREE(gr_mem_list);
1486 if (group_list_ndx > 0)
1487 request_ok(state);
1488 else
1489 request_error(state);
1492 /* List domain groups without mapping to unix ids */
1493 void winbindd_list_groups(struct winbindd_cli_state *state)
1495 winbindd_list_ent(state, LIST_GROUPS);
1498 /* Get user supplementary groups. This is much quicker than trying to
1499 invert the groups database. We merge the groups from the gids and
1500 other_sids info3 fields as trusted domain, universal group
1501 memberships, and nested groups (win2k native mode only) are not
1502 returned by the getgroups RPC call but are present in the info3. */
1504 struct getgroups_state {
1505 struct winbindd_cli_state *state;
1506 struct winbindd_domain *domain;
1507 char *domname;
1508 char *username;
1509 DOM_SID user_sid;
1511 const DOM_SID *token_sids;
1512 size_t i, num_token_sids;
1514 gid_t *token_gids;
1515 size_t num_token_gids;
1518 static void getgroups_usersid_recv(void *private_data, bool success,
1519 const DOM_SID *sid, enum lsa_SidType type);
1520 static void getgroups_tokensids_recv(void *private_data, bool success,
1521 DOM_SID *token_sids, size_t num_token_sids);
1522 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1524 void winbindd_getgroups(struct winbindd_cli_state *state)
1526 struct getgroups_state *s;
1527 char *real_name = NULL;
1528 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1530 /* Ensure null termination */
1531 state->request.data.username
1532 [sizeof(state->request.data.username)-1]='\0';
1534 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1535 state->request.data.username));
1537 /* Parse domain and username */
1539 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1540 if (s == NULL) {
1541 DEBUG(0, ("talloc failed\n"));
1542 request_error(state);
1543 return;
1546 s->state = state;
1548 nt_status = normalize_name_unmap(state->mem_ctx,
1549 state->request.data.username,
1550 &real_name);
1552 /* Reset the real_name pointer if we didn't do anything
1553 productive in the above call */
1554 if (!NT_STATUS_IS_OK(nt_status) &&
1555 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
1557 real_name = state->request.data.username;
1560 if (!parse_domain_user_talloc(state->mem_ctx, real_name,
1561 &s->domname, &s->username)) {
1562 DEBUG(5, ("Could not parse domain user: %s\n",
1563 real_name));
1565 /* error out if we do not have nested group support */
1567 if ( !lp_winbind_nested_groups() ) {
1568 request_error(state);
1569 return;
1572 s->domname = talloc_strdup(state->mem_ctx,
1573 get_global_sam_name());
1574 s->username = talloc_strdup(state->mem_ctx,
1575 state->request.data.username);
1578 /* Get info for the domain (either by short domain name or
1579 DNS name in the case of a UPN) */
1581 s->domain = find_domain_from_name_noinit(s->domname);
1582 if (!s->domain) {
1583 char *p = strchr(s->username, '@');
1585 if (p) {
1586 s->domain = find_domain_from_name_noinit(p+1);
1591 if (s->domain == NULL) {
1592 DEBUG(7, ("could not find domain entry for domain %s\n",
1593 s->domname));
1594 request_error(state);
1595 return;
1598 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1599 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1600 "getgroups() for %s\\%s.\n", s->domname,
1601 s->username));
1602 request_error(state);
1603 return;
1606 /* Get rid and name type from name. The following costs 1 packet */
1608 winbindd_lookupname_async(state->mem_ctx,
1609 s->domname, s->username,
1610 getgroups_usersid_recv,
1611 WINBINDD_GETGROUPS, s);
1614 static void getgroups_usersid_recv(void *private_data, bool success,
1615 const DOM_SID *sid, enum lsa_SidType type)
1617 struct getgroups_state *s =
1618 (struct getgroups_state *)private_data;
1620 if ((!success) ||
1621 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1622 request_error(s->state);
1623 return;
1626 sid_copy(&s->user_sid, sid);
1628 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1629 getgroups_tokensids_recv, s);
1632 static void getgroups_tokensids_recv(void *private_data, bool success,
1633 DOM_SID *token_sids, size_t num_token_sids)
1635 struct getgroups_state *s =
1636 (struct getgroups_state *)private_data;
1638 /* We need at least the user sid and the primary group in the token,
1639 * otherwise it's an error */
1641 if ((!success) || (num_token_sids < 2)) {
1642 request_error(s->state);
1643 return;
1646 s->token_sids = token_sids;
1647 s->num_token_sids = num_token_sids;
1648 s->i = 0;
1650 s->token_gids = NULL;
1651 s->num_token_gids = 0;
1653 getgroups_sid2gid_recv(s, False, 0);
1656 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1658 struct getgroups_state *s =
1659 (struct getgroups_state *)private_data;
1661 if (success) {
1662 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1663 &s->token_gids,
1664 &s->num_token_gids)) {
1665 return;
1669 if (s->i < s->num_token_sids) {
1670 const DOM_SID *sid = &s->token_sids[s->i];
1671 s->i += 1;
1673 if (sid_equal(sid, &s->user_sid)) {
1674 getgroups_sid2gid_recv(s, False, 0);
1675 return;
1678 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1679 getgroups_sid2gid_recv, s);
1680 return;
1683 s->state->response.data.num_entries = s->num_token_gids;
1684 if (s->num_token_gids) {
1685 /* s->token_gids are talloced */
1686 s->state->response.extra_data.data =
1687 smb_xmemdup(s->token_gids,
1688 s->num_token_gids * sizeof(gid_t));
1689 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1691 request_ok(s->state);
1694 /* Get user supplementary sids. This is equivalent to the
1695 winbindd_getgroups() function but it involves a SID->SIDs mapping
1696 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1697 idmap. This call is designed to be used with applications that need
1698 to do ACL evaluation themselves. Note that the cached info3 data is
1699 not used
1701 this function assumes that the SID that comes in is a user SID. If
1702 you pass in another type of SID then you may get unpredictable
1703 results.
1706 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1707 size_t num_sids);
1709 void winbindd_getusersids(struct winbindd_cli_state *state)
1711 DOM_SID *user_sid;
1713 /* Ensure null termination */
1714 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1716 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1717 if (user_sid == NULL) {
1718 DEBUG(1, ("talloc failed\n"));
1719 request_error(state);
1720 return;
1723 if (!string_to_sid(user_sid, state->request.data.sid)) {
1724 DEBUG(1, ("Could not get convert sid %s from string\n",
1725 state->request.data.sid));
1726 request_error(state);
1727 return;
1730 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1731 state);
1734 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1735 size_t num_sids)
1737 struct winbindd_cli_state *state =
1738 (struct winbindd_cli_state *)private_data;
1739 char *ret = NULL;
1740 unsigned ofs, ret_size = 0;
1741 size_t i;
1743 if (!success) {
1744 request_error(state);
1745 return;
1748 /* work out the response size */
1749 for (i = 0; i < num_sids; i++) {
1750 fstring s;
1751 sid_to_fstring(s, &sids[i]);
1752 ret_size += strlen(s) + 1;
1755 /* build the reply */
1756 ret = (char *)SMB_MALLOC(ret_size);
1757 if (!ret) {
1758 DEBUG(0, ("malloc failed\n"));
1759 request_error(state);
1760 return;
1762 ofs = 0;
1763 for (i = 0; i < num_sids; i++) {
1764 fstring s;
1765 sid_to_fstring(s, &sids[i]);
1766 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1767 ofs += strlen(ret+ofs) + 1;
1770 /* Send data back to client */
1771 state->response.data.num_entries = num_sids;
1772 state->response.extra_data.data = ret;
1773 state->response.length += ret_size;
1774 request_ok(state);
1777 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1779 DOM_SID user_sid;
1780 struct winbindd_domain *domain;
1782 /* Ensure null termination */
1783 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1785 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1786 DEBUG(1, ("Could not get convert sid %s from string\n",
1787 state->request.data.sid));
1788 request_error(state);
1789 return;
1792 /* Get info for the domain */
1793 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1794 DEBUG(0,("could not find domain entry for sid %s\n",
1795 sid_string_dbg(&user_sid)));
1796 request_error(state);
1797 return;
1800 sendto_domain(state, domain);
1803 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1804 struct winbindd_cli_state *state)
1806 DOM_SID user_sid;
1807 NTSTATUS status;
1809 char *sidstring;
1810 ssize_t len;
1811 DOM_SID *groups;
1812 uint32 num_groups;
1814 /* Ensure null termination */
1815 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1817 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1818 DEBUG(1, ("Could not get convert sid %s from string\n",
1819 state->request.data.sid));
1820 return WINBINDD_ERROR;
1823 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1824 &user_sid, &num_groups,
1825 &groups);
1826 if (!NT_STATUS_IS_OK(status))
1827 return WINBINDD_ERROR;
1829 if (num_groups == 0) {
1830 state->response.data.num_entries = 0;
1831 state->response.extra_data.data = NULL;
1832 return WINBINDD_OK;
1835 if (!print_sidlist(state->mem_ctx,
1836 groups, num_groups,
1837 &sidstring, &len)) {
1838 DEBUG(0, ("talloc failed\n"));
1839 return WINBINDD_ERROR;
1842 state->response.extra_data.data = SMB_STRDUP(sidstring);
1843 if (!state->response.extra_data.data) {
1844 return WINBINDD_ERROR;
1846 state->response.length += len+1;
1847 state->response.data.num_entries = num_groups;
1849 return WINBINDD_OK;