summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2008-06-30 11:23:05 -0700
committerKarolin Seeger <kseeger@samba.org>2008-06-30 21:11:09 +0200
commit4abfaaf5c47a1c11e67af4c11cef6172230997ef (patch)
treec8f9a9202b313cfbbb4ed9ab836d1f61225e0637
parentbf1955b9b6e11559375971251fc9cb850b871160 (diff)
downloadsamba-4abfaaf5c47a1c11e67af4c11cef6172230997ef.tar.gz
After technical consultation, add Steven Danneman's <steven.danneman@isilon.com> patch to make winbindd enum users and groups async.
We need this for 3.2.0 official. Jeremy. (cherry picked from commit d05451c2c256e04870ebe6467f38585dad72f3a9)
-rw-r--r--source/winbindd/winbindd.h7
-rw-r--r--source/winbindd/winbindd_ads.c2
-rw-r--r--source/winbindd/winbindd_async.c156
-rw-r--r--source/winbindd/winbindd_cache.c8
-rw-r--r--source/winbindd/winbindd_domain.c8
-rw-r--r--source/winbindd/winbindd_group.c87
-rw-r--r--source/winbindd/winbindd_misc.c123
-rw-r--r--source/winbindd/winbindd_user.c94
8 files changed, 305 insertions, 180 deletions
diff --git a/source/winbindd/winbindd.h b/source/winbindd/winbindd.h
index 0840e581fe6..42a1100f02b 100644
--- a/source/winbindd/winbindd.h
+++ b/source/winbindd/winbindd.h
@@ -362,7 +362,12 @@ struct winbindd_tdc_domain {
uint32 trust_type;
};
-
+/* Switch for listing users or groups */
+enum ent_type {
+ LIST_USERS = 0,
+ LIST_GROUPS,
+};
+
#include "winbindd/winbindd_proto.h"
#define WINBINDD_ESTABLISH_LOOP 30
diff --git a/source/winbindd/winbindd_ads.c b/source/winbindd/winbindd_ads.c
index ae8ad9dd1a8..35ffe700f15 100644
--- a/source/winbindd/winbindd_ads.c
+++ b/source/winbindd/winbindd_ads.c
@@ -393,7 +393,7 @@ static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
* using LDAP.
*
* if we ever need to enumerate domain local groups separately,
- * then this the optimization in enum_dom_groups() will need
+ * then this optimization in enum_dom_groups() will need
* to be split out
*/
*num_entries = 0;
diff --git a/source/winbindd/winbindd_async.c b/source/winbindd/winbindd_async.c
index 2ff5ef230d3..10b7e48154d 100644
--- a/source/winbindd/winbindd_async.c
+++ b/source/winbindd/winbindd_async.c
@@ -453,6 +453,162 @@ enum winbindd_result winbindd_dual_lookupname(struct winbindd_domain *domain,
return WINBINDD_OK;
}
+/* This is the first callback after enumerating users/groups from a domain */
+static void listent_recv(TALLOC_CTX *mem_ctx, bool success,
+ struct winbindd_response *response,
+ void *c, void *private_data)
+{
+ void (*cont)(void *priv, bool succ, fstring dom_name, char *data) =
+ (void (*)(void *, bool, fstring, char*))c;
+
+ if (!success || response->result != WINBINDD_OK) {
+ DEBUG(5, ("list_ent() failed!\n"));
+ cont(private_data, False, response->data.name.dom_name, NULL);
+ return;
+ }
+
+ cont(private_data, True, response->data.name.dom_name,
+ response->extra_data.data);
+
+ SAFE_FREE(response->extra_data.data);
+}
+
+/* Request the name of all users/groups in a single domain */
+void winbindd_listent_async(TALLOC_CTX *mem_ctx,
+ struct winbindd_domain *domain,
+ void (*cont)(void *private_data, bool success,
+ fstring dom_name, char* extra_data),
+ void *private_data, enum ent_type type)
+{
+ struct winbindd_request request;
+
+ ZERO_STRUCT(request);
+ if (type == LIST_USERS)
+ request.cmd = WINBINDD_LIST_USERS;
+ else if (type == LIST_GROUPS)
+ request.cmd = WINBINDD_LIST_GROUPS;
+
+ do_async_domain(mem_ctx, domain, &request, listent_recv,
+ (void *)cont, private_data);
+}
+
+enum winbindd_result winbindd_dual_list_users(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state)
+{
+ WINBIND_USERINFO *info;
+ NTSTATUS status;
+ struct winbindd_methods *methods;
+ uint32 num_entries = 0;
+ char *extra_data = NULL;
+ uint32_t extra_data_len = 0, i;
+
+ /* Must copy domain into response first for debugging in parent */
+ fstrcpy(state->response.data.name.dom_name, domain->name);
+
+ /* Query user info */
+ methods = domain->methods;
+ status = methods->query_user_list(domain, state->mem_ctx,
+ &num_entries, &info);
+
+ if (!NT_STATUS_IS_OK(status))
+ return WINBINDD_ERROR;
+
+ if (num_entries == 0)
+ return WINBINDD_OK;
+
+ /* Allocate some memory for extra data. Note that we limit
+ account names to sizeof(fstring) = 256 characters.
+ +1 for the ',' between group names */
+ extra_data = (char *)SMB_REALLOC(extra_data,
+ (sizeof(fstring) + 1) * num_entries);
+
+ if (!extra_data) {
+ DEBUG(0,("failed to enlarge buffer!\n"));
+ return WINBINDD_ERROR;
+ }
+
+ /* Pack user list into extra data fields */
+ for (i = 0; i < num_entries; i++) {
+ fstring acct_name, name;
+
+ if (info[i].acct_name == NULL)
+ fstrcpy(acct_name, "");
+ else
+ fstrcpy(acct_name, info[i].acct_name);
+
+ fill_domain_username(name, domain->name, acct_name, True);
+ /* Append to extra data */
+ memcpy(&extra_data[extra_data_len], name, strlen(name));
+ extra_data_len += strlen(name);
+ extra_data[extra_data_len++] = ',';
+ }
+
+ /* Assign extra_data fields in response structure */
+ if (extra_data) {
+ /* remove trailing ',' */
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data.data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ return WINBINDD_OK;
+}
+
+enum winbindd_result winbindd_dual_list_groups(struct winbindd_domain *domain,
+ struct winbindd_cli_state *state)
+{
+ struct getent_state groups = {};
+ char *extra_data = NULL;
+ uint32_t extra_data_len = 0, i;
+
+ /* Must copy domain into response first for debugging in parent */
+ fstrcpy(state->response.data.name.dom_name, domain->name);
+ fstrcpy(groups.domain_name, domain->name);
+
+ /* Get list of sam groups */
+ if (!get_sam_group_entries(&groups)) {
+ /* this domain is empty or in an error state */
+ return WINBINDD_ERROR;
+ }
+
+ /* Allocate some memory for extra data. Note that we limit
+ account names to sizeof(fstring) = 256 characters.
+ +1 for the ',' between group names */
+ extra_data = (char *)SMB_REALLOC(extra_data,
+ (sizeof(fstring) + 1) * groups.num_sam_entries);
+
+ if (!extra_data) {
+ DEBUG(0,("failed to enlarge buffer!\n"));
+ SAFE_FREE(groups.sam_entries);
+ return WINBINDD_ERROR;
+ }
+
+ /* Pack group list into extra data fields */
+ for (i = 0; i < groups.num_sam_entries; i++) {
+ char *group_name = ((struct acct_info *)
+ groups.sam_entries)[i].acct_name;
+ fstring name;
+
+ fill_domain_username(name, domain->name, group_name, True);
+ /* Append to extra data */
+ memcpy(&extra_data[extra_data_len], name, strlen(name));
+ extra_data_len += strlen(name);
+ extra_data[extra_data_len++] = ',';
+ }
+
+ SAFE_FREE(groups.sam_entries);
+
+ /* Assign extra_data fields in response structure */
+ if (extra_data) {
+ /* remove trailing ',' */
+ extra_data[extra_data_len - 1] = '\0';
+ state->response.extra_data.data = extra_data;
+ state->response.length += extra_data_len;
+ }
+
+ return WINBINDD_OK;
+}
+
bool print_sidlist(TALLOC_CTX *mem_ctx, const DOM_SID *sids,
size_t num_sids, char **result, ssize_t *len)
{
diff --git a/source/winbindd/winbindd_cache.c b/source/winbindd/winbindd_cache.c
index a11e96e5625..dda8b03d5f9 100644
--- a/source/winbindd/winbindd_cache.c
+++ b/source/winbindd/winbindd_cache.c
@@ -502,8 +502,14 @@ static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
mode domain or not. And that we can contact it. */
if ( winbindd_can_contact_domain( domain ) ) {
+ struct winbindd_methods *orig_backend = domain->backend;
status = domain->backend->sequence_number(domain,
&domain->sequence_number);
+ if (domain->backend != orig_backend) {
+ /* Try again. */
+ status = domain->backend->sequence_number(domain,
+ &domain->sequence_number);
+ }
} else {
/* just use the current time */
status = NT_STATUS_OK;
@@ -524,7 +530,7 @@ static void refresh_sequence_number(struct winbindd_domain *domain, bool force)
domain->last_status = status;
domain->last_seq_check = time(NULL);
- /* save the new sequence number ni the cache */
+ /* save the new sequence number in the cache */
store_cache_seqnum( domain );
done:
diff --git a/source/winbindd/winbindd_domain.c b/source/winbindd/winbindd_domain.c
index 1b758cdf40c..2e8c6175ca0 100644
--- a/source/winbindd/winbindd_domain.c
+++ b/source/winbindd/winbindd_domain.c
@@ -50,6 +50,14 @@ static const struct winbindd_child_dispatch_table domain_dispatch_table[] = {
.struct_cmd = WINBINDD_LOOKUPRIDS,
.struct_fn = winbindd_dual_lookuprids,
},{
+ .name = "LIST_USERS",
+ .struct_cmd = WINBINDD_LIST_USERS,
+ .struct_fn = winbindd_dual_list_users,
+ },{
+ .name = "LIST_GROUPS",
+ .struct_cmd = WINBINDD_LIST_GROUPS,
+ .struct_fn = winbindd_dual_list_groups,
+ },{
.name = "LIST_TRUSTDOM",
.struct_cmd = WINBINDD_LIST_TRUSTDOM,
.struct_fn = winbindd_dual_list_trusted_domains,
diff --git a/source/winbindd/winbindd_group.c b/source/winbindd/winbindd_group.c
index ce6ca371e8f..20b90e3283d 100644
--- a/source/winbindd/winbindd_group.c
+++ b/source/winbindd/winbindd_group.c
@@ -971,10 +971,9 @@ void winbindd_endgrent(struct winbindd_cli_state *state)
/* 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. */
+ Return True if some groups were returned, False otherwise. */
-static bool get_sam_group_entries(struct getent_state *ent)
+bool get_sam_group_entries(struct getent_state *ent)
{
NTSTATUS status;
uint32 num_entries;
@@ -1341,89 +1340,9 @@ void winbindd_getgrent(struct winbindd_cli_state *state)
}
/* List domain groups without mapping to unix ids */
-
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;
- unsigned int extra_data_len = 0, i;
-
- DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
-
- /* Ensure null termination */
- state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
- which_domain = state->request.domain_name;
-
- /* Enumerate over trusted domains */
-
- for (domain = domain_list(); domain; domain = domain->next) {
- struct getent_state groups;
-
- /* if we have a domain name restricting the request and this
- one in the list doesn't match, then just bypass the remainder
- of the loop */
-
- if ( *which_domain && !strequal(which_domain, domain->name) )
- continue;
-
- ZERO_STRUCT(groups);
-
- /* Get list of sam groups */
-
- fstrcpy(groups.domain_name, domain->name);
-
- get_sam_group_entries(&groups);
-
- if (groups.num_sam_entries == 0) {
- /* this domain is empty or in an error state */
- continue;
- }
-
- /* keep track the of the total number of groups seen so
- far over all domains */
- total_entries += groups.num_sam_entries;
-
- /* Allocate some memory for extra data. Note that we limit
- account names to sizeof(fstring) = 128 characters. */
- extra_data = (char *)SMB_REALLOC(
- extra_data, sizeof(fstring) * total_entries);
-
- if (!extra_data) {
- DEBUG(0,("failed to enlarge buffer!\n"));
- request_error(state);
- return;
- }
-
- /* Pack group list into extra data fields */
- for (i = 0; i < groups.num_sam_entries; i++) {
- char *group_name = ((struct acct_info *)
- groups.sam_entries)[i].acct_name;
- fstring name;
-
- fill_domain_username(name, domain->name, group_name, True);
- /* Append to extra data */
- memcpy(&extra_data[extra_data_len], name,
- strlen(name));
- extra_data_len += strlen(name);
- extra_data[extra_data_len++] = ',';
- }
-
- SAFE_FREE(groups.sam_entries);
- }
-
- /* Assign extra_data fields in response structure */
- if (extra_data) {
- extra_data[extra_data_len - 1] = '\0';
- 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. */
-
- request_ok(state);
+ winbindd_list_ent(state, LIST_GROUPS);
}
/* Get user supplementary groups. This is much quicker than trying to
diff --git a/source/winbindd/winbindd_misc.c b/source/winbindd/winbindd_misc.c
index f63aa168904..ddfaa7d7ae2 100644
--- a/source/winbindd/winbindd_misc.c
+++ b/source/winbindd/winbindd_misc.c
@@ -97,6 +97,129 @@ enum winbindd_result winbindd_dual_check_machine_acct(struct winbindd_domain *do
return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
}
+/* Helpers for listing user and group names */
+
+const char *ent_type_strings[] = {"users",
+ "groups"};
+
+static const char *get_ent_type_string(enum ent_type type)
+{
+ return ent_type_strings[type];
+}
+
+struct listent_state {
+ TALLOC_CTX *mem_ctx;
+ struct winbindd_cli_state *cli_state;
+ enum ent_type type;
+ int domain_count;
+ char *extra_data;
+ uint32_t extra_data_len;
+};
+
+static void listent_recv(void *private_data, bool success, fstring dom_name,
+ char *extra_data);
+
+/* List domain users/groups without mapping to unix ids */
+void winbindd_list_ent(struct winbindd_cli_state *state, enum ent_type type)
+{
+ struct winbindd_domain *domain;
+ const char *which_domain;
+ struct listent_state *ent_state;
+
+ DEBUG(3, ("[%5lu]: list %s\n", (unsigned long)state->pid,
+ get_ent_type_string(type)));
+
+ /* Ensure null termination */
+ state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
+ which_domain = state->request.domain_name;
+
+ /* Initialize listent_state */
+ ent_state = TALLOC_P(state->mem_ctx, struct listent_state);
+ if (ent_state == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ request_error(state);
+ return;
+ }
+
+ ent_state->mem_ctx = state->mem_ctx;
+ ent_state->cli_state = state;
+ ent_state->type = type;
+ ent_state->domain_count = 0;
+ ent_state->extra_data = NULL;
+ ent_state->extra_data_len = 0;
+
+ /* Must count the full list of expected domains before we request data
+ * from any of them. Otherwise it's possible for a connection to the
+ * first domain to fail, call listent_recv(), and return to the
+ * client without checking any other domains. */
+ for (domain = domain_list(); domain; domain = domain->next) {
+ /* if we have a domain name restricting the request and this
+ one in the list doesn't match, then just bypass the remainder
+ of the loop */
+ if ( *which_domain && !strequal(which_domain, domain->name) )
+ continue;
+
+ ent_state->domain_count++;
+ }
+
+ /* Make sure we're enumerating at least one domain */
+ if (!ent_state->domain_count) {
+ request_ok(state);
+ return;
+ }
+
+ /* Enumerate list of trusted domains and request user/group list from
+ * each */
+ for (domain = domain_list(); domain; domain = domain->next) {
+ if ( *which_domain && !strequal(which_domain, domain->name) )
+ continue;
+
+ winbindd_listent_async(state->mem_ctx, domain,
+ listent_recv, ent_state, type);
+ }
+}
+
+static void listent_recv(void *private_data, bool success, fstring dom_name,
+ char *extra_data)
+{
+ /* extra_data comes to us as a '\0' terminated string of comma
+ separated users or groups */
+ struct listent_state *state = talloc_get_type_abort(
+ private_data, struct listent_state);
+
+ /* Append users/groups from one domain onto the whole list */
+ if (extra_data) {
+ DEBUG(5, ("listent_recv: %s returned %s.\n",
+ dom_name, get_ent_type_string(state->type)));
+ if (!state->extra_data)
+ state->extra_data = talloc_asprintf(state->mem_ctx,
+ "%s", extra_data);
+ else
+ state->extra_data = talloc_asprintf_append(
+ state->extra_data,
+ ",%s", extra_data);
+ /* Add one for the '\0' and each additional ',' */
+ state->extra_data_len += strlen(extra_data) + 1;
+ }
+ else {
+ DEBUG(5, ("listent_recv: %s returned no %s.\n",
+ dom_name, get_ent_type_string(state->type)));
+ }
+
+ if (--state->domain_count)
+ /* Still waiting for some child domains to return */
+ return;
+
+ /* Return list of all users/groups to the client */
+ if (state->extra_data) {
+ state->cli_state->response.extra_data.data =
+ SMB_STRDUP(state->extra_data);
+ state->cli_state->response.length += state->extra_data_len;
+ }
+
+ request_ok(state->cli_state);
+}
+
/* Constants and helper functions for determining domain trust types */
enum trust_type {
diff --git a/source/winbindd/winbindd_user.c b/source/winbindd/winbindd_user.c
index 6241d84fe6d..45918383b75 100644
--- a/source/winbindd/winbindd_user.c
+++ b/source/winbindd/winbindd_user.c
@@ -778,99 +778,7 @@ void winbindd_getpwent(struct winbindd_cli_state *state)
}
/* List domain users without mapping to unix ids */
-
void winbindd_list_users(struct winbindd_cli_state *state)
{
- struct winbindd_domain *domain;
- WINBIND_USERINFO *info;
- const char *which_domain;
- uint32 num_entries = 0, total_entries = 0;
- char *extra_data = NULL;
- int extra_data_len = 0;
- enum winbindd_result rv = WINBINDD_ERROR;
-
- DEBUG(3, ("[%5lu]: list users\n", (unsigned long)state->pid));
-
- /* Ensure null termination */
- state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
- which_domain = state->request.domain_name;
-
- /* Enumerate over trusted domains */
-
- for (domain = domain_list(); domain; domain = domain->next) {
- NTSTATUS status;
- struct winbindd_methods *methods;
- unsigned int i;
-
- /* if we have a domain name restricting the request and this
- one in the list doesn't match, then just bypass the remainder
- of the loop */
-
- if ( *which_domain && !strequal(which_domain, domain->name) )
- continue;
-
- methods = domain->methods;
-
- /* Query display info */
- status = methods->query_user_list(domain, state->mem_ctx,
- &num_entries, &info);
-
- if (!NT_STATUS_IS_OK(status)) {
- continue;
- }
-
- if (num_entries == 0)
- continue;
-
- /* Allocate some memory for extra data */
- total_entries += num_entries;
-
- extra_data = (char *)SMB_REALLOC(
- extra_data, sizeof(fstring) * total_entries);
-
- if (!extra_data) {
- DEBUG(0,("failed to enlarge buffer!\n"));
- goto done;
- }
-
- /* Pack user list into extra data fields */
-
- for (i = 0; i < num_entries; i++) {
- fstring acct_name, name;
-
- if (!info[i].acct_name) {
- fstrcpy(acct_name, "");
- } else {
- fstrcpy(acct_name, info[i].acct_name);
- }
-
- fill_domain_username(name, domain->name, acct_name, True);
-
- /* Append to extra data */
- memcpy(&extra_data[extra_data_len], name,
- strlen(name));
- extra_data_len += strlen(name);
- extra_data[extra_data_len++] = ',';
- }
- }
-
- /* Assign extra_data fields in response structure */
-
- if (extra_data) {
- extra_data[extra_data_len - 1] = '\0';
- state->response.extra_data.data = extra_data;
- state->response.length += extra_data_len;
- }
-
- /* No domains responded but that's still OK so don't return an
- error. */
-
- rv = WINBINDD_OK;
-
- done:
-
- if (rv == WINBINDD_OK)
- request_ok(state);
- else
- request_error(state);
+ winbindd_list_ent(state, LIST_USERS);
}