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, char **pp_members, size_t *p_num_members)
56 struct winbindd_domain *domain;
59 char *domain_name = NULL;
61 enum lsa_SidType type;
70 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
72 if (mem_ctx == NULL) {
73 DEBUG(1, ("talloc_init failed\n"));
77 sid_copy(&dom_sid, sid);
78 sid_split_rid(&dom_sid, &rid);
80 domain = find_lookup_domain_from_sid(sid);
83 DEBUG(3, ("Could not find domain for sid %s\n",
84 sid_string_dbg(sid)));
88 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
89 &domain_name, &name, &type);
91 if (!NT_STATUS_IS_OK(result)) {
92 DEBUG(3, ("sid_to_name failed for sid %s\n",
93 sid_string_dbg(sid)));
97 DEBUG(10, ("Found name %s, type %d\n", name, type));
99 if (type == SID_NAME_USER) {
100 add_member(domain_name, name, pp_members, p_num_members);
104 if (type != SID_NAME_DOM_GRP) {
105 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
110 /* Expand the domain group, this must be done via the target domain */
112 domain = find_domain_from_sid(sid);
114 if (domain == NULL) {
115 DEBUG(3, ("Could not find domain from SID %s\n",
116 sid_string_dbg(sid)));
120 result = domain->methods->lookup_groupmem(domain, mem_ctx,
125 if (!NT_STATUS_IS_OK(result)) {
126 DEBUG(10, ("Could not lookup group members for %s: %s\n",
127 name, nt_errstr(result)));
131 for (i=0; i<num_names; i++) {
132 DEBUG(10, ("Adding group member SID %s\n",
133 sid_string_dbg(&sid_mem[i])));
135 if (types[i] != SID_NAME_USER) {
136 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
137 "Ignoring.\n", names[i], name));
141 add_member(NULL, names[i], pp_members, p_num_members);
145 talloc_destroy(mem_ctx);
149 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
151 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
154 size_t i, num_members;
160 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
164 for (i=0; i<num_members; i++) {
165 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
168 TALLOC_FREE(members);
170 if (*gr_mem != NULL) {
173 /* We have at least one member, strip off the last "," */
174 len = strlen(*gr_mem);
175 (*gr_mem)[len-1] = '\0';
182 /* Fill a grent structure from various other information */
184 static bool fill_grent(struct winbindd_gr *gr, const char *dom_name,
185 const char *gr_name, gid_t unix_gid)
187 fstring full_group_name;
189 fill_domain_username( full_group_name, dom_name, gr_name, True );
191 gr->gr_gid = unix_gid;
193 /* Group name and password */
195 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
196 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
201 /***********************************************************************
202 If "enum users" is set to false, and the group being looked
203 up is the Domain Users SID: S-1-5-domain-513, then for the
204 list of members check if the querying user is in that group,
205 and if so only return that user as the gr_mem array.
206 We can change this to a different parameter than "enum users"
207 if neccessaey, or parameterize the group list we do this for.
208 ***********************************************************************/
210 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
211 struct winbindd_domain *domain,
212 struct winbindd_cli_state *state,
214 enum lsa_SidType group_name_type,
215 size_t *num_gr_mem, char **gr_mem,
218 DOM_SID querying_user_sid;
219 DOM_SID *pquerying_user_sid = NULL;
220 uint32 num_groups = 0;
221 DOM_SID *user_sids = NULL;
222 bool u_in_group = False;
225 unsigned int buf_len = 0;
228 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
232 uid_t ret_uid = (uid_t)-1;
233 if (sys_getpeereid(state->sock, &ret_uid)==0) {
234 /* We know who's asking - look up their SID if
235 it's one we've mapped before. */
236 status = idmap_uid_to_sid(&querying_user_sid, ret_uid);
237 if (NT_STATUS_IS_OK(status)) {
238 pquerying_user_sid = &querying_user_sid;
239 DEBUG(10,("fill_grent_mem_domain_users: querying uid %u -> %s\n",
240 (unsigned int)ret_uid,
241 sid_string_dbg(pquerying_user_sid)));
246 /* Only look up if it was a winbindd user in this domain. */
247 if (pquerying_user_sid &&
248 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
250 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
251 sid_string_dbg(pquerying_user_sid) ));
253 status = domain->methods->lookup_usergroups(domain,
258 if (!NT_STATUS_IS_OK(status)) {
259 DEBUG(1, ("fill_grent_mem_domain_users: lookup_usergroups failed "
260 "for sid %s in domain %s (error: %s)\n",
261 sid_string_dbg(pquerying_user_sid),
267 for (i = 0; i < num_groups; i++) {
268 if (sid_equal(group_sid, &user_sids[i])) {
269 /* User is in Domain Users, add their name
270 as the only group member. */
279 char *domainname = NULL;
280 char *username = NULL;
282 enum lsa_SidType type;
284 DEBUG(10,("fill_grent_mem_domain_users: sid %s in 'Domain Users' in domain %s\n",
285 sid_string_dbg(pquerying_user_sid),
288 status = domain->methods->sid_to_name(domain, mem_ctx,
293 if (!NT_STATUS_IS_OK(status)) {
294 DEBUG(1, ("could not lookup username for user "
295 "sid %s in domain %s (error: %s)\n",
296 sid_string_dbg(pquerying_user_sid),
301 fill_domain_username(name, domain->name, username, True);
304 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
305 DEBUG(1, ("out of memory\n"));
308 memcpy(buf, name, buf_len);
310 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
311 "'Domain Users' in domain %s\n",
312 name, domain->name ));
314 /* user is the only member */
319 *gr_mem_len = buf_len;
321 DEBUG(10, ("fill_grent_mem_domain_users: num_mem = %u, len = %u, mem = %s\n",
322 (unsigned int)*num_gr_mem,
323 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
328 /***********************************************************************
329 Add names to a list. Assumes a canonical version of the string
331 ***********************************************************************/
333 static int namecmp( const void *a, const void *b )
335 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
338 static void sort_unique_list(char ***list, uint32 *n_list)
342 /* search for duplicates for sorting and looking for matching
345 qsort(*list, *n_list, sizeof(char*), QSORT_CAST namecmp);
347 for (i=1; i < *n_list; i++) {
348 if (strcmp((*list)[i-1], (*list)[i]) == 0) {
349 memmove(&((*list)[i-1]), &((*list)[i]),
350 sizeof(char*)*((*n_list)-i));
356 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
357 char ***list, uint32 *n_list,
358 char **names, uint32 n_names )
360 char **new_list = NULL;
361 uint32 n_new_list = 0;
364 if ( !names || (n_names == 0) )
367 /* Alloc the maximum size we'll need */
369 if ( *list == NULL ) {
370 if ( (new_list = TALLOC_ARRAY( ctx, char *, n_names )) == NULL )
371 return NT_STATUS_NO_MEMORY;
372 n_new_list = n_names;
374 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
375 (*n_list) + n_names );
377 return NT_STATUS_NO_MEMORY;
378 n_new_list = (*n_list) + n_names;
383 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
384 new_list[i] = talloc_strdup( new_list, names[j] );
388 *n_list = n_new_list;
393 /***********************************************************************
394 ***********************************************************************/
396 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
397 struct winbindd_domain *d,
398 DOM_SID *glist, uint32 n_glist,
399 DOM_SID **new_glist, uint32 *n_new_glist,
400 char ***members, uint32 *n_members )
403 NTSTATUS status = NT_STATUS_OK;
404 uint32 num_names = 0;
405 uint32 *name_types = NULL;
407 DOM_SID *sid_mem = NULL;
408 TALLOC_CTX *tmp_ctx = NULL;
409 DOM_SID *new_groups = NULL;
410 size_t new_groups_size = 0;
417 for ( i=0; i<n_glist; i++ ) {
418 tmp_ctx = talloc_new( ctx );
420 /* Lookup the group membership */
422 status = d->methods->lookup_groupmem(d, tmp_ctx,
423 &glist[i], &num_names,
426 if ( !NT_STATUS_IS_OK(status) )
429 /* Separate users and groups into two lists */
431 for ( j=0; j<num_names; j++ ) {
434 if ( name_types[j] == SID_NAME_USER ||
435 name_types[j] == SID_NAME_COMPUTER )
437 status = add_names_to_list( ctx, members,
440 if ( !NT_STATUS_IS_OK(status) )
447 if ( name_types[j] == SID_NAME_DOM_GRP ||
448 name_types[j] == SID_NAME_ALIAS )
450 status = add_sid_to_array_unique(ctx,
454 if (!NT_STATUS_IS_OK(status)) {
462 TALLOC_FREE( tmp_ctx );
465 *new_glist = new_groups;
466 *n_new_glist = (uint32)new_groups_size;
469 TALLOC_FREE( tmp_ctx );
474 /***********************************************************************
475 Fill in the group membership field of a NT group given by group_sid
476 ***********************************************************************/
478 static bool fill_grent_mem(struct winbindd_domain *domain,
479 struct winbindd_cli_state *state,
481 enum lsa_SidType group_name_type,
482 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
484 uint32 num_names = 0;
485 unsigned int buf_len = 0, buf_ndx = 0, i;
486 char **names = NULL, *buf = NULL;
490 DOM_SID *glist = NULL;
491 DOM_SID *new_glist = NULL;
492 uint32 n_glist, n_new_glist;
493 int max_depth = lp_winbind_expand_groups();
495 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
498 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
500 /* Initialize with no members */
504 /* HACK ALERT!! This whole routine does not cope with group members
505 * from more than one domain, ie aliases. Thus we have to work it out
506 * ourselves in a special routine. */
508 if (domain->internal) {
509 result = fill_passdb_alias_grmem(domain, group_sid,
515 /* Verify name type */
517 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
518 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
520 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
521 sid_string_dbg(group_sid),
522 domain->name, group_name_type));
526 /* OPTIMIZATION / HACK. See comment in
527 fill_grent_mem_domusers() */
529 sid_peek_rid( group_sid, &group_rid );
530 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
531 result = fill_grent_mem_domusers( mem_ctx, domain, state,
532 group_sid, group_name_type,
538 /* Real work goes here. Create a list of group names to
539 expand starting with the initial one. Pass that to
540 expand_groups() which returns a list of more group names
541 to expand. Do this up to the max search depth. */
543 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
545 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
548 sid_copy( &glist[0], group_sid );
551 for ( i=0; i<max_depth && glist; i++ ) {
552 uint32 n_members = 0;
553 char **members = NULL;
556 nt_status = expand_groups( mem_ctx, domain,
558 &new_glist, &n_new_glist,
559 &members, &n_members);
560 if ( !NT_STATUS_IS_OK(nt_status) ) {
565 /* Add new group members to list */
567 nt_status = add_names_to_list( mem_ctx, &names, &num_names,
568 members, n_members );
569 if ( !NT_STATUS_IS_OK(nt_status) ) {
574 TALLOC_FREE( members );
576 /* If we have no more groups to expand, break out
579 if (new_glist == NULL)
585 n_glist = n_new_glist;
587 TALLOC_FREE( glist );
589 sort_unique_list(&names, &num_names);
591 DEBUG(10, ("looked up %d names\n", num_names));
594 /* Add members to list */
596 for (i = 0; i < num_names; i++) {
599 DEBUG(10, ("processing name %s\n", names[i]));
601 len = strlen(names[i]);
603 /* Add to list or calculate buffer length */
606 buf_len += len + 1; /* List is comma separated */
608 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
610 DEBUG(10, ("appending %s at ndx %d\n",
612 parse_add_domuser(&buf[buf_ndx], names[i], &len);
619 /* Allocate buffer */
621 if (!buf && buf_len != 0) {
622 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
623 DEBUG(1, ("out of memory\n"));
627 memset(buf, 0, buf_len);
633 if (buf && buf_ndx > 0) {
634 buf[buf_ndx - 1] = '\0';
638 *gr_mem_len = buf_len;
640 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
641 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
646 talloc_destroy(mem_ctx);
648 DEBUG(10, ("fill_grent_mem returning %d\n", result));
653 static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid );
655 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
656 enum lsa_SidType type )
658 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
661 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
662 request_error(state);
666 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
667 DEBUG(5,("getgrnam_recv: not a group!\n"));
668 request_error(state);
672 winbindd_getgrsid( state, *sid );
676 /* Return a group structure from a group name */
678 void winbindd_getgrnam(struct winbindd_cli_state *state)
680 struct winbindd_domain *domain;
681 fstring name_domain, name_group;
684 /* Ensure null termination */
685 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
687 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
688 state->request.data.groupname));
690 /* Parse domain and groupname */
692 memset(name_group, 0, sizeof(fstring));
694 tmp = state->request.data.groupname;
696 name_domain[0] = '\0';
697 name_group[0] = '\0';
699 parse_domain_user(tmp, name_domain, name_group);
701 /* if no domain or our local domain and no local tdb group, default to
702 * our local domain for aliases */
704 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
705 fstrcpy(name_domain, get_global_sam_name());
708 /* Get info for the domain */
710 if ((domain = find_domain_from_name(name_domain)) == NULL) {
711 DEBUG(3, ("could not get domain sid for domain %s\n",
713 request_error(state);
716 /* should we deal with users for our domain? */
718 if ( lp_winbind_trusted_domains_only() && domain->primary) {
719 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
720 "getgrnam() for %s\\%s.\n", name_domain, name_group));
721 request_error(state);
725 /* Get rid and name type from name */
727 ws_name_replace( name_group, WB_REPLACE_CHAR );
729 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
730 getgrnam_recv, WINBINDD_GETGRNAM, state );
733 struct getgrsid_state {
734 struct winbindd_cli_state *state;
735 struct winbindd_domain *domain;
737 enum lsa_SidType group_type;
742 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
744 struct getgrsid_state *s =
745 (struct getgrsid_state *)private_data;
746 struct winbindd_domain *domain;
750 fstring dom_name, group_name;
753 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
754 request_error(s->state);
760 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
761 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
762 request_error(s->state);
767 /* Fill in group structure */
769 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
770 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
771 request_error(s->state);
775 if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) ||
776 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
777 &num_gr_mem, &gr_mem, &gr_mem_len))
779 request_error(s->state);
783 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
785 /* Group membership lives at start of extra data */
787 s->state->response.data.gr.gr_mem_ofs = 0;
789 s->state->response.length += gr_mem_len;
790 s->state->response.extra_data.data = gr_mem;
792 request_ok(s->state);
795 static void getgrsid_lookupsid_recv( void *private_data, bool success,
796 const char *dom_name, const char *name,
797 enum lsa_SidType name_type )
799 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
802 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
803 request_error(s->state);
807 /* either it's a domain group, a domain local group, or a
808 local group in an internal domain */
810 if ( !( (name_type==SID_NAME_DOM_GRP) ||
811 ((name_type==SID_NAME_ALIAS) &&
812 (s->domain->primary || s->domain->internal)) ) )
814 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
815 dom_name, name, name_type));
816 request_error(s->state);
820 if ( (s->group_name = talloc_asprintf( s->state->mem_ctx,
823 *lp_winbind_separator(),
826 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
827 request_error(s->state);
831 s->group_type = name_type;
833 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
834 getgrsid_sid2gid_recv, s);
837 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
839 struct getgrsid_state *s;
841 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
842 DEBUG(0, ("talloc failed\n"));
843 request_error(state);
849 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
850 DEBUG(3, ("Could not find domain for sid %s\n",
851 sid_string_dbg(&group_sid)));
852 request_error(state);
856 sid_copy(&s->group_sid, &group_sid);
858 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
859 getgrsid_lookupsid_recv, s );
863 static void getgrgid_recv(void *private_data, bool success, const char *sid)
865 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
866 enum lsa_SidType name_type;
870 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
871 (unsigned long)(state->request.data.gid), sid));
873 string_to_sid(&group_sid, sid);
874 winbindd_getgrsid(state, group_sid);
878 /* Ok, this might be "ours", i.e. an alias */
879 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
880 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
881 (name_type == SID_NAME_ALIAS)) {
882 /* Hey, got an alias */
883 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
884 (unsigned long)(state->request.data.gid), sid));
885 winbindd_getgrsid(state, group_sid);
889 DEBUG(1, ("could not convert gid %lu to sid\n",
890 (unsigned long)state->request.data.gid));
891 request_error(state);
894 /* Return a group structure from a gid number */
895 void winbindd_getgrgid(struct winbindd_cli_state *state)
897 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
898 (unsigned long)state->request.data.gid));
900 /* always use the async interface */
901 winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
905 * set/get/endgrent functions
908 /* "Rewind" file pointer for group database enumeration */
910 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
912 struct winbindd_domain *domain;
914 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
916 /* Check user has enabled this */
918 if (!lp_winbind_enum_groups()) {
922 /* Free old static data if it exists */
924 if (state->getgrent_state != NULL) {
925 free_getent_state(state->getgrent_state);
926 state->getgrent_state = NULL;
929 /* Create sam pipes for each domain we know about */
931 for (domain = domain_list(); domain != NULL; domain = domain->next) {
932 struct getent_state *domain_state;
934 /* Create a state record for this domain */
936 /* don't add our domaina if we are a PDC or if we
937 are a member of a Samba domain */
939 if ( lp_winbind_trusted_domains_only() && domain->primary )
945 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
946 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
950 ZERO_STRUCTP(domain_state);
952 fstrcpy(domain_state->domain_name, domain->name);
954 /* Add to list of open domains */
956 DLIST_ADD(state->getgrent_state, domain_state);
959 state->getgrent_initialized = True;
963 void winbindd_setgrent(struct winbindd_cli_state *state)
965 if (winbindd_setgrent_internal(state)) {
968 request_error(state);
972 /* Close file pointer to ntdom group database */
974 void winbindd_endgrent(struct winbindd_cli_state *state)
976 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
978 free_getent_state(state->getgrent_state);
979 state->getgrent_initialized = False;
980 state->getgrent_state = NULL;
984 /* Get the list of domain groups and domain aliases for a domain. We fill in
985 the sam_entries and num_sam_entries fields with domain group information.
986 Return True if some groups were returned, False otherwise. */
988 bool get_sam_group_entries(struct getent_state *ent)
992 struct acct_info *name_list = NULL;
995 struct acct_info *sam_grp_entries = NULL;
996 struct winbindd_domain *domain;
998 if (ent->got_sam_entries)
1001 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1002 ent->domain_name))) {
1003 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
1007 /* Free any existing group info */
1009 SAFE_FREE(ent->sam_entries);
1010 ent->num_sam_entries = 0;
1011 ent->got_sam_entries = True;
1013 /* Enumerate domain groups */
1017 if (!(domain = find_domain_from_name(ent->domain_name))) {
1018 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
1022 /* always get the domain global groups */
1024 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1026 if (!NT_STATUS_IS_OK(status)) {
1027 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
1032 /* Copy entries into return buffer */
1035 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
1036 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
1041 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
1044 ent->num_sam_entries = num_entries;
1046 /* get the domain local groups if we are a member of a native win2k domain
1047 and are not using LDAP to get the groups */
1049 if ( ( lp_security() != SEC_ADS && domain->native_mode
1050 && domain->primary) || domain->internal )
1052 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
1053 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
1055 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
1057 if ( !NT_STATUS_IS_OK(status) ) {
1058 DEBUG(3,("get_sam_group_entries: "
1059 "Failed to enumerate "
1060 "domain local groups with error %s!\n",
1061 nt_errstr(status)));
1065 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
1067 /* Copy entries into return buffer */
1069 if ( num_entries ) {
1070 if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
1072 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
1078 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
1079 num_entries * sizeof(struct acct_info) );
1082 ent->num_sam_entries += num_entries;
1086 /* Fill in remaining fields */
1088 ent->sam_entries = name_list;
1089 ent->sam_entry_index = 0;
1091 result = (ent->num_sam_entries > 0);
1094 talloc_destroy(mem_ctx);
1099 /* Fetch next group entry from ntdom database */
1101 #define MAX_GETGRENT_GROUPS 500
1103 void winbindd_getgrent(struct winbindd_cli_state *state)
1105 struct getent_state *ent;
1106 struct winbindd_gr *group_list = NULL;
1107 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1108 char *gr_mem_list = NULL;
1110 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1112 /* Check user has enabled this */
1114 if (!lp_winbind_enum_groups()) {
1115 request_error(state);
1119 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1121 if (num_groups == 0) {
1122 request_error(state);
1126 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
1127 request_error(state);
1131 memset(state->response.extra_data.data, '\0',
1132 num_groups * sizeof(struct winbindd_gr) );
1134 state->response.data.num_entries = 0;
1136 group_list = (struct winbindd_gr *)state->response.extra_data.data;
1138 if (!state->getgrent_initialized)
1139 winbindd_setgrent_internal(state);
1141 if (!(ent = state->getgrent_state)) {
1142 request_error(state);
1146 /* Start sending back groups */
1148 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1149 struct acct_info *name_list = NULL;
1150 fstring domain_group_name;
1156 struct winbindd_domain *domain;
1158 /* Do we need to fetch another chunk of groups? */
1162 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1163 ent->sam_entry_index, ent->num_sam_entries));
1165 if (ent->num_sam_entries == ent->sam_entry_index) {
1167 while(ent && !get_sam_group_entries(ent)) {
1168 struct getent_state *next_ent;
1170 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
1172 /* Free state information for this domain */
1174 SAFE_FREE(ent->sam_entries);
1176 next_ent = ent->next;
1177 DLIST_REMOVE(state->getgrent_state, ent);
1183 /* No more domains */
1189 name_list = (struct acct_info *)ent->sam_entries;
1192 find_domain_from_name(ent->domain_name))) {
1193 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
1198 /* Lookup group info */
1200 sid_copy(&group_sid, &domain->sid);
1201 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1203 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) {
1205 enum lsa_SidType type;
1207 DEBUG(10, ("SID %s not in idmap\n",
1208 sid_string_dbg(&group_sid)));
1210 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1211 DEBUG(1, ("could not look up gid for group "
1213 name_list[ent->sam_entry_index].acct_name));
1214 ent->sam_entry_index++;
1218 if ((type != SID_NAME_DOM_GRP) &&
1219 (type != SID_NAME_ALIAS) &&
1220 (type != SID_NAME_WKN_GRP)) {
1221 DEBUG(1, ("Group %s is a %s, not a group\n",
1222 sid_type_lookup(type),
1223 name_list[ent->sam_entry_index].acct_name));
1224 ent->sam_entry_index++;
1230 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
1231 (unsigned long)name_list[ent->sam_entry_index].rid));
1233 /* Fill in group entry */
1235 fill_domain_username(domain_group_name, ent->domain_name,
1236 name_list[ent->sam_entry_index].acct_name, True);
1238 result = fill_grent(&group_list[group_list_ndx],
1240 name_list[ent->sam_entry_index].acct_name,
1243 /* Fill in group membership entry */
1246 size_t num_gr_mem = 0;
1248 group_list[group_list_ndx].num_gr_mem = 0;
1252 /* Get group membership */
1253 if (state->request.cmd == WINBINDD_GETGRLST) {
1256 sid_copy(&member_sid, &domain->sid);
1257 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1258 result = fill_grent_mem(
1264 &gr_mem, &gr_mem_len);
1266 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1271 /* Append to group membership list */
1272 gr_mem_list = (char *)SMB_REALLOC(
1273 gr_mem_list, gr_mem_list_len + gr_mem_len);
1275 if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
1276 DEBUG(0, ("out of memory\n"));
1277 gr_mem_list_len = 0;
1281 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1282 gr_mem_list_len, (unsigned int)gr_mem_len));
1284 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1289 group_list[group_list_ndx].gr_mem_ofs =
1292 gr_mem_list_len += gr_mem_len;
1295 ent->sam_entry_index++;
1297 /* Add group to return list */
1301 DEBUG(10, ("adding group num_entries = %d\n",
1302 state->response.data.num_entries));
1305 state->response.data.num_entries++;
1307 state->response.length +=
1308 sizeof(struct winbindd_gr);
1311 DEBUG(0, ("could not lookup domain group %s\n",
1312 domain_group_name));
1316 /* Copy the list of group memberships to the end of the extra data */
1318 if (group_list_ndx == 0)
1321 state->response.extra_data.data = SMB_REALLOC(
1322 state->response.extra_data.data,
1323 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1325 if (!state->response.extra_data.data) {
1326 DEBUG(0, ("out of memory\n"));
1328 SAFE_FREE(gr_mem_list);
1329 request_error(state);
1333 memcpy(&((char *)state->response.extra_data.data)
1334 [group_list_ndx * sizeof(struct winbindd_gr)],
1335 gr_mem_list, gr_mem_list_len);
1337 state->response.length += gr_mem_list_len;
1339 DEBUG(10, ("returning %d groups, length = %d\n",
1340 group_list_ndx, gr_mem_list_len));
1342 /* Out of domains */
1346 SAFE_FREE(gr_mem_list);
1348 if (group_list_ndx > 0)
1351 request_error(state);
1354 /* List domain groups without mapping to unix ids */
1355 void winbindd_list_groups(struct winbindd_cli_state *state)
1357 winbindd_list_ent(state, LIST_GROUPS);
1360 /* Get user supplementary groups. This is much quicker than trying to
1361 invert the groups database. We merge the groups from the gids and
1362 other_sids info3 fields as trusted domain, universal group
1363 memberships, and nested groups (win2k native mode only) are not
1364 returned by the getgroups RPC call but are present in the info3. */
1366 struct getgroups_state {
1367 struct winbindd_cli_state *state;
1368 struct winbindd_domain *domain;
1373 const DOM_SID *token_sids;
1374 size_t i, num_token_sids;
1377 size_t num_token_gids;
1380 static void getgroups_usersid_recv(void *private_data, bool success,
1381 const DOM_SID *sid, enum lsa_SidType type);
1382 static void getgroups_tokensids_recv(void *private_data, bool success,
1383 DOM_SID *token_sids, size_t num_token_sids);
1384 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1386 void winbindd_getgroups(struct winbindd_cli_state *state)
1388 struct getgroups_state *s;
1390 /* Ensure null termination */
1391 state->request.data.username
1392 [sizeof(state->request.data.username)-1]='\0';
1394 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1395 state->request.data.username));
1397 /* Parse domain and username */
1399 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1401 DEBUG(0, ("talloc failed\n"));
1402 request_error(state);
1408 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1410 if (!parse_domain_user_talloc(state->mem_ctx,
1411 state->request.data.username,
1412 &s->domname, &s->username)) {
1413 DEBUG(5, ("Could not parse domain user: %s\n",
1414 state->request.data.username));
1416 /* error out if we do not have nested group support */
1418 if ( !lp_winbind_nested_groups() ) {
1419 request_error(state);
1423 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1424 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1427 /* Get info for the domain (either by short domain name or
1428 DNS name in the case of a UPN) */
1430 s->domain = find_domain_from_name_noinit(s->domname);
1432 char *p = strchr(s->username, '@');
1435 s->domain = find_domain_from_name_noinit(p+1);
1440 if (s->domain == NULL) {
1441 DEBUG(7, ("could not find domain entry for domain %s\n",
1443 request_error(state);
1447 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1448 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1449 "getgroups() for %s\\%s.\n", s->domname,
1451 request_error(state);
1455 /* Get rid and name type from name. The following costs 1 packet */
1457 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1458 getgroups_usersid_recv, WINBINDD_GETGROUPS, s);
1461 static void getgroups_usersid_recv(void *private_data, bool success,
1462 const DOM_SID *sid, enum lsa_SidType type)
1464 struct getgroups_state *s =
1465 (struct getgroups_state *)private_data;
1468 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1469 request_error(s->state);
1473 sid_copy(&s->user_sid, sid);
1475 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1476 getgroups_tokensids_recv, s);
1479 static void getgroups_tokensids_recv(void *private_data, bool success,
1480 DOM_SID *token_sids, size_t num_token_sids)
1482 struct getgroups_state *s =
1483 (struct getgroups_state *)private_data;
1485 /* We need at least the user sid and the primary group in the token,
1486 * otherwise it's an error */
1488 if ((!success) || (num_token_sids < 2)) {
1489 request_error(s->state);
1493 s->token_sids = token_sids;
1494 s->num_token_sids = num_token_sids;
1497 s->token_gids = NULL;
1498 s->num_token_gids = 0;
1500 getgroups_sid2gid_recv(s, False, 0);
1503 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1505 struct getgroups_state *s =
1506 (struct getgroups_state *)private_data;
1509 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1511 &s->num_token_gids)) {
1516 if (s->i < s->num_token_sids) {
1517 const DOM_SID *sid = &s->token_sids[s->i];
1520 if (sid_equal(sid, &s->user_sid)) {
1521 getgroups_sid2gid_recv(s, False, 0);
1525 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1526 getgroups_sid2gid_recv, s);
1530 s->state->response.data.num_entries = s->num_token_gids;
1531 if (s->num_token_gids) {
1532 /* s->token_gids are talloced */
1533 s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t));
1534 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1536 request_ok(s->state);
1539 /* Get user supplementary sids. This is equivalent to the
1540 winbindd_getgroups() function but it involves a SID->SIDs mapping
1541 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1542 idmap. This call is designed to be used with applications that need
1543 to do ACL evaluation themselves. Note that the cached info3 data is
1546 this function assumes that the SID that comes in is a user SID. If
1547 you pass in another type of SID then you may get unpredictable
1551 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1554 void winbindd_getusersids(struct winbindd_cli_state *state)
1558 /* Ensure null termination */
1559 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1561 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1562 if (user_sid == NULL) {
1563 DEBUG(1, ("talloc failed\n"));
1564 request_error(state);
1568 if (!string_to_sid(user_sid, state->request.data.sid)) {
1569 DEBUG(1, ("Could not get convert sid %s from string\n",
1570 state->request.data.sid));
1571 request_error(state);
1575 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1579 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1582 struct winbindd_cli_state *state =
1583 (struct winbindd_cli_state *)private_data;
1585 unsigned ofs, ret_size = 0;
1589 request_error(state);
1593 /* work out the response size */
1594 for (i = 0; i < num_sids; i++) {
1596 sid_to_fstring(s, &sids[i]);
1597 ret_size += strlen(s) + 1;
1600 /* build the reply */
1601 ret = (char *)SMB_MALLOC(ret_size);
1603 DEBUG(0, ("malloc failed\n"));
1604 request_error(state);
1608 for (i = 0; i < num_sids; i++) {
1610 sid_to_fstring(s, &sids[i]);
1611 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1612 ofs += strlen(ret+ofs) + 1;
1615 /* Send data back to client */
1616 state->response.data.num_entries = num_sids;
1617 state->response.extra_data.data = ret;
1618 state->response.length += ret_size;
1622 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1625 struct winbindd_domain *domain;
1627 /* Ensure null termination */
1628 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1630 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1631 DEBUG(1, ("Could not get convert sid %s from string\n",
1632 state->request.data.sid));
1633 request_error(state);
1637 /* Get info for the domain */
1638 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1639 DEBUG(0,("could not find domain entry for sid %s\n",
1640 sid_string_dbg(&user_sid)));
1641 request_error(state);
1645 sendto_domain(state, domain);
1648 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1649 struct winbindd_cli_state *state)
1659 /* Ensure null termination */
1660 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1662 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1663 DEBUG(1, ("Could not get convert sid %s from string\n",
1664 state->request.data.sid));
1665 return WINBINDD_ERROR;
1668 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1669 &user_sid, &num_groups,
1671 if (!NT_STATUS_IS_OK(status))
1672 return WINBINDD_ERROR;
1674 if (num_groups == 0) {
1675 state->response.data.num_entries = 0;
1676 state->response.extra_data.data = NULL;
1680 if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) {
1681 DEBUG(0, ("talloc failed\n"));
1682 return WINBINDD_ERROR;
1685 state->response.extra_data.data = SMB_STRDUP(sidstring);
1686 if (!state->response.extra_data.data) {
1687 return WINBINDD_ERROR;
1689 state->response.length += len+1;
1690 state->response.data.num_entries = num_groups;