From a36f9250e7c9446f3eece6d8db29fcbde99256fb Mon Sep 17 00:00:00 2001 From: Tim Potter Date: Mon, 7 May 2001 04:32:40 +0000 Subject: Preliminary merge of winbind into HEAD. Note that this compiles and links but I haven't actually run it yet so it probably doesn't work. (-: (This used to be commit 59f95416b66db6df05289bde224de29c721978e5) --- source3/nsswitch/winbindd_group.c | 1371 +++++++++++++++++++++---------------- 1 file changed, 774 insertions(+), 597 deletions(-) (limited to 'source3/nsswitch/winbindd_group.c') diff --git a/source3/nsswitch/winbindd_group.c b/source3/nsswitch/winbindd_group.c index 77825cd579..67cf10a1f5 100644 --- a/source3/nsswitch/winbindd_group.c +++ b/source3/nsswitch/winbindd_group.c @@ -25,741 +25,918 @@ /* Fill a grent structure from various other information */ -static void winbindd_fill_grent(struct winbindd_gr *gr, char *gr_name, - gid_t unix_gid) +static BOOL fill_grent(struct winbindd_gr *gr, char *gr_name, + gid_t unix_gid) { - /* Fill in uid/gid */ + /* Fill in uid/gid */ - gr->gr_gid = unix_gid; - - /* Group name and password */ + gr->gr_gid = unix_gid; + + /* Group name and password */ + + safe_strcpy(gr->gr_name, gr_name, sizeof(gr->gr_name) - 1); + safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1); - safe_strcpy(gr->gr_name, gr_name, sizeof(gr->gr_name) - 1); - safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1); + return True; } -/* Fill in group membership */ - -struct grent_mem_group { - uint32 rid; - enum SID_NAME_USE name_type; - fstring domain_name; - struct winbindd_domain *domain; - struct grent_mem_group *prev, *next; -}; +/* Fill in the group membership field of a NT group given by group_rid */ -struct grent_mem_list { - fstring name; - struct grent_mem_list *prev, *next; -}; - -/* Name comparison function for qsort() */ - -static int name_comp(struct grent_mem_list *n1, struct grent_mem_list *n2) +static BOOL fill_grent_mem(struct winbindd_domain *domain, + uint32 group_rid, + enum SID_NAME_USE group_name_type, + int *num_gr_mem, char **gr_mem, int *gr_mem_len) { - /* Silly cases */ - - if (!n1 && !n2) return 0; - if (!n1) return -1; - if (!n2) return 1; - - return strcmp(n1->name, n2->name); + uint32 *rid_mem = NULL, num_names = 0; + enum SID_NAME_USE *name_types = NULL; + int buf_len, buf_ndx, i; + char **names = NULL, *buf; + BOOL result = False; + + if (!num_gr_mem || !gr_mem || !gr_mem_len) return False; + + /* Initialise group membership information */ + + DEBUG(10, ("fill_grent_mem(): group %s rid 0x%x\n", + domain ? domain->name : "NULL", group_rid)); + + *num_gr_mem = 0; + + if (group_name_type != SID_NAME_DOM_GRP) { + DEBUG(1, ("fill_grent_mem(): rid %d in domain %s isn't a " + "domain group\n", group_rid, domain->name)); + return False; + } + + /* Lookup group members */ + + if (!winbindd_lookup_groupmem(domain, group_rid, &num_names, + &rid_mem, &names, &name_types)) { + + DEBUG(1, ("fill_grent_mem(): could not lookup membership " + "for group rid %d in domain %s\n", + group_rid, domain->name)); + + return False; + } + + DEBUG(10, ("fill_grent_mem(): looked up %d names\n", num_names)); + + if (DEBUGLEVEL >= 10) { + for (i = 0; i < num_names; i++) { + DEBUG(10, ("\t%20s %x %d\n", names[i], rid_mem[i], + name_types[i])); + } + } + + /* Add members to list */ + + buf = NULL; + buf_len = buf_ndx = 0; + + again: + + for (i = 0; i < num_names; i++) { + char *the_name; + fstring name; + int len; + + the_name = names[i]; + + DEBUG(10, ("fill_grent_mem(): processing name %s\n", the_name)); + + /* Only add domain users */ + + if (name_types[i] != SID_NAME_USER) { + DEBUG(3, ("fill_grent_mem(): name %s isn't a domain " + "user\n", the_name)); + continue; + } + + /* Don't bother with machine accounts */ + + if (the_name[strlen(the_name) - 1] == '$') { + DEBUG(10, ("fill_grent_mem(): %s is machine account\n", + the_name)); + continue; + } + + /* Append domain name */ + + snprintf(name, sizeof(name), "%s%s%s", domain->name, + lp_winbind_separator(), the_name); + + len = strlen(name); + + /* Add to list or calculate buffer length */ + + if (!buf) { + buf_len += len + 1; /* List is comma separated */ + (*num_gr_mem)++; + DEBUG(10, ("fill_grent_mem(): buf_len + %d = %d\n", len + 1, + buf_len)); + } else { + DEBUG(10, ("fill_grent_mem(): appending %s at index %d\n", + name, len)); + safe_strcpy(&buf[buf_ndx], name, len); + buf_ndx += len; + buf[buf_ndx] = ','; + buf_ndx++; + } + } + + /* Allocate buffer */ + + if (!buf) { + if (!(buf = malloc(buf_len))) { + DEBUG(1, ("fill_grent_mem(): out of memory\n")); + result = False; + goto cleanup; + } + memset(buf, 0, buf_len); + goto again; + } + + if (buf && buf_ndx > 0) { + buf[buf_ndx - 1] = '\0'; + } + + *gr_mem = buf; + *gr_mem_len = buf_len; + + DEBUG(10, ("fill_grent_mem(): num_mem = %d, len = %d, mem = %s\n", *num_gr_mem, + buf_len, buf)); + + result = True; + + cleanup: + + /* Free memory allocated in winbindd_lookup_groupmem() */ + + safe_free(name_types); + safe_free(rid_mem); + + free_char_array(num_names, names); + + DEBUG(10, ("fill_grent_mem(): returning %d\n", result)); + + return result; } -static struct grent_mem_list *sort_groupmem_list(struct grent_mem_list *list, - int num_gr_mem) -{ - struct grent_mem_list *groupmem_array, *temp; - int i; - - /* Allocate and zero an array to hold sorted entries */ - - if ((groupmem_array = malloc(num_gr_mem * - sizeof(struct grent_mem_list))) == NULL) { - return NULL; - } +/* Return a group structure from a group name */ - memset((char *)groupmem_array, 0, num_gr_mem * - sizeof(struct grent_mem_list)); +enum winbindd_result winbindd_getgrnam_from_group(struct winbindd_cli_state + *state) +{ + DOM_SID group_sid; + struct winbindd_domain *domain; + enum SID_NAME_USE name_type; + uint32 group_rid; + fstring name_domain, name_group, name; + char *tmp, *gr_mem; + gid_t gid; + int extra_data_len, gr_mem_len; + + DEBUG(3, ("[%5d]: getgrnam %s\n", state->pid, + state->request.data.groupname)); + + /* Parse domain and groupname */ + + memset(name_group, 0, sizeof(fstring)); + + tmp = state->request.data.groupname; + parse_domain_user(tmp, name_domain, name_group); + + /* Reject names that don't have a domain - i.e name_domain contains + the entire name. */ + + if (strequal(name_group, "")) { + return WINBINDD_ERROR; + } + + /* Get info for the domain */ + + if ((domain = find_domain_from_name(name_domain)) == NULL) { + DEBUG(0, ("getgrname_from_group(): could not get domain " + "sid for domain %s\n", name_domain)); + return WINBINDD_ERROR; + } + + if (!domain_handles_open(domain)) { + return WINBINDD_ERROR; + } + + /* Check for cached group entry */ + + if (winbindd_fetch_group_cache_entry(name_domain, name_group, + &state->response.data.gr, + &state->response.extra_data, + &extra_data_len)) { + state->response.length += extra_data_len; + return WINBINDD_OK; + } + + snprintf(name, sizeof(name), "%s\\%s", name_domain, name_group); + + /* Get rid and name type from name */ + + if (!winbindd_lookup_sid_by_name(name, &group_sid, &name_type)) { + DEBUG(1, ("group %s in domain %s does not exist\n", + name_group, name_domain)); + return WINBINDD_ERROR; + } - /* Copy list to array */ + if ((name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_DOM_GRP)) { + DEBUG(1, ("from_group: name '%s' is not a local or domain " + "group: %d\n", name_group, name_type)); + return WINBINDD_ERROR; + } - for(temp = list, i = 0; temp && i < num_gr_mem; temp = temp->next, i++) { - fstrcpy(groupmem_array[i].name, temp->name); - } + /* Fill in group structure */ - /* Sort array */ + sid_split_rid(&group_sid, &group_rid); - qsort(groupmem_array, num_gr_mem, sizeof(struct grent_mem_list), - name_comp); + if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) { + DEBUG(1, ("error sursing unix gid for sid\n")); + return WINBINDD_ERROR; + } - /* Fix up resulting array to a linked list and return it */ + if (!fill_grent(&state->response.data.gr, + state->request.data.groupname, gid) || + !fill_grent_mem(domain, group_rid, name_type, + &state->response.data.gr.num_gr_mem, + &gr_mem, &gr_mem_len)) { + return WINBINDD_ERROR; + } - for(i = 0; i < num_gr_mem; i++) { + /* Group membership lives at start of extra data */ - /* Fix up previous link */ + state->response.data.gr.gr_mem_ofs = 0; - if (i != 0) { - groupmem_array[i].prev = &groupmem_array[i - 1]; - } + state->response.length += gr_mem_len; + state->response.extra_data = gr_mem; - /* Fix up next link */ + /* Update cached group info */ - if (i != (num_gr_mem - 1)) { - groupmem_array[i].next = &groupmem_array[i + 1]; - } - } + winbindd_store_group_cache_entry(name_domain, name_group, + &state->response.data.gr, + state->response.extra_data, + gr_mem_len); - return groupmem_array; + return WINBINDD_OK; } -static BOOL winbindd_fill_grent_mem(struct winbindd_domain *domain, - uint32 group_rid, - enum SID_NAME_USE group_name_type, - struct winbindd_response *response) -{ - struct grent_mem_group *done_groups = NULL, *todo_groups = NULL; - struct grent_mem_group *temp_group; - struct grent_mem_list *groupmem_list = NULL; - struct winbindd_gr *gr; - - if (response) { - gr = &response->data.gr; - } else { - return False; - } - - /* Initialise group membership information */ - - gr->num_gr_mem = 0; - - /* Add first group to todo_groups list */ - - if ((temp_group = - (struct grent_mem_group *)malloc(sizeof(*temp_group))) == NULL) { - return False; - } - - ZERO_STRUCTP(temp_group); - - temp_group->rid = group_rid; - temp_group->name_type = group_name_type; - temp_group->domain = domain; - fstrcpy(temp_group->domain_name, domain->name); - - DLIST_ADD(todo_groups, temp_group); - - /* Iterate over all groups to find members of */ - - while(todo_groups != NULL) { - struct grent_mem_group *current_group = todo_groups; - uint32 num_names = 0, *rid_mem = NULL; - enum SID_NAME_USE *name_types = NULL; - - DOM_SID **sids = NULL; - char **names = NULL; - BOOL done_group; - int i; - - /* Check we haven't looked up this group before */ +/* Return a group structure from a gid number */ - done_group = 0; +enum winbindd_result winbindd_getgrnam_from_gid(struct winbindd_cli_state + *state) +{ + struct winbindd_domain *domain; + DOM_SID group_sid; + enum SID_NAME_USE name_type; + fstring group_name; + uint32 group_rid; + int extra_data_len, gr_mem_len; + char *gr_mem; - for (temp_group = done_groups; temp_group != NULL; - temp_group = temp_group->next) { + /* Bug out if the gid isn't in the winbind range */ - if ((temp_group->rid == current_group->rid) && - (strcmp(temp_group->domain_name, - current_group->domain_name) == 0)) { - - done_group = 1; - } - } + if ((state->request.data.gid < server_state.gid_low) || + (state->request.data.gid > server_state.gid_high)) { + return WINBINDD_ERROR; + } - if (done_group) goto cleanup; + DEBUG(3, ("[%5d]: getgrgid %d\n", state->pid, + state->request.data.gid)); - /* Lookup group membership for the current group */ + /* Get rid from gid */ - if (current_group->name_type == SID_NAME_DOM_GRP) { + if (!winbindd_idmap_get_rid_from_gid(state->request.data.gid, + &group_rid, &domain)) { + DEBUG(1, ("Could not convert gid %d to rid\n", + state->request.data.gid)); + return WINBINDD_ERROR; + } - if (!winbindd_lookup_groupmem(current_group->domain, - current_group->rid, &num_names, - &rid_mem, &names, &name_types)) { + if (!domain_handles_open(domain)) { + return WINBINDD_ERROR; + } - DEBUG(1, ("fill_grent_mem(): could not lookup membership " - "for group rid %d in domain %s\n", - current_group->rid, - current_group->domain->name)); + /* Try a cached entry */ - /* Exit if we cannot lookup the membership for the group - this function was called to look at */ + if (winbindd_fetch_gid_cache_entry(domain->name, + state->request.data.gid, + &state->response.data.gr, + &state->response.extra_data, + &extra_data_len)) { + state->response.length += extra_data_len; + return WINBINDD_OK; + } - if (current_group->rid == group_rid) { - return False; - } else { - goto cleanup; - } - } - } + /* Get sid from gid */ - if (current_group->name_type == SID_NAME_ALIAS) { + sid_copy(&group_sid, &domain->sid); + sid_append_rid(&group_sid, group_rid); - if (!winbindd_lookup_aliasmem(current_group->domain, - current_group->rid, &num_names, - &sids, &names, &name_types)) { + if (!winbindd_lookup_name_by_sid(&group_sid, group_name, &name_type)) { + DEBUG(1, ("Could not lookup sid\n")); + return WINBINDD_ERROR; + } - DEBUG(1, ("fill_grent_mem(): group rid %d not a local group\n", - group_rid)); + if (strcmp(lp_winbind_separator(),"\\")) { + string_sub(group_name, "\\", lp_winbind_separator(), + sizeof(fstring)); + } - /* Exit if we cannot lookup the membership for the group - this function was called to look at */ + if (!((name_type == SID_NAME_ALIAS) || + (name_type == SID_NAME_DOM_GRP))) { + DEBUG(1, ("from_gid: name '%s' is not a local or domain " + "group: %d\n", group_name, name_type)); + return WINBINDD_ERROR; + } - if (current_group->rid == group_rid) { - return False; - } else { - goto cleanup; - } - } - } + /* Fill in group structure */ - /* Now for each member of the group, add it to the group list if it - is a user, otherwise push it onto the todo_group list if it is a - group or an alias. */ - - for (i = 0; i < num_names; i++) { - enum SID_NAME_USE name_type; - fstring name_part1, name_part2; - char *name_dom, *name_user, *the_name; - struct winbindd_domain *name_domain; - - /* Lookup name */ + if (!fill_grent(&state->response.data.gr, group_name, + state->request.data.gid) || + !fill_grent_mem(domain, group_rid, name_type, + &state->response.data.gr.num_gr_mem, + &gr_mem, &gr_mem_len)) { + return WINBINDD_ERROR; + } - ZERO_STRUCT(name_part1); - ZERO_STRUCT(name_part2); - the_name = names[i]; - parse_domain_user(the_name, name_part1, name_part2); + /* Group membership lives at start of extra data */ - if (strcmp(name_part1, "") != 0) { - name_dom = name_part1; - name_user = name_part2; + state->response.data.gr.gr_mem_ofs = 0; - if ((name_domain = find_domain_from_name(name_dom)) == NULL) { - DEBUG(0, ("unable to look up domain record for domain " - "%s\n", name_dom)); - continue; - } + state->response.length += gr_mem_len; + state->response.extra_data = gr_mem; - } else { - name_dom = current_group->domain->name; - name_user = name_part2; - name_domain = current_group->domain; - } + /* Update cached group info */ - if (winbindd_lookup_sid_by_name(name_domain, name_user, NULL, - &name_type) == WINBINDD_OK) { + winbindd_store_gid_cache_entry(domain->name, state->request.data.gid, + &state->response.data.gr, + state->response.extra_data, + gr_mem_len); - /* Check name type */ + return WINBINDD_OK; +} - if (name_type == SID_NAME_USER) { - struct grent_mem_list *entry; +/* + * set/get/endgrent functions + */ - /* Add to group membership list */ - - if ((entry = (struct grent_mem_list *) - malloc(sizeof(*entry))) != NULL) { +/* "Rewind" file pointer for group database enumeration */ - /* Create name */ - slprintf(entry->name, sizeof(entry->name)-1, - "%s%s%s", name_dom, lp_winbind_separator(), name_user); - - /* Add to list */ +enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state) +{ + struct winbindd_domain *tmp; + + DEBUG(3, ("[%5d]: setgrent\n", state->pid)); + + if (state == NULL) return WINBINDD_ERROR; + + /* Check user has enabled this */ + + if (!lp_winbind_enum_groups()) { + return WINBINDD_ERROR; + } + + /* Free old static data if it exists */ + + if (state->getgrent_state != NULL) { + free_getent_state(state->getgrent_state); + state->getgrent_state = NULL; + } + + /* Create sam pipes for each domain we know about */ + + for (tmp = domain_list; tmp != NULL; tmp = tmp->next) { + struct getent_state *domain_state; + + /* Skip domains other than WINBINDD_DOMAIN environment + variable */ + + if ((strcmp(state->request.domain, "") != 0) && + !check_domain_env(state->request.domain, tmp->name)) { + continue; + } + + /* Create a state record for this domain */ + + if ((domain_state = (struct getent_state *) + malloc(sizeof(struct getent_state))) == NULL) { + + return WINBINDD_ERROR; + } + + ZERO_STRUCTP(domain_state); + + /* Add to list of open domains */ + + domain_state->domain = tmp; + DLIST_ADD(state->getgrent_state, domain_state); + } + + return WINBINDD_OK; +} - DLIST_ADD(groupmem_list, entry); - gr->num_gr_mem++; - } +/* Close file pointer to ntdom group database */ - } else { - struct grent_mem_group *todo_group; - DOM_SID todo_sid; - uint32 todo_rid; +enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state) +{ + DEBUG(3, ("[%5d]: endgrent\n", state->pid)); - /* Add group to todo list */ + if (state == NULL) return WINBINDD_ERROR; - if (winbindd_lookup_sid_by_name(name_domain, names[i], - &todo_sid, &name_type) - == WINBINDD_OK) { + free_getent_state(state->getgrent_state); + state->getgrent_state = NULL; + + return WINBINDD_OK; +} - /* Fill in group entry */ +/* Get the list of domain groups and domain aliases for a domain. We fill in + the sam_entries and num_sam_entries fields with domain group information. + The dispinfo_ndx field is incremented to the index of the next group to + fetch. Return True if some groups were returned, False otherwise. */ - sid_split_rid(&todo_sid, &todo_rid); +#define MAX_FETCH_SAM_ENTRIES 100 - if ((todo_group = (struct grent_mem_group *) - malloc(sizeof(*todo_group))) != NULL) { - - ZERO_STRUCTP(todo_group); +static BOOL get_sam_group_entries(struct getent_state *ent) +{ + uint32 status, num_entries, start_ndx = 0; + struct acct_info *name_list = NULL; + + if (ent->got_all_sam_entries) { + return False; + } + +#if 0 + if (winbindd_fetch_group_cache(ent->domain->name, + &ent->sam_entries, + &ent->num_sam_entries)) { + return True; + } +#endif + + /* Fetch group entries */ + + if (!domain_handles_open(ent->domain)) { + return False; + } + + /* Free any existing group info */ + + if (ent->sam_entries) { + free(ent->sam_entries); + ent->sam_entries = NULL; + ent->num_sam_entries = 0; + } + + /* Enumerate domain groups */ + + do { + struct acct_info *sam_grp_entries = NULL; + + num_entries = 0; + + status = + samr_enum_dom_groups(&ent->domain-> + sam_dom_handle, + &start_ndx, + 0x8000, /* buffer size? */ + (struct acct_info **) + &sam_grp_entries, + &num_entries); + + /* Copy entries into return buffer */ + + if (num_entries) { + + name_list = Realloc(name_list, + sizeof(struct acct_info) * + (ent->num_sam_entries + + num_entries)); + + memcpy(&name_list[ent->num_sam_entries], + sam_grp_entries, + num_entries * sizeof(struct acct_info)); + + safe_free(sam_grp_entries); + } + + ent->num_sam_entries += num_entries; + + if (status != STATUS_MORE_ENTRIES) { + break; + } + + } while (ent->num_sam_entries < MAX_FETCH_SAM_ENTRIES); + +#if 0 + /* Fill cache with received entries */ + + winbindd_store_group_cache(ent->domain->name, ent->sam_entries, + ent->num_sam_entries); +#endif + + /* Fill in remaining fields */ + + ent->sam_entries = name_list; + ent->sam_entry_index = 0; + ent->got_all_sam_entries = (status != STATUS_MORE_ENTRIES); + + return ent->num_sam_entries > 0; +} - todo_group->rid = todo_rid; - todo_group->name_type = name_type; - todo_group->domain = name_domain; +/* Fetch next group entry from ntdom database */ - fstrcpy(todo_group->domain_name, name_dom); +#define MAX_GETGRENT_GROUPS 500 - DLIST_ADD(todo_groups, todo_group); - } - } - } - } - } +enum winbindd_result 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 *sep, *new_extra_data, *gr_mem_list = NULL; - cleanup: - - /* Remove group from todo list and add to done_groups list */ + DEBUG(3, ("[%5d]: getgrent\n", state->pid)); - DLIST_REMOVE(todo_groups, current_group); - DLIST_ADD(done_groups, current_group); + if (state == NULL) return WINBINDD_ERROR; - /* Free memory allocated in winbindd_lookup_{alias,group}mem() */ + /* Check user has enabled this */ - safe_free(name_types); - safe_free(rid_mem); + if (!lp_winbind_enum_groups()) { + return WINBINDD_ERROR; + } - free_char_array(num_names, names); - free_sid_array(num_names, sids); - } - - /* Free done groups list */ + num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries); - temp_group = done_groups; + if ((state->response.extra_data = + malloc(num_groups * sizeof(struct winbindd_gr))) == NULL) { + return WINBINDD_ERROR; + } - if (temp_group != NULL) { - while (temp_group != NULL) { - struct grent_mem_group *next; + state->response.data.num_entries = 0; - DLIST_REMOVE(done_groups, temp_group); - next = temp_group->next; + group_list = (struct winbindd_gr *)state->response.extra_data; + sep = lp_winbind_separator(); - free(temp_group); - temp_group = next; - } - } + if (!(ent = state->getgrent_state)) { + return WINBINDD_ERROR; + } - /* Remove duplicates from group member list. */ + /* Start sending back groups */ - if (gr->num_gr_mem > 0) { - struct grent_mem_list *sorted_groupmem_list, *temp; - int extra_data_len = 0; - fstring prev_name; - char *head; + for (i = 0; i < num_groups; i++) { + struct acct_info *name_list = NULL; + fstring domain_group_name; + uint32 result; + gid_t group_gid; + + /* Do we need to fetch another chunk of groups? */ - /* Sort list */ + tryagain: - sorted_groupmem_list = sort_groupmem_list(groupmem_list, - gr->num_gr_mem); - /* Remove duplicates by iteration */ + DEBUG(10, ("getgrent(): entry_index = %d, num_entries = %d\n", + ent->sam_entry_index, ent->num_sam_entries)); - fstrcpy(prev_name, ""); + if (ent->num_sam_entries == ent->sam_entry_index) { - for(temp = sorted_groupmem_list; temp; temp = temp->next) { - if (strequal(temp->name, prev_name)) { + while(ent && !get_sam_group_entries(ent)) { + struct getent_state *next_ent; - /* Got a duplicate name - delete it. Don't panic as we're - only adjusting the prev and next pointers so memory - allocation is not messed up. */ + DEBUG(10, ("getgrent(): freeing state info for " + "domain %s\n", ent->domain->name)); - DLIST_REMOVE(sorted_groupmem_list, temp); - gr->num_gr_mem--; + /* Free state information for this domain */ - } else { + safe_free(ent->sam_entries); + ent->sam_entries = NULL; - /* Got a unique name - count how long it is */ + next_ent = ent->next; + DLIST_REMOVE(state->getgrent_state, ent); + + free(ent); + ent = next_ent; + } - extra_data_len += strlen(temp->name) + 1; - } - } + /* No more domains */ - extra_data_len++; /* Don't forget null a terminator */ + if (!ent) break; + } + + name_list = ent->sam_entries; + + /* Lookup group info */ + + if (!winbindd_idmap_get_gid_from_rid( + ent->domain->name, + name_list[ent->sam_entry_index].rid, + &group_gid)) { + + DEBUG(1, ("getgrent(): could not look up gid for group %s\n", + name_list[ent->sam_entry_index].acct_name)); - /* Convert sorted list into extra data field to send back to ntdom - client. Add one to extra_data_len for null termination */ + ent->sam_entry_index++; + goto tryagain; + } - if ((response->extra_data = malloc(extra_data_len))) { + DEBUG(10, ("getgrent(): got gid %d for group %x\n", group_gid, + name_list[ent->sam_entry_index].rid)); + + /* Fill in group entry */ - /* Initialise extra data */ + slprintf(domain_group_name, sizeof(domain_group_name) - 1, + "%s%s%s", ent->domain->name, lp_winbind_separator(), + name_list[ent->sam_entry_index].acct_name); + + result = fill_grent(&group_list[group_list_ndx], + domain_group_name, group_gid); - memset(response->extra_data, 0, extra_data_len); + /* Fill in group membership entry */ - head = response->extra_data; + if (result) { + int gr_mem_len; + char *gr_mem, *new_gr_mem_list; - /* Fill in extra data */ + /* Get group membership */ - for(temp = sorted_groupmem_list; temp; temp = temp->next) { - int len = strlen(temp->name) + 1; - - safe_strcpy(head, temp->name, len); - head[len - 1] = ','; - head += len; - } - - *head = '\0'; + result = fill_grent_mem( + ent->domain, + name_list[ent->sam_entry_index].rid, + SID_NAME_DOM_GRP, + &group_list[group_list_ndx].num_gr_mem, + &gr_mem, &gr_mem_len); - /* Update response length */ + /* Append to group membership list */ - response->length = sizeof(struct winbindd_response) + - extra_data_len; - } + new_gr_mem_list = Realloc( + gr_mem_list, + gr_mem_list_len + gr_mem_len); - /* Free memory for sorted_groupmem_list. It was allocated as an - array in sort_groupmem_list() so can be freed in one go. */ + if (!new_gr_mem_list) { + DEBUG(0, ("getgrent(): out of memory\n")); + free(gr_mem_list); + gr_mem_list_len = 0; + break; + } - free(sorted_groupmem_list); + DEBUG(10, ("getgrent(): list_len = %d, mem_len = %d\n", + gr_mem_list_len, gr_mem_len)); - /* Free groupmem_list */ + gr_mem_list = new_gr_mem_list; - temp = groupmem_list; + memcpy(&gr_mem_list[gr_mem_list_len], gr_mem, + gr_mem_len); - while (temp != NULL) { - struct grent_mem_list *next; - - DLIST_REMOVE(groupmem_list, temp); - next = temp->next; - - free(temp); - temp = next; - } - } + safe_free(gr_mem); - return True; -} + group_list[group_list_ndx].gr_mem_ofs = + gr_mem_list_len; -/* Return a group structure from a group name */ + gr_mem_list_len += gr_mem_len; + } -enum winbindd_result winbindd_getgrnam_from_group(struct winbindd_cli_state *state) -{ - DOM_SID group_sid; - struct winbindd_domain *domain; - enum SID_NAME_USE name_type; - uint32 group_rid; - fstring name_domain, name_group, name; - char *tmp; - gid_t gid; - int extra_data_len; + ent->sam_entry_index++; + + /* Add group to return list */ + + if (result) { - /* Parse domain and groupname */ + DEBUG(10, ("getgrent(): adding group num_entries = %d\n", + state->response.data.num_entries)); - memset(name_group, 0, sizeof(fstring)); + group_list_ndx++; + state->response.data.num_entries++; + + state->response.length += + sizeof(struct winbindd_gr); + + } else { + DEBUG(0, ("could not lookup domain group %s\n", + domain_group_name)); + } + } - tmp = state->request.data.groupname; - parse_domain_user(tmp, name_domain, name_group); + /* Copy the list of group memberships to the end of the extra data */ - /* Reject names that don't have a domain - i.e name_domain contains the - entire name. */ + if (group_list_ndx == 0) { + goto done; + } - if (strequal(name_group, "")) { - return WINBINDD_ERROR; - } + new_extra_data = Realloc( + state->response.extra_data, + group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len); - /* Get info for the domain */ + if (!new_extra_data) { + DEBUG(0, ("out of memory\n")); + group_list_ndx = 0; + safe_free(state->response.extra_data); + state->response.extra_data = NULL; + safe_free(gr_mem_list); - if ((domain = find_domain_from_name(name_domain)) == NULL) { - DEBUG(0, ("getgrname_from_group(): could not get domain sid for " - "domain %s\n", name_domain)); - return WINBINDD_ERROR; - } + return WINBINDD_ERROR; + } - /* Check for cached user entry */ + state->response.extra_data = new_extra_data; - if (winbindd_fetch_group_cache_entry(name_domain, name_group, - &state->response.data.gr, - &state->response.extra_data, - &extra_data_len)) { - state->response.length += extra_data_len; - return WINBINDD_OK; - } + memcpy(&((char *)state->response.extra_data) + [group_list_ndx * sizeof(struct winbindd_gr)], + gr_mem_list, gr_mem_list_len); - slprintf(name, sizeof(name)-1, "%s\\%s", name_domain, name_group); + safe_free(gr_mem_list); - /* Get rid and name type from name */ - - if (!winbindd_lookup_sid_by_name(domain, name, &group_sid, - &name_type)) { - DEBUG(1, ("group %s in domain %s does not exist\n", name_group, - name_domain)); - return WINBINDD_ERROR; - } - - if ((name_type != SID_NAME_ALIAS) && (name_type != SID_NAME_DOM_GRP)) { - DEBUG(1, ("from_group: name '%s' is not a local or domain group: %d\n", - name_group, name_type)); - return WINBINDD_ERROR; - } - - /* Fill in group structure */ - - sid_split_rid(&group_sid, &group_rid); - - if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) { - DEBUG(1, ("error sursing unix gid for sid\n")); - return WINBINDD_ERROR; - } - - winbindd_fill_grent(&state->response.data.gr, - state->request.data.groupname, gid); - - if (!winbindd_fill_grent_mem(domain, group_rid, name_type, - &state->response)) { - return WINBINDD_ERROR; - } + state->response.length += gr_mem_list_len; - /* Update cached group info */ + DEBUG(10, ("getgrent(): returning %d groups, length = %d\n", + group_list_ndx, gr_mem_list_len)); - winbindd_fill_group_cache_entry(name_domain, name_group, - &state->response.data.gr, - state->response.extra_data, - state->response.length - - sizeof(struct winbindd_response)); + /* Out of domains */ - return WINBINDD_OK; + done: + return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR; } -/* Return a group structure from a gid number */ +/* List domain groups without mapping to unix ids */ -enum winbindd_result winbindd_getgrnam_from_gid(struct winbindd_cli_state - *state) +enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state) { - struct winbindd_domain *domain; - DOM_SID group_sid; - enum SID_NAME_USE name_type; - fstring group_name; - uint32 group_rid; - int extra_data_len; - - /* Get rid from gid */ - if (!winbindd_idmap_get_rid_from_gid(state->request.data.gid, &group_rid, - &domain)) { - DEBUG(1, ("Could not convert gid %d to rid\n", - state->request.data.gid)); - return WINBINDD_ERROR; - } - - /* try a cached entry */ - if (winbindd_fetch_gid_cache_entry(domain->name, state->request.data.gid, - &state->response.data.gr, - &state->response.extra_data, - &extra_data_len)) { - state->response.length += extra_data_len; - return WINBINDD_OK; - } - - /* Get sid from gid */ - - sid_copy(&group_sid, &domain->sid); - sid_append_rid(&group_sid, group_rid); - - if (!winbindd_lookup_name_by_sid(domain, &group_sid, group_name, - &name_type)) { - DEBUG(1, ("Could not lookup sid\n")); - return WINBINDD_ERROR; - } - - if (strcmp(lp_winbind_separator(),"\\")) { - string_sub(group_name, "\\", lp_winbind_separator(), sizeof(fstring)); - } - - if (!((name_type == SID_NAME_ALIAS) || (name_type == SID_NAME_DOM_GRP))) { - DEBUG(1, ("from_gid: name '%s' is not a local or domain group: %d\n", - group_name, name_type)); - return WINBINDD_ERROR; - } - - /* Fill in group structure */ - - winbindd_fill_grent(&state->response.data.gr, group_name, - state->request.data.gid); - - if (!winbindd_fill_grent_mem(domain, group_rid, name_type, - &state->response)) { - return WINBINDD_ERROR; - } - - /* Update cached group info */ - winbindd_fill_gid_cache_entry(domain->name, state->request.data.gid, - &state->response.data.gr, - state->response.extra_data, - state->response.length - - sizeof(struct winbindd_response)); - - return WINBINDD_OK; -} + uint32 total_entries = 0; + struct winbindd_domain *domain; + struct getent_state groups; + char *extra_data = NULL; + int extra_data_len = 0, i; -/* - * set/get/endgrent functions - */ + DEBUG(3, ("[%5d]: list groups\n", state->pid)); -/* "Rewind" file pointer for group database enumeration */ + /* Enumerate over trusted domains */ -enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state) -{ - struct winbindd_domain *tmp; + for (domain = domain_list; domain; domain = domain->next) { - if (state == NULL) return WINBINDD_ERROR; + /* Skip domains other than WINBINDD_DOMAIN environment + variable */ - /* Free old static data if it exists */ + if ((strcmp(state->request.domain, "") != 0) && + !check_domain_env(state->request.domain, domain->name)) { + continue; + } - if (state->getgrent_state != NULL) { - free_getent_state(state->getgrent_state); - state->getgrent_state = NULL; - } + /* Get list of sam groups */ - /* Create sam pipes for each domain we know about */ + ZERO_STRUCT(groups); + groups.domain = domain; - for (tmp = domain_list; tmp != NULL; tmp = tmp->next) { - struct getent_state *domain_state; + get_sam_group_entries(&groups); - /* Skip domains other than WINBINDD_DOMAIN environment variable */ + if (groups.num_sam_entries == 0) continue; - if ((strcmp(state->request.domain, "") != 0) && - (strcmp(state->request.domain, tmp->name) != 0)) { - continue; - } + /* Allocate some memory for extra data. Note that we limit + account names to sizeof(fstring) = 128 characters. */ - /* Create a state record for this domain */ + total_entries += groups.num_sam_entries; + extra_data = Realloc(extra_data, + sizeof(fstring) * total_entries); - if ((domain_state = (struct getent_state *) - malloc(sizeof(struct getent_state))) == NULL) { + if (!extra_data) { + return WINBINDD_ERROR; + } - return WINBINDD_ERROR; - } + /* Pack group list into extra data fields */ - ZERO_STRUCTP(domain_state); + for (i = 0; i < groups.num_sam_entries; i++) { + char *group_name = ((struct acct_info *) + groups.sam_entries)[i].acct_name; + fstring name; - /* Add to list of open domains */ + /* Convert unistring to ascii */ - domain_state->domain = tmp; - DLIST_ADD(state->getgrent_state, domain_state); - } + snprintf(name, sizeof(name), "%s%s%s", + domain->name, lp_winbind_separator(), + group_name); - return WINBINDD_OK; -} + /* Append to extra data */ + + memcpy(&extra_data[extra_data_len], name, + strlen(name)); + extra_data_len += strlen(name); -/* Close file pointer to ntdom group database */ + extra_data[extra_data_len++] = ','; + } + } -enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state) -{ - if (state == NULL) return WINBINDD_ERROR; + /* 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.length += extra_data_len; + } - free_getent_state(state->getgrent_state); - state->getgrent_state = NULL; + /* No domains may have responded but that's still OK so don't + return an error. */ - return WINBINDD_OK; + return WINBINDD_OK; } -/* Fetch next group entry from netdom database */ +/* Get user supplementary groups. This is much quicker than trying to + invert the groups database. */ -enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) +enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) { - if (state == NULL) return WINBINDD_ERROR; - - /* Process the current head of the getent_state list */ - - while(state->getgrent_state != NULL) { - struct getent_state *ent = state->getgrent_state; - - /* Get list of entries if we haven't already got them */ - - if (!ent->got_sam_entries) { - uint32 status, start_ndx = 0, start_ndx2 = 0; - - if (!winbindd_fetch_group_cache(ent->domain->name, - &ent->sam_entries, - &ent->num_sam_entries)) { - - /* Fetch group entries */ - - if (!domain_handles_open(ent->domain)) goto cleanup; - - /* Enumerate domain groups */ - - do { - status = - samr_enum_dom_groups(&ent->domain->sam_dom_handle, - &start_ndx, 0x100000, - &ent->sam_entries, - &ent->num_sam_entries); - } while (status == STATUS_MORE_ENTRIES); - - /* Enumerate domain aliases */ - - do { - status = - samr_enum_dom_aliases(&ent->domain->sam_dom_handle, - &start_ndx2, 0x100000, - &ent->sam_entries, - &ent->num_sam_entries); - } while (status == STATUS_MORE_ENTRIES); - - /* Fill cache with received entries */ - - winbindd_fill_group_cache(ent->domain->name, ent->sam_entries, - ent->num_sam_entries); - } - - ent->got_sam_entries = True; - } - - /* Send back a group */ - - while (ent->sam_entry_index < ent->num_sam_entries) { - enum winbindd_result result; - fstring domain_group_name; - char *group_name = (ent->sam_entries) - [ent->sam_entry_index].acct_name; - - /* Prepend domain to name */ - - slprintf(domain_group_name, sizeof(domain_group_name)-1, - "%s%s%s", ent->domain->name, lp_winbind_separator(), group_name); - - /* Get group entry from group name */ - - fstrcpy(state->request.data.groupname, domain_group_name); - result = winbindd_getgrnam_from_group(state); - - ent->sam_entry_index++; - - if (result == WINBINDD_OK) { - return result; - } - - /* Try next group */ - - DEBUG(1, ("could not getgrnam_from_group for group name %s\n", - domain_group_name)); - } - - /* We've exhausted all users for this pipe - close it down and - start on the next one. */ - - cleanup: - - /* Free mallocated memory for sam entries. The data stored here - may have been allocated from the cache. */ - - if (ent->sam_entries != NULL) free(ent->sam_entries); - ent->sam_entries = NULL; - - /* Free state information for this domain */ - - { - struct getent_state *old_ent; - - old_ent = state->getgrent_state; - DLIST_REMOVE(state->getgrent_state, state->getgrent_state); - free(old_ent); - } - } - - /* Out of pipes so we're done */ - - return WINBINDD_ERROR; + fstring name_domain, name_user, name; + DOM_SID user_sid; + enum SID_NAME_USE name_type; + uint32 user_rid, num_groups, num_gids; + DOM_GID *user_groups = NULL; + struct winbindd_domain *domain; + enum winbindd_result result; + gid_t *gid_list; + int i; + + DEBUG(3, ("[%5d]: getgroups %s\n", state->pid, + state->request.data.username)); + + if (state == NULL) return WINBINDD_ERROR; + + /* Parse domain and username */ + + parse_domain_user(state->request.data.username, name_domain, + name_user); + + /* Reject names that don't have a domain - i.e name_domain contains + the entire name. */ + + if (strequal(name_domain, "")) { + return WINBINDD_ERROR; + } + + /* Get info for the domain */ + + if ((domain = find_domain_from_name(name_domain)) == NULL) { + DEBUG(0, ("could not find domain entry for domain %s\n", + name_domain)); + return WINBINDD_ERROR; + } + + if (!domain_handles_open(domain)) { + return WINBINDD_ERROR; + } + + slprintf(name, sizeof(name) - 1, "%s\\%s", name_domain, name_user); + + /* Get rid and name type from name. The following costs 1 packet */ + + if (!winbindd_lookup_sid_by_name(name, &user_sid, &name_type)) { + DEBUG(1, ("user '%s' does not exist\n", name_user)); + return WINBINDD_ERROR; + } + + if (name_type != SID_NAME_USER) { + DEBUG(1, ("name '%s' is not a user name: %d\n", name_user, + name_type)); + return WINBINDD_ERROR; + } + + sid_split_rid(&user_sid, &user_rid); + + if (!winbindd_lookup_usergroups(domain, user_rid, &num_groups, + &user_groups)) { + return WINBINDD_ERROR; + } + + /* Copy data back to client */ + + num_gids = 0; + gid_list = malloc(sizeof(gid_t) * num_groups); + + if (state->response.extra_data) { + result = WINBINDD_ERROR; + goto done; + } + + for (i = 0; i < num_groups; i++) { + if (!winbindd_idmap_get_gid_from_rid( + domain->name, user_groups[i].g_rid, + &gid_list[num_gids])) { + + DEBUG(1, ("unable to convert group rid %d to gid\n", + user_groups[i].g_rid)); + continue; + } + + num_gids++; + } + + 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: + safe_free(user_groups); + + return result; } -- cgit