X-Git-Url: http://git.samba.org/samba.git/?p=ira%2Fwip.git;a=blobdiff_plain;f=source3%2Fnsswitch%2Fwinbindd_group.c;h=9a4b02f7343bfc92bac6dc73e3cb1885c03be26a;hp=8f5306321accd5af84253c4a59962466d154c274;hb=5e54558c6dea67b56bbfaba5698f3a434d3dffb6;hpb=ae2e6490743c0fd89e11c85cd72d549f39be7674 diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c index 8f5306321ac..9a4b02f7343 100644 --- a/source3/nsswitch/winbindd_group.c +++ b/source3/nsswitch/winbindd_group.c @@ -6,10 +6,11 @@ Copyright (C) Tim Potter 2000 Copyright (C) Jeremy Allison 2001. Copyright (C) Gerald (Jerry) Carter 2003. + Copyright (C) Volker Lendecke 2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -18,8 +19,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. If not, see . */ #include "includes.h" @@ -30,37 +30,150 @@ extern BOOL opt_nocache; #undef DBGC_CLASS #define DBGC_CLASS DBGC_WINBIND -/********************************************************************* -*********************************************************************/ +static void add_member(const char *domain, const char *user, + char **pp_members, size_t *p_num_members) +{ + fstring name; + + fill_domain_username(name, domain, user, True); + safe_strcat(name, ",", sizeof(name)-1); + string_append(pp_members, name); + *p_num_members += 1; +} -static int gr_mem_buffer( char **buffer, char **members, int num_members ) +/********************************************************************** + Add member users resulting from sid. Expand if it is a domain group. +**********************************************************************/ + +static void add_expanded_sid(const DOM_SID *sid, char **pp_members, size_t *p_num_members) { - int i; - int len = 0; - int idx = 0; + DOM_SID dom_sid; + uint32 rid; + struct winbindd_domain *domain; + size_t i; + + char *domain_name = NULL; + char *name = NULL; + enum lsa_SidType type; + + uint32 num_names; + DOM_SID *sid_mem; + char **names; + uint32 *types; + + NTSTATUS result; - if ( num_members == 0 ) { - *buffer = NULL; - return 0; + TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid"); + + if (mem_ctx == NULL) { + DEBUG(1, ("talloc_init failed\n")); + return; } - - for ( i=0; imethods->sid_to_name(domain, mem_ctx, sid, + &domain_name, &name, &type); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(3, ("sid_to_name failed for sid %s\n", + sid_string_static(sid))); + goto done; + } + + DEBUG(10, ("Found name %s, type %d\n", name, type)); + + if (type == SID_NAME_USER) { + add_member(domain_name, name, pp_members, p_num_members); + goto done; + } + + if (type != SID_NAME_DOM_GRP) { + DEBUG(10, ("Alias member %s neither user nor group, ignore\n", + name)); + goto done; + } + + /* Expand the domain group, this must be done via the target domain */ + + domain = find_domain_from_sid(sid); + + if (domain == NULL) { + DEBUG(3, ("Could not find domain from SID %s\n", + sid_string_static(sid))); + goto done; + } + + result = domain->methods->lookup_groupmem(domain, mem_ctx, + sid, &num_names, + &sid_mem, &names, + &types); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(10, ("Could not lookup group members for %s: %s\n", + name, nt_errstr(result))); + goto done; + } + + for (i=0; iname, names[i], pp_members, p_num_members); + } + + done: + talloc_destroy(mem_ctx); + return; } -/*************************************************************** - Empty static struct for negative caching. -****************************************************************/ +static BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain, + DOM_SID *group_sid, + size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len) +{ + DOM_SID *members; + size_t i, num_members; + + *num_gr_mem = 0; + *gr_mem = NULL; + *gr_mem_len = 0; + + if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members, + &num_members))) + return True; + + for (i=0; igr_gid = unix_gid; @@ -81,29 +194,303 @@ static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name, return True; } -/* Fill in the group membership field of a NT group given by group_sid */ +/*********************************************************************** + If "enum users" is set to false, and the group being looked + up is the Domain Users SID: S-1-5-domain-513, then for the + list of members check if the querying user is in that group, + and if so only return that user as the gr_mem array. + We can change this to a different parameter than "enum users" + if neccessaey, or parameterize the group list we do this for. +***********************************************************************/ + +static BOOL fill_grent_mem_domusers( TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + struct winbindd_cli_state *state, + DOM_SID *group_sid, + enum lsa_SidType group_name_type, + size_t *num_gr_mem, char **gr_mem, + size_t *gr_mem_len) +{ + DOM_SID querying_user_sid; + DOM_SID *pquerying_user_sid = NULL; + uint32 num_groups = 0; + DOM_SID *user_sids = NULL; + BOOL u_in_group = False; + NTSTATUS status; + int i; + unsigned int buf_len = 0; + char *buf = NULL; + + DEBUG(10,("fill_grent_mem_domain_users: domain %s\n", + domain->name )); + + if (state) { + uid_t ret_uid = (uid_t)-1; + if (sys_getpeereid(state->sock, &ret_uid)==0) { + /* We know who's asking - look up their SID if + it's one we've mapped before. */ + status = idmap_uid_to_sid(&querying_user_sid, ret_uid); + if (NT_STATUS_IS_OK(status)) { + pquerying_user_sid = &querying_user_sid; + DEBUG(10,("fill_grent_mem_domain_users: querying uid %u -> %s\n", + (unsigned int)ret_uid, + sid_string_static(pquerying_user_sid) )); + } + } + } + + /* Only look up if it was a winbindd user in this domain. */ + if (pquerying_user_sid && + (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) { + + DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n", + sid_string_static(pquerying_user_sid) )); + + status = domain->methods->lookup_usergroups(domain, + mem_ctx, + pquerying_user_sid, + &num_groups, + &user_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("fill_grent_mem_domain_users: lookup_usergroups failed " + "for sid %s in domain %s (error: %s)\n", + sid_string_static(pquerying_user_sid), + domain->name, + nt_errstr(status))); + return False; + } + + for (i = 0; i < num_groups; i++) { + if (sid_equal(group_sid, &user_sids[i])) { + /* User is in Domain Users, add their name + as the only group member. */ + u_in_group = True; + break; + } + } + } + + if (u_in_group) { + size_t len = 0; + char *domainname = NULL; + char *username = NULL; + fstring name; + enum lsa_SidType type; + + DEBUG(10,("fill_grent_mem_domain_users: sid %s in 'Domain Users' in domain %s\n", + sid_string_static(pquerying_user_sid), domain->name )); + + status = domain->methods->sid_to_name(domain, mem_ctx, + pquerying_user_sid, + &domainname, + &username, + &type); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("could not lookup username for user " + "sid %s in domain %s (error: %s)\n", + sid_string_static(pquerying_user_sid), + domain->name, + nt_errstr(status))); + return False; + } + fill_domain_username(name, domain->name, username, True); + len = strlen(name); + buf_len = len + 1; + if (!(buf = (char *)SMB_MALLOC(buf_len))) { + DEBUG(1, ("out of memory\n")); + return False; + } + memcpy(buf, name, buf_len); + + DEBUG(10,("fill_grent_mem_domain_users: user %s in " + "'Domain Users' in domain %s\n", + name, domain->name )); + + /* user is the only member */ + *num_gr_mem = 1; + } + + *gr_mem = buf; + *gr_mem_len = buf_len; + + DEBUG(10, ("fill_grent_mem_domain_users: num_mem = %u, len = %u, mem = %s\n", + (unsigned int)*num_gr_mem, + (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); + + return True; +} + +/*********************************************************************** + Add names to a list. Assumes a canonical version of the string + in DOMAIN\user +***********************************************************************/ + +static int namecmp( const void *a, const void *b ) +{ + return StrCaseCmp( * (char * const *) a, * (char * const *) b); +} + +static NTSTATUS add_names_to_list( TALLOC_CTX *ctx, + char ***list, uint32 *n_list, + char **names, uint32 n_names ) +{ + char **new_list = NULL; + uint32 n_new_list = 0; + int i, j; + + if ( !names || (n_names == 0) ) + return NT_STATUS_OK; + + /* Alloc the maximum size we'll need */ + + if ( *list == NULL ) { + if ( (new_list = TALLOC_ARRAY( ctx, char *, n_names )) == NULL ) + return NT_STATUS_NO_MEMORY; + n_new_list = n_names; + } else { + new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *, + (*n_list) + n_names ); + if ( !new_list ) + return NT_STATUS_NO_MEMORY; + n_new_list = (*n_list) + n_names; + } + + /* Add all names */ + + for ( i=*n_list, j=0; imethods->lookup_groupmem(d, tmp_ctx, + &glist[i], &num_names, + &sid_mem, &names, + &name_types); + if ( !NT_STATUS_IS_OK(status) ) + goto out; + + /* Separate users and groups into two lists */ + + for ( j=0; jname))) return False; - /* Initialise group membership information */ - - DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid))); + DEBUG(10, ("group SID %s\n", sid_string_static(group_sid))); + + /* Initialize with no members */ *num_gr_mem = 0; @@ -111,70 +498,98 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain, * from more than one domain, ie aliases. Thus we have to work it out * ourselves in a special routine. */ - if (domain->internal) - return fill_passdb_alias_grmem(domain, group_sid, + if (domain->internal) { + result = fill_passdb_alias_grmem(domain, group_sid, num_gr_mem, gr_mem, gr_mem_len); - + goto done; + } + + /* Verify name type */ + if ( !((group_name_type==SID_NAME_DOM_GRP) || - ((group_name_type==SID_NAME_ALIAS) && domain->primary)) ) + ((group_name_type==SID_NAME_ALIAS) && domain->primary)) ) { DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", - sid_to_string(sid_string, group_sid), domain->name, + sid_string_static(group_sid), domain->name, group_name_type)); goto done; } - /* Lookup group members */ - status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names, - &sid_mem, &names, &name_types); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n", - sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status))); + /* OPTIMIZATION / HACK. See comment in + fill_grent_mem_domusers() */ - goto done; + sid_peek_rid( group_sid, &group_rid ); + if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) { + result = fill_grent_mem_domusers( mem_ctx, domain, state, + group_sid, group_name_type, + num_gr_mem, gr_mem, + gr_mem_len ); + goto done; } - DEBUG(10, ("looked up %d names\n", num_names)); + /* Real work goes here. Create a list of group names to + expand startign with the initial one. Pass that to + expand_groups() which returns a list of more group names + to expand. Do this up to the max search depth. */ - if (DEBUGLEVEL >= 10) { - for (i = 0; i < num_names; i++) - DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]), - name_types[i])); + if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) { + result = False; + DEBUG(0,("fill_grent_mem: talloc failure!\n")); + goto done; } + sid_copy( &glist[0], group_sid ); + n_glist = 1; + + for ( i=0; iname, the_name); - - len = strlen(name); + len = strlen(names[i]); /* Add to list or calculate buffer length */ @@ -183,8 +598,8 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain, (*num_gr_mem)++; DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len)); } else { - DEBUG(10, ("appending %s at ndx %d\n", name, len)); - safe_strcpy(&buf[buf_ndx], name, len); + DEBUG(10, ("appending %s at ndx %d\n", names[i], buf_ndx)); + safe_strcpy(&buf[buf_ndx], names[i], len); buf_ndx += len; buf[buf_ndx] = ','; buf_ndx++; @@ -194,7 +609,7 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain, /* Allocate buffer */ if (!buf && buf_len != 0) { - if (!(buf = malloc(buf_len))) { + if (!(buf = (char *)SMB_MALLOC(buf_len))) { DEBUG(1, ("out of memory\n")); result = False; goto done; @@ -203,6 +618,8 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain, goto again; } + /* Now we're done */ + if (buf && buf_ndx > 0) { buf[buf_ndx - 1] = '\0'; } @@ -210,8 +627,8 @@ static BOOL fill_grent_mem(struct winbindd_domain *domain, *gr_mem = buf; *gr_mem_len = buf_len; - DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem, - buf_len, *num_gr_mem ? buf : "NULL")); + DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem, + (unsigned int)buf_len, *num_gr_mem ? buf : "NULL")); result = True; done: @@ -223,18 +640,36 @@ done: return result; } +static void winbindd_getgrsid( struct winbindd_cli_state *state, DOM_SID group_sid ); + +static void getgrnam_recv( void *private_data, BOOL success, const DOM_SID *sid, + enum lsa_SidType type ) +{ + struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data; + + if (!success) { + DEBUG(5,("getgrnam_recv: lookupname failed!\n")); + request_error(state); + return; + } + + if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) { + DEBUG(5,("getgrnam_recv: not a group!\n")); + request_error(state); + return; + } + + winbindd_getgrsid( state, *sid ); +} + + /* Return a group structure from a group name */ -enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) +void winbindd_getgrnam(struct winbindd_cli_state *state) { - DOM_SID group_sid; - WINBINDD_GR *grp; struct winbindd_domain *domain; - enum SID_NAME_USE name_type; fstring name_domain, name_group; - char *tmp, *gr_mem; - int gr_mem_len; - gid_t gid; + char *tmp; /* Ensure null termination */ state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0'; @@ -247,26 +682,11 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) memset(name_group, 0, sizeof(fstring)); tmp = state->request.data.groupname; - - parse_domain_user(tmp, name_domain, name_group); - /* if no domain or our local domain, then do a local tdb search */ + name_domain[0] = '\0'; + name_group[0] = '\0'; - if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) && - ((grp = wb_getgrnam(name_group)) != NULL) ) { - - char *buffer = NULL; - - memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) ); - - gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem ); - - state->response.data.gr.gr_mem_ofs = 0; - state->response.length += gr_mem_len; - state->response.extra_data = buffer; /* give the memory away */ - - return WINBINDD_OK; - } + parse_domain_user(tmp, name_domain, name_group); /* if no domain or our local domain and no local tdb group, default to * our local domain for aliases */ @@ -280,141 +700,193 @@ enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) if ((domain = find_domain_from_name(name_domain)) == NULL) { DEBUG(3, ("could not get domain sid for domain %s\n", name_domain)); - return WINBINDD_ERROR; + request_error(state); + return; } /* should we deal with users for our domain? */ if ( lp_winbind_trusted_domains_only() && domain->primary) { - DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n", - name_domain, name_group)); - return WINBINDD_ERROR; + DEBUG(7,("winbindd_getgrnam: My domain -- rejecting " + "getgrnam() for %s\\%s.\n", name_domain, name_group)); + request_error(state); + return; } /* Get rid and name type from name */ + + ws_name_replace( name_group, WB_REPLACE_CHAR ); - if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid, - &name_type)) { - DEBUG(1, ("group %s in domain %s does not exist\n", - name_group, name_domain)); - return WINBINDD_ERROR; - } + winbindd_lookupname_async( state->mem_ctx, domain->name, name_group, + getgrnam_recv, WINBINDD_GETGRNAM, state ); +} - if ( !((name_type==SID_NAME_DOM_GRP) || - ((name_type==SID_NAME_ALIAS) && domain->primary) || - ((name_type==SID_NAME_ALIAS) && domain->internal)) ) +struct getgrsid_state { + struct winbindd_cli_state *state; + struct winbindd_domain *domain; + char *group_name; + enum lsa_SidType group_type; + uid_t gid; + DOM_SID group_sid; +}; + +static void getgrsid_sid2gid_recv(void *private_data, BOOL success, gid_t gid) { - DEBUG(1, ("name '%s' is not a local or domain group: %d\n", - name_group, name_type)); - return WINBINDD_ERROR; + struct getgrsid_state *s = + (struct getgrsid_state *)private_data; + struct winbindd_domain *domain; + size_t gr_mem_len; + size_t num_gr_mem; + char *gr_mem; + fstring dom_name, group_name; + + if (!success) { + DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n")); + request_error(s->state); + return; } - if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) { - DEBUG(1, ("error converting unix gid to sid\n")); - return WINBINDD_ERROR; + s->gid = gid; + + if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) { + DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n")); + request_error(s->state); + return; } - if (!fill_grent(&state->response.data.gr, name_domain, - name_group, gid) || - !fill_grent_mem(domain, &group_sid, name_type, - &state->response.data.gr.num_gr_mem, - &gr_mem, &gr_mem_len)) { - return WINBINDD_ERROR; + + /* Fill in group structure */ + + if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) { + DEBUG(1,("Can't find domain from name (%s)\n", dom_name)); + request_error(s->state); + return; } - /* Group membership lives at start of extra data */ + if (!fill_grent(&s->state->response.data.gr, dom_name, group_name, gid) || + !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type, + &num_gr_mem, &gr_mem, &gr_mem_len)) + { + request_error(s->state); + return; + } - state->response.data.gr.gr_mem_ofs = 0; + s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem; - state->response.length += gr_mem_len; - state->response.extra_data = gr_mem; + /* Group membership lives at start of extra data */ - return WINBINDD_OK; -} + s->state->response.data.gr.gr_mem_ofs = 0; -/* Return a group structure from a gid number */ + s->state->response.length += gr_mem_len; + s->state->response.extra_data.data = gr_mem; -enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state) + request_ok(s->state); + } + +static void getgrsid_lookupsid_recv( void *private_data, BOOL success, + const char *dom_name, const char *name, + enum lsa_SidType name_type ) { - struct winbindd_domain *domain; - WINBINDD_GR *grp; - DOM_SID group_sid; - enum SID_NAME_USE name_type; - fstring dom_name; - fstring group_name; - int gr_mem_len; - char *gr_mem; + struct getgrsid_state *s = (struct getgrsid_state *)private_data; - DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, - (unsigned long)state->request.data.gid)); + if (!success) { + DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n")); + request_error(s->state); + return; + } - /* Bug out if the gid isn't in the winbind range */ + /* either it's a domain group, a domain local group, or a + local group in an internal domain */ - if ((state->request.data.gid < server_state.gid_low) || - (state->request.data.gid > server_state.gid_high)) - return WINBINDD_ERROR; + if ( !( (name_type==SID_NAME_DOM_GRP) || + ((name_type==SID_NAME_ALIAS) && + (s->domain->primary || s->domain->internal)) ) ) + { + DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n", + dom_name, name, name_type)); + request_error(s->state); + return; +} - /* alway try local tdb lookup first */ - if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) { - char *buffer = NULL; - - memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) ); - - gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem ); - - state->response.data.gr.gr_mem_ofs = 0; - state->response.length += gr_mem_len; - state->response.extra_data = buffer; /* give away the memory */ - - return WINBINDD_OK; + if ( (s->group_name = talloc_asprintf( s->state->mem_ctx, + "%s\\%s", + dom_name, name )) == NULL ) +{ + DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n")); + request_error(s->state); + return; } - /* Get rid from gid */ - if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) { - DEBUG(1, ("could not convert gid %lu to rid\n", - (unsigned long)state->request.data.gid)); - return WINBINDD_ERROR; + s->group_type = name_type; + + winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid, + getgrsid_sid2gid_recv, s); } - /* Get name from sid */ +static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid ) + { + struct getgrsid_state *s; - if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) { - DEBUG(1, ("could not lookup sid\n")); - return WINBINDD_ERROR; + if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) { + DEBUG(0, ("talloc failed\n")); + request_error(state); + return; } - /* Fill in group structure */ + s->state = state; + + if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) { + DEBUG(3, ("Could not find domain for sid %s\n", + sid_string_static(&group_sid))); + request_error(state); + return; + } + + sid_copy(&s->group_sid, &group_sid); + + winbindd_lookupsid_async( s->state->mem_ctx, &group_sid, + getgrsid_lookupsid_recv, s ); +} + + +static void getgrgid_recv(void *private_data, BOOL success, const char *sid) +{ + struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state); + enum lsa_SidType name_type; + DOM_SID group_sid; - domain = find_domain_from_sid(&group_sid); + if (success) { + DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n", + (unsigned long)(state->request.data.gid), sid)); - if (!domain) { - DEBUG(1,("Can't find domain from sid\n")); - return WINBINDD_ERROR; + string_to_sid(&group_sid, sid); + winbindd_getgrsid(state, group_sid); + return; } - if ( !((name_type==SID_NAME_DOM_GRP) || - ((name_type==SID_NAME_ALIAS) && domain->primary) || - ((name_type==SID_NAME_ALIAS) && domain->internal)) ) - { - DEBUG(1, ("name '%s' is not a local or domain group: %d\n", - group_name, name_type)); - return WINBINDD_ERROR; + /* Ok, this might be "ours", i.e. an alias */ + if (pdb_gid_to_sid(state->request.data.gid, &group_sid) && + lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) && + (name_type == SID_NAME_ALIAS)) { + /* Hey, got an alias */ + DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n", + (unsigned long)(state->request.data.gid), sid)); + winbindd_getgrsid(state, group_sid); + return; } - if (!fill_grent(&state->response.data.gr, dom_name, group_name, - state->request.data.gid) || - !fill_grent_mem(domain, &group_sid, name_type, - &state->response.data.gr.num_gr_mem, - &gr_mem, &gr_mem_len)) - return WINBINDD_ERROR; - - /* Group membership lives at start of extra data */ - - state->response.data.gr.gr_mem_ofs = 0; + DEBUG(1, ("could not convert gid %lu to sid\n", + (unsigned long)state->request.data.gid)); + request_error(state); +} - state->response.length += gr_mem_len; - state->response.extra_data = gr_mem; +/* Return a group structure from a gid number */ +void winbindd_getgrgid(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, + (unsigned long)state->request.data.gid)); - return WINBINDD_OK; + /* always use the async interface */ + winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state); } /* @@ -423,7 +895,7 @@ enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state) /* "Rewind" file pointer for group database enumeration */ -enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state) +static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state) { struct winbindd_domain *domain; @@ -431,8 +903,9 @@ enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state) /* Check user has enabled this */ - if (!lp_winbind_enum_groups()) - return WINBINDD_ERROR; + if (!lp_winbind_enum_groups()) { + return False; + } /* Free old static data if it exists */ @@ -446,22 +919,20 @@ enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state) for (domain = domain_list(); domain != NULL; domain = domain->next) { struct getent_state *domain_state; - + /* Create a state record for this domain */ + /* don't add our domaina if we are a PDC or if we are a member of a Samba domain */ - if ( (IS_DC || lp_winbind_trusted_domains_only()) - && domain->primary ) + if ( lp_winbind_trusted_domains_only() && domain->primary ) { continue; } - /* Create a state record for this domain */ - if ((domain_state = (struct getent_state *) - malloc(sizeof(struct getent_state))) == NULL) { + if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) { DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n")); - return WINBINDD_ERROR; + return False; } ZERO_STRUCTP(domain_state); @@ -473,19 +944,29 @@ enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state) DLIST_ADD(state->getgrent_state, domain_state); } - return WINBINDD_OK; + state->getgrent_initialized = True; + return True; +} + +void winbindd_setgrent(struct winbindd_cli_state *state) +{ + if (winbindd_setgrent_internal(state)) { + request_ok(state); + } else { + request_error(state); + } } /* Close file pointer to ntdom group database */ -enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state) +void winbindd_endgrent(struct winbindd_cli_state *state) { DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid)); free_getent_state(state->getgrent_state); + state->getgrent_initialized = False; state->getgrent_state = NULL; - - return WINBINDD_OK; + request_ok(state); } /* Get the list of domain groups and domain aliases for a domain. We fill in @@ -493,13 +974,11 @@ enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state) The dispinfo_ndx field is incremented to the index of the next group to fetch. Return True if some groups were returned, False otherwise. */ -#define MAX_FETCH_SAM_ENTRIES 100 - static BOOL get_sam_group_entries(struct getent_state *ent) { NTSTATUS status; uint32 num_entries; - struct acct_info *name_list = NULL, *tmp_name_list = NULL; + struct acct_info *name_list = NULL; TALLOC_CTX *mem_ctx; BOOL result = False; struct acct_info *sam_grp_entries = NULL; @@ -542,7 +1021,7 @@ static BOOL get_sam_group_entries(struct getent_state *ent) /* Copy entries into return buffer */ if (num_entries) { - if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) { + if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) { DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", num_entries)); result = False; @@ -559,7 +1038,8 @@ static BOOL get_sam_group_entries(struct getent_state *ent) if ( ( lp_security() != SEC_ADS && domain->native_mode && domain->primary) || domain->internal ) { - DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n")); + DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n", + domain->native_mode ? "Native Mode 2k":"BUILTIN or local")); status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries); @@ -573,17 +1053,14 @@ static BOOL get_sam_group_entries(struct getent_state *ent) /* Copy entries into return buffer */ if ( num_entries ) { - if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) ) + if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) ) { DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", num_entries)); result = False; - SAFE_FREE( name_list ); goto done; } - name_list = tmp_name_list; - memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, num_entries * sizeof(struct acct_info) ); } @@ -609,42 +1086,58 @@ static BOOL get_sam_group_entries(struct getent_state *ent) #define MAX_GETGRENT_GROUPS 500 -enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) +void winbindd_getgrent(struct winbindd_cli_state *state) { struct getent_state *ent; struct winbindd_gr *group_list = NULL; - int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0; - char *new_extra_data, *gr_mem_list = NULL; + int num_groups, group_list_ndx, gr_mem_list_len = 0; + char *gr_mem_list = NULL; DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid)); /* Check user has enabled this */ - if (!lp_winbind_enum_groups()) - return WINBINDD_ERROR; + if (!lp_winbind_enum_groups()) { + request_error(state); + return; + } num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries); - if ((state->response.extra_data = - malloc(num_groups * sizeof(struct winbindd_gr))) == NULL) - return WINBINDD_ERROR; + if (num_groups == 0) { + request_error(state); + return; + } + + if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) { + request_error(state); + return; + } + + memset(state->response.extra_data.data, '\0', + num_groups * sizeof(struct winbindd_gr) ); state->response.data.num_entries = 0; - group_list = (struct winbindd_gr *)state->response.extra_data; + group_list = (struct winbindd_gr *)state->response.extra_data.data; - if (!(ent = state->getgrent_state)) - return WINBINDD_ERROR; + if (!state->getgrent_initialized) + winbindd_setgrent_internal(state); + + if (!(ent = state->getgrent_state)) { + request_error(state); + return; + } /* Start sending back groups */ - for (i = 0; i < num_groups; i++) { + for (group_list_ndx = 0; group_list_ndx < num_groups; ) { struct acct_info *name_list = NULL; fstring domain_group_name; uint32 result; gid_t group_gid; - int gr_mem_len; - char *gr_mem, *new_gr_mem_list; + size_t gr_mem_len; + char *gr_mem; DOM_SID group_sid; struct winbindd_domain *domain; @@ -679,7 +1172,7 @@ enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) break; } - name_list = ent->sam_entries; + name_list = (struct acct_info *)ent->sam_entries; if (!(domain = find_domain_from_name(ent->domain_name))) { @@ -693,13 +1186,31 @@ enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) sid_copy(&group_sid, &domain->sid); sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid); - if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) { - - DEBUG(1, ("could not look up gid for group %s\n", - name_list[ent->sam_entry_index].acct_name)); - - ent->sam_entry_index++; - goto tryagain; + if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) { + union unid_t id; + enum lsa_SidType type; + + DEBUG(10, ("SID %s not in idmap\n", + sid_string_static(&group_sid))); + + if (!pdb_sid_to_id(&group_sid, &id, &type)) { + DEBUG(1, ("could not look up gid for group " + "%s\n", + name_list[ent->sam_entry_index].acct_name)); + ent->sam_entry_index++; + goto tryagain; + } + + if ((type != SID_NAME_DOM_GRP) && + (type != SID_NAME_ALIAS) && + (type != SID_NAME_WKN_GRP)) { + DEBUG(1, ("Group %s is a %s, not a group\n", + sid_type_lookup(type), + name_list[ent->sam_entry_index].acct_name)); + ent->sam_entry_index++; + goto tryagain; + } + group_gid = id.gid; } DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid, @@ -708,7 +1219,7 @@ enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) /* Fill in group entry */ fill_domain_username(domain_group_name, ent->domain_name, - name_list[ent->sam_entry_index].acct_name); + name_list[ent->sam_entry_index].acct_name, True); result = fill_grent(&group_list[group_list_ndx], ent->domain_name, @@ -718,6 +1229,7 @@ enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) /* Fill in group membership entry */ if (result) { + size_t num_gr_mem = 0; DOM_SID member_sid; group_list[group_list_ndx].num_gr_mem = 0; gr_mem = NULL; @@ -731,30 +1243,29 @@ enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid); result = fill_grent_mem( domain, + NULL, &member_sid, SID_NAME_DOM_GRP, - &group_list[group_list_ndx].num_gr_mem, + &num_gr_mem, &gr_mem, &gr_mem_len); + + group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem; } } if (result) { /* Append to group membership list */ - new_gr_mem_list = Realloc( - gr_mem_list, - gr_mem_list_len + gr_mem_len); + gr_mem_list = (char *)SMB_REALLOC( + gr_mem_list, gr_mem_list_len + gr_mem_len); - if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) { + if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) { DEBUG(0, ("out of memory\n")); - SAFE_FREE(gr_mem_list); gr_mem_list_len = 0; break; } - DEBUG(10, ("list_len = %d, mem_len = %d\n", - gr_mem_list_len, gr_mem_len)); - - gr_mem_list = new_gr_mem_list; + DEBUG(10, ("list_len = %d, mem_len = %u\n", + gr_mem_list_len, (unsigned int)gr_mem_len)); memcpy(&gr_mem_list[gr_mem_list_len], gr_mem, gr_mem_len); @@ -793,27 +1304,22 @@ enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) if (group_list_ndx == 0) goto done; - new_extra_data = Realloc( - state->response.extra_data, + state->response.extra_data.data = SMB_REALLOC( + state->response.extra_data.data, group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len); - if (!new_extra_data) { + if (!state->response.extra_data.data) { DEBUG(0, ("out of memory\n")); group_list_ndx = 0; - SAFE_FREE(state->response.extra_data); SAFE_FREE(gr_mem_list); - - return WINBINDD_ERROR; + request_error(state); + return; } - state->response.extra_data = new_extra_data; - - memcpy(&((char *)state->response.extra_data) + memcpy(&((char *)state->response.extra_data.data) [group_list_ndx * sizeof(struct winbindd_gr)], gr_mem_list, gr_mem_list_len); - SAFE_FREE(gr_mem_list); - state->response.length += gr_mem_list_len; DEBUG(10, ("returning %d groups, length = %d\n", @@ -823,18 +1329,22 @@ enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) done: - return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR; + SAFE_FREE(gr_mem_list); + + if (group_list_ndx > 0) + request_ok(state); + else + request_error(state); } /* List domain groups without mapping to unix ids */ -enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state) +void winbindd_list_groups(struct winbindd_cli_state *state) { uint32 total_entries = 0; struct winbindd_domain *domain; const char *which_domain; char *extra_data = NULL; - char *ted = NULL; unsigned int extra_data_len = 0, i; DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid)); @@ -874,14 +1384,14 @@ enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state) /* Allocate some memory for extra data. Note that we limit account names to sizeof(fstring) = 128 characters. */ - ted = Realloc(extra_data, sizeof(fstring) * total_entries); + extra_data = (char *)SMB_REALLOC( + extra_data, sizeof(fstring) * total_entries); - if (!ted) { + if (!extra_data) { DEBUG(0,("failed to enlarge buffer!\n")); - SAFE_FREE(extra_data); - return WINBINDD_ERROR; - } else - extra_data = ted; + request_error(state); + return; + } /* Pack group list into extra data fields */ for (i = 0; i < groups.num_sam_entries; i++) { @@ -889,7 +1399,7 @@ enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state) groups.sam_entries)[i].acct_name; fstring name; - fill_domain_username(name, domain->name, group_name); + fill_domain_username(name, domain->name, group_name, True); /* Append to extra data */ memcpy(&extra_data[extra_data_len], name, strlen(name)); @@ -903,239 +1413,184 @@ enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state) /* Assign extra_data fields in response structure */ if (extra_data) { extra_data[extra_data_len - 1] = '\0'; - state->response.extra_data = extra_data; + state->response.extra_data.data = extra_data; state->response.length += extra_data_len; } /* No domains may have responded but that's still OK so don't return an error. */ - return WINBINDD_OK; + request_ok(state); } -static void add_gid_to_array_unique(gid_t gid, gid_t **gids, int *num) -{ - int i; +/* Get user supplementary groups. This is much quicker than trying to + invert the groups database. We merge the groups from the gids and + other_sids info3 fields as trusted domain, universal group + memberships, and nested groups (win2k native mode only) are not + returned by the getgroups RPC call but are present in the info3. */ - if ((*num) >= groups_max()) - return; +struct getgroups_state { + struct winbindd_cli_state *state; + struct winbindd_domain *domain; + char *domname; + char *username; + DOM_SID user_sid; - for (i=0; i<*num; i++) { - if ((*gids)[i] == gid) - return; - } - - *gids = Realloc(*gids, (*num+1) * sizeof(gid_t)); + const DOM_SID *token_sids; + size_t i, num_token_sids; - if (*gids == NULL) - return; + gid_t *token_gids; + size_t num_token_gids; +}; - (*gids)[*num] = gid; - *num += 1; -} +static void getgroups_usersid_recv(void *private_data, BOOL success, + const DOM_SID *sid, enum lsa_SidType type); +static void getgroups_tokensids_recv(void *private_data, BOOL success, + DOM_SID *token_sids, size_t num_token_sids); +static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid); -static void add_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num) +void winbindd_getgroups(struct winbindd_cli_state *state) { - gid_t gid; - DOM_SID *aliases; - int j, num_aliases; - - DEBUG(10, ("Adding gids from SID: %s\n", sid_string_static(sid))); - - if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0))) - add_gid_to_array_unique(gid, gids, num); + struct getgroups_state *s; - /* Don't expand aliases if not explicitly activated -- for now */ - /* we don't support windows local nested groups if we are a DC. - refer to to sid_to_gid() in the smbd server code to see why - -- jerry */ + /* Ensure null termination */ + state->request.data.username + [sizeof(state->request.data.username)-1]='\0'; - if (!lp_winbind_nested_groups() || IS_DC) - return; + DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid, + state->request.data.username)); - /* Add nested group memberships */ + /* Parse domain and username */ - if (!pdb_enum_alias_memberships(sid, &aliases, &num_aliases)) + s = TALLOC_P(state->mem_ctx, struct getgroups_state); + if (s == NULL) { + DEBUG(0, ("talloc failed\n")); + request_error(state); return; - - for (j=0; jstate = state; -enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) -{ - fstring name_domain, name_user; - DOM_SID user_sid, group_sid; - enum SID_NAME_USE name_type; - uint32 num_groups = 0; - uint32 num_gids = 0; - NTSTATUS status; - DOM_SID **user_grpsids; - struct winbindd_domain *domain; - enum winbindd_result result = WINBINDD_ERROR; - gid_t *gid_list = NULL; - unsigned int i; - TALLOC_CTX *mem_ctx; - NET_USER_INFO_3 *info3 = NULL; - - /* Ensure null termination */ - state->request.data.username[sizeof(state->request.data.username)-1]='\0'; + ws_name_return( state->request.data.username, WB_REPLACE_CHAR ); - DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid, - state->request.data.username)); + if (!parse_domain_user_talloc(state->mem_ctx, + state->request.data.username, + &s->domname, &s->username)) { + DEBUG(5, ("Could not parse domain user: %s\n", + state->request.data.username)); - if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)", - state->request.data.username))) - return WINBINDD_ERROR; + /* error out if we do not have nested group support */ - /* Parse domain and username */ + if ( !lp_winbind_nested_groups() ) { + request_error(state); + return; + } - parse_domain_user(state->request.data.username, - name_domain, name_user); + s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() ); + s->username = talloc_strdup( state->mem_ctx, state->request.data.username ); + } /* Get info for the domain */ - - if ((domain = find_domain_from_name(name_domain)) == NULL) { + + s->domain = find_domain_from_name_noinit(s->domname); + + if (s->domain == NULL) { DEBUG(7, ("could not find domain entry for domain %s\n", - name_domain)); - goto done; + s->domname)); + request_error(state); + return; } - if ( domain->primary && lp_winbind_trusted_domains_only()) { - DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n", - name_domain, name_user)); - return WINBINDD_ERROR; + if ( s->domain->primary && lp_winbind_trusted_domains_only()) { + DEBUG(7,("winbindd_getgroups: My domain -- rejecting " + "getgroups() for %s\\%s.\n", s->domname, + s->username)); + request_error(state); + return; } - - /* Get rid and name type from name. The following costs 1 packet */ - if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid, - &name_type)) { - DEBUG(1, ("user '%s' does not exist\n", name_user)); - goto done; - } + /* Get rid and name type from name. The following costs 1 packet */ - if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) { - DEBUG(1, ("name '%s' is not a user name: %d\n", - name_user, name_type)); - goto done; - } + winbindd_lookupname_async(state->mem_ctx, s->domname, s->username, + getgroups_usersid_recv, WINBINDD_GETGROUPS, s); +} - add_gids_from_sid(&user_sid, &gid_list, &num_gids); +static void getgroups_usersid_recv(void *private_data, BOOL success, + const DOM_SID *sid, enum lsa_SidType type) +{ + struct getgroups_state *s = + (struct getgroups_state *)private_data; - /* Treat the info3 cache as authoritative as the - lookup_usergroups() function may return cached data. */ + if ((!success) || + ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) { + request_error(s->state); + return; + } - if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) { + sid_copy(&s->user_sid, sid); - DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n", - info3->num_groups2, info3->num_other_sids)); + winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid, + getgroups_tokensids_recv, s); +} - num_groups = info3->num_other_sids + info3->num_groups2; +static void getgroups_tokensids_recv(void *private_data, BOOL success, + DOM_SID *token_sids, size_t num_token_sids) +{ + struct getgroups_state *s = + (struct getgroups_state *)private_data; - /* Go through each other sid and convert it to a gid */ + /* We need at least the user sid and the primary group in the token, + * otherwise it's an error */ - for (i = 0; i < info3->num_other_sids; i++) { - fstring name; - fstring dom_name; - enum SID_NAME_USE sid_type; + if ((!success) || (num_token_sids < 2)) { + request_error(s->state); + return; + } - /* Is this sid known to us? It can either be - a trusted domain sid or a foreign sid. */ + s->token_sids = token_sids; + s->num_token_sids = num_token_sids; + s->i = 0; - if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid, - dom_name, name, &sid_type)) - { - DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n", - sid_string_static(&info3->other_sids[i].sid))); - continue; - } + s->token_gids = NULL; + s->num_token_gids = 0; - /* Check it is a domain group or an alias (domain local group) - in a win2k native mode domain. */ - - if ( !((sid_type==SID_NAME_DOM_GRP) || - ((sid_type==SID_NAME_ALIAS) && domain->primary)) ) - { - DEBUG(10, ("winbindd_getgroups: sid type %d " - "for %s is not a domain group\n", - sid_type, - sid_string_static( - &info3->other_sids[i].sid))); - continue; - } + getgroups_sid2gid_recv(s, False, 0); +} - add_gids_from_sid(&info3->other_sids[i].sid, - &gid_list, &num_gids); +static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid) +{ + struct getgroups_state *s = + (struct getgroups_state *)private_data; - if (gid_list == NULL) - goto done; + if (success) { + if (!add_gid_to_array_unique(s->state->mem_ctx, gid, + &s->token_gids, + &s->num_token_gids)) { + return; } + } - for (i = 0; i < info3->num_groups2; i++) { - - /* create the group SID */ - - sid_copy( &group_sid, &domain->sid ); - sid_append_rid( &group_sid, info3->gids[i].g_rid ); - - add_gids_from_sid(&group_sid, &gid_list, &num_gids); + if (s->i < s->num_token_sids) { + const DOM_SID *sid = &s->token_sids[s->i]; + s->i += 1; - if (gid_list == NULL) - goto done; + if (sid_equal(sid, &s->user_sid)) { + getgroups_sid2gid_recv(s, False, 0); + return; } - SAFE_FREE(info3); - - } else { - status = domain->methods->lookup_usergroups(domain, mem_ctx, - &user_sid, &num_groups, - &user_grpsids); - if (!NT_STATUS_IS_OK(status)) - goto done; - - if (state->response.extra_data) - goto done; - - for (i = 0; i < num_groups; i++) { - add_gids_from_sid(user_grpsids[i], - &gid_list, &num_gids); - - if (gid_list == NULL) - goto done; - } + winbindd_sid2gid_async(s->state->mem_ctx, sid, + getgroups_sid2gid_recv, s); + return; } - remove_duplicate_gids( &num_gids, gid_list ); - - /* Send data back to client */ - - state->response.data.num_entries = num_gids; - state->response.extra_data = gid_list; - state->response.length += num_gids * sizeof(gid_t); - - result = WINBINDD_OK; - - done: - - talloc_destroy(mem_ctx); - - return result; + s->state->response.data.num_entries = s->num_token_gids; + /* s->token_gids are talloced */ + s->state->response.extra_data.data = smb_xmemdup(s->token_gids, s->num_token_gids * sizeof(gid_t)); + s->state->response.length += s->num_token_gids * sizeof(gid_t); + request_ok(s->state); } - /* Get user supplementary sids. This is equivalent to the winbindd_getgroups() function but it involves a SID->SIDs mapping rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid @@ -1147,75 +1602,145 @@ enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) you pass in another type of SID then you may get unpredictable results. */ -enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state) + +static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids, + size_t num_sids); + +void winbindd_getusersids(struct winbindd_cli_state *state) { - DOM_SID user_sid; - NTSTATUS status; - DOM_SID **user_grpsids; - struct winbindd_domain *domain; - enum winbindd_result result = WINBINDD_ERROR; - unsigned int i; - TALLOC_CTX *mem_ctx; - char *ret = NULL; - uint32 num_groups; - unsigned ofs, ret_size = 0; + DOM_SID *user_sid; /* Ensure null termination */ state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; - if (!string_to_sid(&user_sid, state->request.data.sid)) { - DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid)); - return WINBINDD_ERROR; + user_sid = TALLOC_P(state->mem_ctx, DOM_SID); + if (user_sid == NULL) { + DEBUG(1, ("talloc failed\n")); + request_error(state); + return; } - if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)", - state->request.data.username))) { - return WINBINDD_ERROR; + if (!string_to_sid(user_sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + request_error(state); + return; } - /* Get info for the domain */ - if ((domain = find_domain_from_sid(&user_sid)) == NULL) { - DEBUG(0,("could not find domain entry for sid %s\n", - sid_string_static(&user_sid))); - goto done; - } - - status = domain->methods->lookup_usergroups(domain, mem_ctx, - &user_sid, &num_groups, - &user_grpsids); - if (!NT_STATUS_IS_OK(status)) - goto done; + winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv, + state); +} - if (num_groups == 0) { - goto no_groups; +static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids, + size_t num_sids) +{ + struct winbindd_cli_state *state = + (struct winbindd_cli_state *)private_data; + char *ret = NULL; + unsigned ofs, ret_size = 0; + size_t i; + + if (!success) { + request_error(state); + return; } /* work out the response size */ - for (i = 0; i < num_groups; i++) { - const char *s = sid_string_static(user_grpsids[i]); + for (i = 0; i < num_sids; i++) { + const char *s = sid_string_static(&sids[i]); ret_size += strlen(s) + 1; } /* build the reply */ - ret = malloc(ret_size); - if (!ret) goto done; + ret = (char *)SMB_MALLOC(ret_size); + if (!ret) { + DEBUG(0, ("malloc failed\n")); + request_error(state); + return; + } ofs = 0; - for (i = 0; i < num_groups; i++) { - const char *s = sid_string_static(user_grpsids[i]); + for (i = 0; i < num_sids; i++) { + const char *s = sid_string_static(&sids[i]); safe_strcpy(ret + ofs, s, ret_size - ofs - 1); ofs += strlen(ret+ofs) + 1; } -no_groups: /* Send data back to client */ - state->response.data.num_entries = num_groups; - state->response.extra_data = ret; + state->response.data.num_entries = num_sids; + state->response.extra_data.data = ret; state->response.length += ret_size; - result = WINBINDD_OK; + request_ok(state); +} - done: - talloc_destroy(mem_ctx); +void winbindd_getuserdomgroups(struct winbindd_cli_state *state) +{ + DOM_SID user_sid; + struct winbindd_domain *domain; - return result; + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + if (!string_to_sid(&user_sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + request_error(state); + return; + } + + /* Get info for the domain */ + if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) { + DEBUG(0,("could not find domain entry for sid %s\n", + sid_string_static(&user_sid))); + request_error(state); + return; + } + + sendto_domain(state, domain); } +enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + DOM_SID user_sid; + NTSTATUS status; + + char *sidstring; + ssize_t len; + DOM_SID *groups; + uint32 num_groups; + + /* Ensure null termination */ + state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; + + if (!string_to_sid(&user_sid, state->request.data.sid)) { + DEBUG(1, ("Could not get convert sid %s from string\n", + state->request.data.sid)); + return WINBINDD_ERROR; + } + + status = domain->methods->lookup_usergroups(domain, state->mem_ctx, + &user_sid, &num_groups, + &groups); + if (!NT_STATUS_IS_OK(status)) + return WINBINDD_ERROR; + + if (num_groups == 0) { + state->response.data.num_entries = 0; + state->response.extra_data.data = NULL; + return WINBINDD_OK; + } + + if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) { + DEBUG(0, ("talloc failed\n")); + return WINBINDD_ERROR; + } + + state->response.extra_data.data = SMB_STRDUP(sidstring); + if (!state->response.extra_data.data) { + return WINBINDD_ERROR; + } + state->response.length += len+1; + state->response.data.num_entries = num_groups; + + return WINBINDD_OK; +}