summaryrefslogtreecommitdiff
path: root/source3/nsswitch/winbindd_group.c
diff options
context:
space:
mode:
authorTim Potter <tpot@samba.org>2001-05-07 04:32:40 +0000
committerTim Potter <tpot@samba.org>2001-05-07 04:32:40 +0000
commita36f9250e7c9446f3eece6d8db29fcbde99256fb (patch)
tree5b981dc1171e92f4a28232c3cc7b6d619054ea75 /source3/nsswitch/winbindd_group.c
parentc2887d57b5ff6da52aefac4657c23c142977ee2e (diff)
downloadsamba-a36f9250e7c9446f3eece6d8db29fcbde99256fb.tar.gz
samba-a36f9250e7c9446f3eece6d8db29fcbde99256fb.tar.bz2
samba-a36f9250e7c9446f3eece6d8db29fcbde99256fb.zip
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)
Diffstat (limited to 'source3/nsswitch/winbindd_group.c')
-rw-r--r--source3/nsswitch/winbindd_group.c1371
1 files changed, 774 insertions, 597 deletions
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;
}