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/>.
28 extern bool opt_nocache;
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)
39 fill_domain_username(name, domain, user, True);
43 safe_strcat(name, ",", sizeof(name)-1);
44 string_append(pp_members, name);
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,
54 size_t *p_num_members)
58 struct winbindd_domain *domain;
61 char *domain_name = NULL;
63 enum lsa_SidType type;
72 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
74 if (mem_ctx == NULL) {
75 DEBUG(1, ("talloc_init failed\n"));
79 sid_copy(&dom_sid, sid);
80 sid_split_rid(&dom_sid, &rid);
82 domain = find_lookup_domain_from_sid(sid);
85 DEBUG(3, ("Could not find domain for sid %s\n",
86 sid_string_dbg(sid)));
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)));
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);
106 if (type != SID_NAME_DOM_GRP) {
107 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
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)));
122 result = domain->methods->lookup_groupmem(domain, mem_ctx,
127 if (!NT_STATUS_IS_OK(result)) {
128 DEBUG(10, ("Could not lookup group members for %s: %s\n",
129 name, nt_errstr(result)));
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));
143 add_member(NULL, names[i], pp_members, p_num_members);
147 talloc_destroy(mem_ctx);
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)
156 size_t i, num_members;
162 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
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) {
175 /* We have at least one member, strip off the last "," */
176 len = strlen(*gr_mem);
177 (*gr_mem)[len-1] = '\0';
184 /* Fill a grent structure from various other information */
186 static bool fill_grent(struct winbindd_gr *gr, const char *dom_name,
187 const char *gr_name, gid_t unix_gid)
189 fstring full_group_name;
191 fill_domain_username( full_group_name, dom_name, gr_name, True );
193 gr->gr_gid = unix_gid;
195 /* Group name and password */
197 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
198 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
203 /***********************************************************************
204 If "enum users" is set to false, and the group being looked
205 up is the Domain Users SID: S-1-5-domain-513, then for the
206 list of members check if the querying user is in that group,
207 and if so only return that user as the gr_mem array.
208 We can change this to a different parameter than "enum users"
209 if neccessaey, or parameterize the group list we do this for.
210 ***********************************************************************/
212 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
213 struct winbindd_domain *domain,
214 struct winbindd_cli_state *state,
216 enum lsa_SidType group_name_type,
217 size_t *num_gr_mem, char **gr_mem,
220 DOM_SID querying_user_sid;
221 DOM_SID *pquerying_user_sid = NULL;
222 uint32 num_groups = 0;
223 DOM_SID *user_sids = NULL;
224 bool u_in_group = False;
227 unsigned int buf_len = 0;
230 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
234 uid_t ret_uid = (uid_t)-1;
235 if (sys_getpeereid(state->sock, &ret_uid)==0) {
236 /* We know who's asking - look up their SID if
237 it's one we've mapped before. */
238 status = idmap_uid_to_sid(domain->name,
239 &querying_user_sid, ret_uid);
240 if (NT_STATUS_IS_OK(status)) {
241 pquerying_user_sid = &querying_user_sid;
242 DEBUG(10,("fill_grent_mem_domain_users: "
243 "querying uid %u -> %s\n",
244 (unsigned int)ret_uid,
245 sid_string_dbg(pquerying_user_sid)));
250 /* Only look up if it was a winbindd user in this domain. */
251 if (pquerying_user_sid &&
252 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
254 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
255 sid_string_dbg(pquerying_user_sid) ));
257 status = domain->methods->lookup_usergroups(domain,
262 if (!NT_STATUS_IS_OK(status)) {
263 DEBUG(1, ("fill_grent_mem_domain_users: "
264 "lookup_usergroups failed "
265 "for sid %s in domain %s (error: %s)\n",
266 sid_string_dbg(pquerying_user_sid),
272 for (i = 0; i < num_groups; i++) {
273 if (sid_equal(group_sid, &user_sids[i])) {
274 /* User is in Domain Users, add their name
275 as the only group member. */
284 char *domainname = NULL;
285 char *username = NULL;
287 enum lsa_SidType type;
289 DEBUG(10,("fill_grent_mem_domain_users: "
290 "sid %s in 'Domain Users' in domain %s\n",
291 sid_string_dbg(pquerying_user_sid),
294 status = domain->methods->sid_to_name(domain, mem_ctx,
299 if (!NT_STATUS_IS_OK(status)) {
300 DEBUG(1, ("could not lookup username for user "
301 "sid %s in domain %s (error: %s)\n",
302 sid_string_dbg(pquerying_user_sid),
307 fill_domain_username(name, domain->name, username, True);
310 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
311 DEBUG(1, ("out of memory\n"));
314 memcpy(buf, name, buf_len);
316 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
317 "'Domain Users' in domain %s\n",
318 name, domain->name ));
320 /* user is the only member */
325 *gr_mem_len = buf_len;
327 DEBUG(10, ("fill_grent_mem_domain_users: "
328 "num_mem = %u, len = %u, mem = %s\n",
329 (unsigned int)*num_gr_mem,
330 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
335 /***********************************************************************
336 Add names to a list. Assumes a canonical version of the string
338 ***********************************************************************/
340 static int namecmp( const void *a, const void *b )
342 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
345 static void sort_unique_list(char ***list, uint32 *n_list)
349 /* search for duplicates for sorting and looking for matching
352 qsort(*list, *n_list, sizeof(char*), QSORT_CAST namecmp);
354 for (i=1; i < *n_list; i++) {
355 if (strcmp((*list)[i-1], (*list)[i]) == 0) {
356 memmove(&((*list)[i-1]), &((*list)[i]),
357 sizeof(char*)*((*n_list)-i));
363 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
364 char ***list, uint32 *n_list,
365 char **names, uint32 n_names )
367 char **new_list = NULL;
368 uint32 n_new_list = 0;
371 if ( !names || (n_names == 0) )
374 /* Alloc the maximum size we'll need */
376 if ( *list == NULL ) {
377 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
378 return NT_STATUS_NO_MEMORY;
380 n_new_list = n_names;
382 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
383 (*n_list) + n_names );
385 return NT_STATUS_NO_MEMORY;
386 n_new_list = (*n_list) + n_names;
391 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
392 new_list[i] = talloc_strdup( new_list, names[j] );
396 *n_list = n_new_list;
401 /***********************************************************************
402 ***********************************************************************/
404 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
405 struct winbindd_domain *d,
406 DOM_SID *glist, uint32 n_glist,
407 DOM_SID **new_glist, uint32 *n_new_glist,
408 char ***members, uint32 *n_members )
411 NTSTATUS status = NT_STATUS_OK;
412 uint32 num_names = 0;
413 uint32 *name_types = NULL;
415 DOM_SID *sid_mem = NULL;
416 TALLOC_CTX *tmp_ctx = NULL;
417 DOM_SID *new_groups = NULL;
418 size_t new_groups_size = 0;
425 for ( i=0; i<n_glist; i++ ) {
426 tmp_ctx = talloc_new( ctx );
428 /* Lookup the group membership */
430 status = d->methods->lookup_groupmem(d, tmp_ctx,
431 &glist[i], &num_names,
434 if ( !NT_STATUS_IS_OK(status) )
437 /* Separate users and groups into two lists */
439 for ( j=0; j<num_names; j++ ) {
442 if ( name_types[j] == SID_NAME_USER ||
443 name_types[j] == SID_NAME_COMPUTER )
445 status = add_names_to_list( ctx, members,
448 if ( !NT_STATUS_IS_OK(status) )
455 if ( name_types[j] == SID_NAME_DOM_GRP ||
456 name_types[j] == SID_NAME_ALIAS )
458 status = add_sid_to_array_unique(ctx,
462 if (!NT_STATUS_IS_OK(status)) {
470 TALLOC_FREE( tmp_ctx );
473 *new_glist = new_groups;
474 *n_new_glist = (uint32)new_groups_size;
477 TALLOC_FREE( tmp_ctx );
482 /***********************************************************************
483 Fill in the group membership field of a NT group given by group_sid
484 ***********************************************************************/
486 static bool fill_grent_mem(struct winbindd_domain *domain,
487 struct winbindd_cli_state *state,
489 enum lsa_SidType group_name_type,
490 size_t *num_gr_mem, char **gr_mem,
493 uint32 num_names = 0;
494 unsigned int buf_len = 0, buf_ndx = 0, i;
495 char **names = NULL, *buf = NULL;
499 DOM_SID *glist = NULL;
500 DOM_SID *new_glist = NULL;
501 uint32 n_glist, n_new_glist;
502 int max_depth = lp_winbind_expand_groups();
504 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
507 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
509 /* Initialize with no members */
513 /* HACK ALERT!! This whole routine does not cope with group members
514 * from more than one domain, ie aliases. Thus we have to work it out
515 * ourselves in a special routine. */
517 if (domain->internal) {
518 result = fill_passdb_alias_grmem(domain, group_sid,
524 /* Verify name type */
526 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
527 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
529 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
530 sid_string_dbg(group_sid),
531 domain->name, group_name_type));
535 /* OPTIMIZATION / HACK. See comment in
536 fill_grent_mem_domusers() */
538 sid_peek_rid( group_sid, &group_rid );
539 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
540 result = fill_grent_mem_domusers( mem_ctx, domain, state,
541 group_sid, group_name_type,
547 /* Real work goes here. Create a list of group names to
548 expand startign with the initial one. Pass that to
549 expand_groups() which returns a list of more group names
550 to expand. Do this up to the max search depth. */
552 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
554 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
557 sid_copy( &glist[0], group_sid );
560 for ( i=0; i<max_depth && glist; i++ ) {
561 uint32 n_members = 0;
562 char **members = NULL;
565 nt_status = expand_groups( mem_ctx, domain,
567 &new_glist, &n_new_glist,
568 &members, &n_members);
569 if ( !NT_STATUS_IS_OK(nt_status) ) {
574 /* Add new group members to list */
576 nt_status = add_names_to_list( mem_ctx, &names, &num_names,
577 members, n_members );
578 if ( !NT_STATUS_IS_OK(nt_status) ) {
583 TALLOC_FREE( members );
585 /* If we have no more groups to expand, break out
588 if (new_glist == NULL)
594 n_glist = n_new_glist;
596 TALLOC_FREE( glist );
598 sort_unique_list(&names, &num_names);
600 DEBUG(10, ("looked up %d names\n", num_names));
603 /* Add members to list */
605 for (i = 0; i < num_names; i++) {
608 DEBUG(10, ("processing name %s\n", names[i]));
610 len = strlen(names[i]);
612 /* Add to list or calculate buffer length */
615 buf_len += len + 1; /* List is comma separated */
617 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
619 DEBUG(10, ("appending %s at ndx %d\n",
621 parse_add_domuser(&buf[buf_ndx], names[i], &len);
628 /* Allocate buffer */
630 if (!buf && buf_len != 0) {
631 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
632 DEBUG(1, ("out of memory\n"));
636 memset(buf, 0, buf_len);
642 if (buf && buf_ndx > 0) {
643 buf[buf_ndx - 1] = '\0';
647 *gr_mem_len = buf_len;
649 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
650 (unsigned int)*num_gr_mem,
651 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
656 talloc_destroy(mem_ctx);
658 DEBUG(10, ("fill_grent_mem returning %d\n", result));
663 static void winbindd_getgrsid(struct winbindd_cli_state *state, DOM_SID group_sid);
665 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
666 enum lsa_SidType type )
668 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
671 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
672 request_error(state);
676 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
677 DEBUG(5,("getgrnam_recv: not a group!\n"));
678 request_error(state);
682 winbindd_getgrsid( state, *sid );
686 /* Return a group structure from a group name */
688 void winbindd_getgrnam(struct winbindd_cli_state *state)
690 struct winbindd_domain *domain;
691 fstring name_domain, name_group;
694 /* Ensure null termination */
695 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
697 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
698 state->request.data.groupname));
700 /* Parse domain and groupname */
702 memset(name_group, 0, sizeof(fstring));
704 tmp = state->request.data.groupname;
706 name_domain[0] = '\0';
707 name_group[0] = '\0';
709 parse_domain_user(tmp, name_domain, name_group);
711 /* if no domain or our local domain and no local tdb group, default to
712 * our local domain for aliases */
714 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
715 fstrcpy(name_domain, get_global_sam_name());
718 /* Get info for the domain */
720 if ((domain = find_domain_from_name(name_domain)) == NULL) {
721 DEBUG(3, ("could not get domain sid for domain %s\n",
723 request_error(state);
726 /* should we deal with users for our domain? */
728 if ( lp_winbind_trusted_domains_only() && domain->primary) {
729 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
730 "getgrnam() for %s\\%s.\n", name_domain, name_group));
731 request_error(state);
735 /* Get rid and name type from name */
737 ws_name_replace( name_group, WB_REPLACE_CHAR );
739 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
740 getgrnam_recv, WINBINDD_GETGRNAM, state );
743 struct getgrsid_state {
744 struct winbindd_cli_state *state;
745 struct winbindd_domain *domain;
747 enum lsa_SidType group_type;
752 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
754 struct getgrsid_state *s =
755 (struct getgrsid_state *)private_data;
756 struct winbindd_domain *domain;
760 fstring dom_name, group_name;
763 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
764 request_error(s->state);
770 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
771 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
772 request_error(s->state);
777 /* Fill in group structure */
779 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
780 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
781 request_error(s->state);
785 if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
786 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
787 &num_gr_mem, &gr_mem, &gr_mem_len))
789 request_error(s->state);
793 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
795 /* Group membership lives at start of extra data */
797 s->state->response.data.gr.gr_mem_ofs = 0;
799 s->state->response.length += gr_mem_len;
800 s->state->response.extra_data.data = gr_mem;
802 request_ok(s->state);
805 static void getgrsid_lookupsid_recv( void *private_data, bool success,
806 const char *dom_name, const char *name,
807 enum lsa_SidType name_type )
809 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
812 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
813 request_error(s->state);
817 /* either it's a domain group, a domain local group, or a
818 local group in an internal domain */
820 if ( !( (name_type==SID_NAME_DOM_GRP) ||
821 ((name_type==SID_NAME_ALIAS) &&
822 (s->domain->primary || s->domain->internal)) ) )
824 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
825 dom_name, name, name_type));
826 request_error(s->state);
830 if ( (s->group_name = talloc_asprintf( s->state->mem_ctx,
833 *lp_winbind_separator(),
836 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
837 request_error(s->state);
841 s->group_type = name_type;
843 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
844 getgrsid_sid2gid_recv, s);
847 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
849 struct getgrsid_state *s;
851 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
852 DEBUG(0, ("talloc failed\n"));
853 request_error(state);
859 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
860 DEBUG(3, ("Could not find domain for sid %s\n",
861 sid_string_dbg(&group_sid)));
862 request_error(state);
866 sid_copy(&s->group_sid, &group_sid);
868 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
869 getgrsid_lookupsid_recv, s );
873 static void getgrgid_recv(void *private_data, bool success, const char *sid)
875 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
876 enum lsa_SidType name_type;
880 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
881 (unsigned long)(state->request.data.gid), sid));
883 string_to_sid(&group_sid, sid);
884 winbindd_getgrsid(state, group_sid);
888 /* Ok, this might be "ours", i.e. an alias */
889 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
890 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
891 (name_type == SID_NAME_ALIAS)) {
892 /* Hey, got an alias */
893 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
894 (unsigned long)(state->request.data.gid), sid));
895 winbindd_getgrsid(state, group_sid);
899 DEBUG(1, ("could not convert gid %lu to sid\n",
900 (unsigned long)state->request.data.gid));
901 request_error(state);
904 /* Return a group structure from a gid number */
905 void winbindd_getgrgid(struct winbindd_cli_state *state)
907 gid_t gid = state->request.data.gid;
909 DEBUG(3, ("[%5lu]: getgrgid %lu\n",
910 (unsigned long)state->pid,
911 (unsigned long)gid));
913 /* always use the async interface */
914 winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
918 * set/get/endgrent functions
921 /* "Rewind" file pointer for group database enumeration */
923 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
925 struct winbindd_domain *domain;
927 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
929 /* Check user has enabled this */
931 if (!lp_winbind_enum_groups()) {
935 /* Free old static data if it exists */
937 if (state->getgrent_state != NULL) {
938 free_getent_state(state->getgrent_state);
939 state->getgrent_state = NULL;
942 /* Create sam pipes for each domain we know about */
944 for (domain = domain_list(); domain != NULL; domain = domain->next) {
945 struct getent_state *domain_state;
947 /* Create a state record for this domain */
949 /* don't add our domaina if we are a PDC or if we
950 are a member of a Samba domain */
952 if ( lp_winbind_trusted_domains_only() && domain->primary )
957 domain_state = SMB_MALLOC_P(struct getent_state);
959 DEBUG(1, ("winbindd_setgrent: "
960 "malloc failed for domain_state!\n"));
964 ZERO_STRUCTP(domain_state);
966 fstrcpy(domain_state->domain_name, domain->name);
968 /* Add to list of open domains */
970 DLIST_ADD(state->getgrent_state, domain_state);
973 state->getgrent_initialized = True;
977 void winbindd_setgrent(struct winbindd_cli_state *state)
979 if (winbindd_setgrent_internal(state)) {
982 request_error(state);
986 /* Close file pointer to ntdom group database */
988 void winbindd_endgrent(struct winbindd_cli_state *state)
990 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
992 free_getent_state(state->getgrent_state);
993 state->getgrent_initialized = False;
994 state->getgrent_state = NULL;
998 /* Get the list of domain groups and domain aliases for a domain. We fill in
999 the sam_entries and num_sam_entries fields with domain group information.
1000 Return True if some groups were returned, False otherwise. */
1002 bool get_sam_group_entries(struct getent_state *ent)
1006 struct acct_info *name_list = NULL;
1007 TALLOC_CTX *mem_ctx;
1008 bool result = False;
1009 struct acct_info *sam_grp_entries = NULL;
1010 struct winbindd_domain *domain;
1012 if (ent->got_sam_entries)
1015 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1016 ent->domain_name))) {
1017 DEBUG(1, ("get_sam_group_entries: "
1018 "could not create talloc context!\n"));
1022 /* Free any existing group info */
1024 SAFE_FREE(ent->sam_entries);
1025 ent->num_sam_entries = 0;
1026 ent->got_sam_entries = True;
1028 /* Enumerate domain groups */
1032 if (!(domain = find_domain_from_name(ent->domain_name))) {
1033 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1038 /* always get the domain global groups */
1040 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1043 if (!NT_STATUS_IS_OK(status)) {
1044 DEBUG(3, ("get_sam_group_entries: "
1045 "could not enumerate domain groups! Error: %s\n",
1046 nt_errstr(status)));
1051 /* Copy entries into return buffer */
1054 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1056 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1057 "memory for %d domain groups!\n",
1062 memcpy(name_list, sam_grp_entries,
1063 num_entries * sizeof(struct acct_info));
1066 ent->num_sam_entries = num_entries;
1068 /* get the domain local groups if we are a member of a native win2k
1069 * domain and are not using LDAP to get the groups */
1071 if ( ( lp_security() != SEC_ADS && domain->native_mode
1072 && domain->primary) || domain->internal )
1074 DEBUG(4,("get_sam_group_entries: %s domain; "
1075 "enumerating local groups as well\n",
1076 domain->native_mode ? "Native Mode 2k":
1077 "BUILTIN or local"));
1079 status = domain->methods->enum_local_groups(domain, mem_ctx,
1083 if ( !NT_STATUS_IS_OK(status) ) {
1084 DEBUG(3,("get_sam_group_entries: "
1085 "Failed to enumerate "
1086 "domain local groups with error %s!\n",
1087 nt_errstr(status)));
1091 DEBUG(4,("get_sam_group_entries: "
1092 "Returned %d local groups\n",
1095 /* Copy entries into return buffer */
1097 if ( num_entries ) {
1098 name_list = SMB_REALLOC_ARRAY(name_list,
1100 ent->num_sam_entries+
1103 DEBUG(0,("get_sam_group_entries: "
1104 "Failed to realloc more memory "
1105 "for %d local groups!\n",
1111 memcpy(&name_list[ent->num_sam_entries],
1113 num_entries * sizeof(struct acct_info));
1116 ent->num_sam_entries += num_entries;
1120 /* Fill in remaining fields */
1122 ent->sam_entries = name_list;
1123 ent->sam_entry_index = 0;
1125 result = (ent->num_sam_entries > 0);
1128 talloc_destroy(mem_ctx);
1133 /* Fetch next group entry from ntdom database */
1135 #define MAX_GETGRENT_GROUPS 500
1137 void winbindd_getgrent(struct winbindd_cli_state *state)
1139 struct getent_state *ent;
1140 struct winbindd_gr *group_list = NULL;
1141 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1142 char *gr_mem_list = NULL;
1144 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1146 /* Check user has enabled this */
1148 if (!lp_winbind_enum_groups()) {
1149 request_error(state);
1153 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1155 if (num_groups == 0) {
1156 request_error(state);
1160 group_list = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups);
1162 request_error(state);
1165 /* will be freed by process_request() */
1166 state->response.extra_data.data = group_list;
1168 memset(state->response.extra_data.data, '\0',
1169 num_groups * sizeof(struct winbindd_gr) );
1171 state->response.data.num_entries = 0;
1173 if (!state->getgrent_initialized)
1174 winbindd_setgrent_internal(state);
1176 if (!(ent = state->getgrent_state)) {
1177 request_error(state);
1181 /* Start sending back groups */
1183 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1184 struct acct_info *name_list = NULL;
1185 fstring domain_group_name;
1191 struct winbindd_domain *domain;
1193 /* Do we need to fetch another chunk of groups? */
1197 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1198 ent->sam_entry_index, ent->num_sam_entries));
1200 if (ent->num_sam_entries == ent->sam_entry_index) {
1202 while(ent && !get_sam_group_entries(ent)) {
1203 struct getent_state *next_ent;
1205 DEBUG(10, ("freeing state info for domain %s\n",
1208 /* Free state information for this domain */
1210 SAFE_FREE(ent->sam_entries);
1212 next_ent = ent->next;
1213 DLIST_REMOVE(state->getgrent_state, ent);
1219 /* No more domains */
1225 name_list = (struct acct_info *)ent->sam_entries;
1227 if (!(domain = find_domain_from_name(ent->domain_name))) {
1228 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1234 /* Lookup group info */
1236 sid_copy(&group_sid, &domain->sid);
1237 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1239 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->name, &group_sid,
1242 enum lsa_SidType type;
1244 DEBUG(10, ("SID %s not in idmap\n",
1245 sid_string_dbg(&group_sid)));
1247 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1248 DEBUG(1,("could not look up gid for group %s\n",
1249 name_list[ent->sam_entry_index].acct_name));
1250 ent->sam_entry_index++;
1254 if ((type != SID_NAME_DOM_GRP) &&
1255 (type != SID_NAME_ALIAS) &&
1256 (type != SID_NAME_WKN_GRP)) {
1257 DEBUG(1, ("Group %s is a %s, not a group\n",
1258 sid_type_lookup(type),
1259 name_list[ent->sam_entry_index].acct_name));
1260 ent->sam_entry_index++;
1266 DEBUG(10, ("got gid %lu for group %lu\n",
1267 (unsigned long)group_gid,
1268 (unsigned long)name_list[ent->sam_entry_index].rid));
1270 /* Fill in group entry */
1272 fill_domain_username(domain_group_name, ent->domain_name,
1273 name_list[ent->sam_entry_index].acct_name, True);
1275 result = fill_grent(&group_list[group_list_ndx],
1277 name_list[ent->sam_entry_index].acct_name,
1280 /* Fill in group membership entry */
1283 size_t num_gr_mem = 0;
1285 group_list[group_list_ndx].num_gr_mem = 0;
1289 /* Get group membership */
1290 if (state->request.cmd == WINBINDD_GETGRLST) {
1293 sid_copy(&member_sid, &domain->sid);
1294 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1295 result = fill_grent_mem(
1301 &gr_mem, &gr_mem_len);
1303 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1308 /* Append to group membership list */
1309 gr_mem_list = (char *)SMB_REALLOC(
1310 gr_mem_list, gr_mem_list_len + gr_mem_len);
1313 (group_list[group_list_ndx].num_gr_mem != 0)) {
1314 DEBUG(0, ("out of memory\n"));
1315 gr_mem_list_len = 0;
1319 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1320 gr_mem_list_len, (unsigned int)gr_mem_len));
1322 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1327 group_list[group_list_ndx].gr_mem_ofs =
1330 gr_mem_list_len += gr_mem_len;
1333 ent->sam_entry_index++;
1335 /* Add group to return list */
1339 DEBUG(10, ("adding group num_entries = %d\n",
1340 state->response.data.num_entries));
1343 state->response.data.num_entries++;
1345 state->response.length +=
1346 sizeof(struct winbindd_gr);
1349 DEBUG(0, ("could not lookup domain group %s\n",
1350 domain_group_name));
1354 /* Copy the list of group memberships to the end of the extra data */
1356 if (group_list_ndx == 0)
1359 state->response.extra_data.data = SMB_REALLOC(
1360 state->response.extra_data.data,
1361 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1363 if (!state->response.extra_data.data) {
1364 DEBUG(0, ("out of memory\n"));
1366 SAFE_FREE(gr_mem_list);
1367 request_error(state);
1371 memcpy(&((char *)state->response.extra_data.data)
1372 [group_list_ndx * sizeof(struct winbindd_gr)],
1373 gr_mem_list, gr_mem_list_len);
1375 state->response.length += gr_mem_list_len;
1377 DEBUG(10, ("returning %d groups, length = %d\n",
1378 group_list_ndx, gr_mem_list_len));
1380 /* Out of domains */
1384 SAFE_FREE(gr_mem_list);
1386 if (group_list_ndx > 0)
1389 request_error(state);
1392 /* List domain groups without mapping to unix ids */
1393 void winbindd_list_groups(struct winbindd_cli_state *state)
1395 winbindd_list_ent(state, LIST_GROUPS);
1398 /* Get user supplementary groups. This is much quicker than trying to
1399 invert the groups database. We merge the groups from the gids and
1400 other_sids info3 fields as trusted domain, universal group
1401 memberships, and nested groups (win2k native mode only) are not
1402 returned by the getgroups RPC call but are present in the info3. */
1404 struct getgroups_state {
1405 struct winbindd_cli_state *state;
1406 struct winbindd_domain *domain;
1411 const DOM_SID *token_sids;
1412 size_t i, num_token_sids;
1415 size_t num_token_gids;
1418 static void getgroups_usersid_recv(void *private_data, bool success,
1419 const DOM_SID *sid, enum lsa_SidType type);
1420 static void getgroups_tokensids_recv(void *private_data, bool success,
1421 DOM_SID *token_sids, size_t num_token_sids);
1422 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1424 void winbindd_getgroups(struct winbindd_cli_state *state)
1426 struct getgroups_state *s;
1428 /* Ensure null termination */
1429 state->request.data.username
1430 [sizeof(state->request.data.username)-1]='\0';
1432 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1433 state->request.data.username));
1435 /* Parse domain and username */
1437 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1439 DEBUG(0, ("talloc failed\n"));
1440 request_error(state);
1446 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1448 if (!parse_domain_user_talloc(state->mem_ctx,
1449 state->request.data.username,
1450 &s->domname, &s->username)) {
1451 DEBUG(5, ("Could not parse domain user: %s\n",
1452 state->request.data.username));
1454 /* error out if we do not have nested group support */
1456 if ( !lp_winbind_nested_groups() ) {
1457 request_error(state);
1461 s->domname = talloc_strdup(state->mem_ctx,
1462 get_global_sam_name());
1463 s->username = talloc_strdup(state->mem_ctx,
1464 state->request.data.username);
1467 /* Get info for the domain (either by short domain name or
1468 DNS name in the case of a UPN) */
1470 s->domain = find_domain_from_name_noinit(s->domname);
1472 char *p = strchr(s->username, '@');
1475 s->domain = find_domain_from_name_noinit(p+1);
1480 if (s->domain == NULL) {
1481 DEBUG(7, ("could not find domain entry for domain %s\n",
1483 request_error(state);
1487 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1488 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1489 "getgroups() for %s\\%s.\n", s->domname,
1491 request_error(state);
1495 /* Get rid and name type from name. The following costs 1 packet */
1497 winbindd_lookupname_async(state->mem_ctx,
1498 s->domname, s->username,
1499 getgroups_usersid_recv,
1500 WINBINDD_GETGROUPS, s);
1503 static void getgroups_usersid_recv(void *private_data, bool success,
1504 const DOM_SID *sid, enum lsa_SidType type)
1506 struct getgroups_state *s =
1507 (struct getgroups_state *)private_data;
1510 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1511 request_error(s->state);
1515 sid_copy(&s->user_sid, sid);
1517 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1518 getgroups_tokensids_recv, s);
1521 static void getgroups_tokensids_recv(void *private_data, bool success,
1522 DOM_SID *token_sids, size_t num_token_sids)
1524 struct getgroups_state *s =
1525 (struct getgroups_state *)private_data;
1527 /* We need at least the user sid and the primary group in the token,
1528 * otherwise it's an error */
1530 if ((!success) || (num_token_sids < 2)) {
1531 request_error(s->state);
1535 s->token_sids = token_sids;
1536 s->num_token_sids = num_token_sids;
1539 s->token_gids = NULL;
1540 s->num_token_gids = 0;
1542 getgroups_sid2gid_recv(s, False, 0);
1545 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1547 struct getgroups_state *s =
1548 (struct getgroups_state *)private_data;
1551 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1553 &s->num_token_gids)) {
1558 if (s->i < s->num_token_sids) {
1559 const DOM_SID *sid = &s->token_sids[s->i];
1562 if (sid_equal(sid, &s->user_sid)) {
1563 getgroups_sid2gid_recv(s, False, 0);
1567 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1568 getgroups_sid2gid_recv, s);
1572 s->state->response.data.num_entries = s->num_token_gids;
1573 if (s->num_token_gids) {
1574 /* s->token_gids are talloced */
1575 s->state->response.extra_data.data =
1576 smb_xmemdup(s->token_gids,
1577 s->num_token_gids * sizeof(gid_t));
1578 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1580 request_ok(s->state);
1583 /* Get user supplementary sids. This is equivalent to the
1584 winbindd_getgroups() function but it involves a SID->SIDs mapping
1585 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1586 idmap. This call is designed to be used with applications that need
1587 to do ACL evaluation themselves. Note that the cached info3 data is
1590 this function assumes that the SID that comes in is a user SID. If
1591 you pass in another type of SID then you may get unpredictable
1595 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1598 void winbindd_getusersids(struct winbindd_cli_state *state)
1602 /* Ensure null termination */
1603 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1605 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1606 if (user_sid == NULL) {
1607 DEBUG(1, ("talloc failed\n"));
1608 request_error(state);
1612 if (!string_to_sid(user_sid, state->request.data.sid)) {
1613 DEBUG(1, ("Could not get convert sid %s from string\n",
1614 state->request.data.sid));
1615 request_error(state);
1619 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1623 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1626 struct winbindd_cli_state *state =
1627 (struct winbindd_cli_state *)private_data;
1629 unsigned ofs, ret_size = 0;
1633 request_error(state);
1637 /* work out the response size */
1638 for (i = 0; i < num_sids; i++) {
1640 sid_to_fstring(s, &sids[i]);
1641 ret_size += strlen(s) + 1;
1644 /* build the reply */
1645 ret = (char *)SMB_MALLOC(ret_size);
1647 DEBUG(0, ("malloc failed\n"));
1648 request_error(state);
1652 for (i = 0; i < num_sids; i++) {
1654 sid_to_fstring(s, &sids[i]);
1655 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1656 ofs += strlen(ret+ofs) + 1;
1659 /* Send data back to client */
1660 state->response.data.num_entries = num_sids;
1661 state->response.extra_data.data = ret;
1662 state->response.length += ret_size;
1666 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1669 struct winbindd_domain *domain;
1671 /* Ensure null termination */
1672 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1674 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1675 DEBUG(1, ("Could not get convert sid %s from string\n",
1676 state->request.data.sid));
1677 request_error(state);
1681 /* Get info for the domain */
1682 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1683 DEBUG(0,("could not find domain entry for sid %s\n",
1684 sid_string_dbg(&user_sid)));
1685 request_error(state);
1689 sendto_domain(state, domain);
1692 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1693 struct winbindd_cli_state *state)
1703 /* Ensure null termination */
1704 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1706 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1707 DEBUG(1, ("Could not get convert sid %s from string\n",
1708 state->request.data.sid));
1709 return WINBINDD_ERROR;
1712 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1713 &user_sid, &num_groups,
1715 if (!NT_STATUS_IS_OK(status))
1716 return WINBINDD_ERROR;
1718 if (num_groups == 0) {
1719 state->response.data.num_entries = 0;
1720 state->response.extra_data.data = NULL;
1724 if (!print_sidlist(state->mem_ctx,
1726 &sidstring, &len)) {
1727 DEBUG(0, ("talloc failed\n"));
1728 return WINBINDD_ERROR;
1731 state->response.extra_data.data = SMB_STRDUP(sidstring);
1732 if (!state->response.extra_data.data) {
1733 return WINBINDD_ERROR;
1735 state->response.length += len+1;
1736 state->response.data.num_entries = num_groups;