summaryrefslogtreecommitdiff
path: root/src/providers
diff options
context:
space:
mode:
authorStephen Gallagher <sgallagh@redhat.com>2012-09-18 16:51:15 -0400
committerJakub Hrozek <jhrozek@redhat.com>2012-09-24 15:00:11 +0200
commitd0e0e73e86f2afdb7f8fefbed70fda8d77b1c25a (patch)
tree445f1806f9f1344dfa3b4f05b5c9294cdc541696 /src/providers
parente6ba224432bfcd64802222a3544bc38c179727cd (diff)
downloadsssd-d0e0e73e86f2afdb7f8fefbed70fda8d77b1c25a.tar.gz
sssd-d0e0e73e86f2afdb7f8fefbed70fda8d77b1c25a.tar.bz2
sssd-d0e0e73e86f2afdb7f8fefbed70fda8d77b1c25a.zip
AD: Optimize initgroups lookups with tokenGroups
https://fedorahosted.org/sssd/ticket/1355
Diffstat (limited to 'src/providers')
-rw-r--r--src/providers/ldap/sdap_async.h16
-rw-r--r--src/providers/ldap/sdap_async_initgroups.c24
-rw-r--r--src/providers/ldap/sdap_async_initgroups_ad.c277
3 files changed, 313 insertions, 4 deletions
diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h
index d024e30a..8c16d94e 100644
--- a/src/providers/ldap/sdap_async.h
+++ b/src/providers/ldap/sdap_async.h
@@ -31,6 +31,8 @@
#include "providers/ldap/sdap_id_op.h"
#include "providers/fail_over.h"
+#define AD_TOKENGROUPS_ATTR "tokenGroups"
+
struct tevent_req *sdap_connect_send(TALLOC_CTX *memctx,
struct tevent_context *ev,
struct sdap_options *opts,
@@ -275,4 +277,18 @@ sdap_get_ad_match_rule_initgroups_send(TALLOC_CTX *mem_ctx,
errno_t
sdap_get_ad_match_rule_initgroups_recv(struct tevent_req *req);
+
+struct tevent_req *
+sdap_get_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct sysdb_ctx *sysdb,
+ struct sdap_handle *sh,
+ const char *name,
+ const char *orig_dn,
+ int timeout);
+
+errno_t
+sdap_get_ad_tokengroups_initgroups_recv(struct tevent_req *req);
+
#endif /* _SDAP_ASYNC_H_ */
diff --git a/src/providers/ldap/sdap_async_initgroups.c b/src/providers/ldap/sdap_async_initgroups.c
index d55f661f..71b4536b 100644
--- a/src/providers/ldap/sdap_async_initgroups.c
+++ b/src/providers/ldap/sdap_async_initgroups.c
@@ -2642,6 +2642,8 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
const char *orig_dn;
const char *cname;
bool in_transaction = false;
+ bool use_id_mapping =
+ dp_opt_get_bool(state->opts->basic, SDAP_ID_MAPPING);
DEBUG(9, ("Receiving info for the user\n"));
@@ -2731,9 +2733,17 @@ static void sdap_get_initgr_user(struct tevent_req *subreq)
return;
}
- if (state->opts->support_matching_rule
- && dp_opt_get_bool(state->opts->basic,
- SDAP_AD_MATCHING_RULE_INITGROUPS)) {
+ if (use_id_mapping
+ && state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
+ /* Take advantage of AD's tokenGroups mechanism to look up all
+ * parent groups in a single request.
+ */
+ subreq = sdap_get_ad_tokengroups_initgroups_send(
+ state, state->ev, state->opts, state->sysdb,
+ state->sh, cname, orig_dn, state->timeout);
+ } else if (state->opts->support_matching_rule
+ && dp_opt_get_bool(state->opts->basic,
+ SDAP_AD_MATCHING_RULE_INITGROUPS)) {
/* Take advantage of AD's extensibleMatch filter to look up
* all parent groups in a single request.
*/
@@ -2815,7 +2825,13 @@ static void sdap_get_initgr_done(struct tevent_req *subreq)
case SDAP_SCHEMA_RFC2307BIS:
case SDAP_SCHEMA_AD:
- if (dp_opt_get_bool(state->opts->basic, SDAP_AD_MATCHING_RULE_INITGROUPS)) {
+ if (use_id_mapping
+ && state->opts->dc_functional_level >= DS_BEHAVIOR_WIN2008) {
+ ret = sdap_get_ad_tokengroups_initgroups_recv(subreq);
+ }
+ else if (state->opts->support_matching_rule
+ && dp_opt_get_bool(state->opts->basic,
+ SDAP_AD_MATCHING_RULE_INITGROUPS)) {
ret = sdap_get_ad_match_rule_initgroups_recv(subreq);
} else {
ret = sdap_initgr_rfc2307bis_recv(subreq);
diff --git a/src/providers/ldap/sdap_async_initgroups_ad.c b/src/providers/ldap/sdap_async_initgroups_ad.c
index f74729f4..1333e308 100644
--- a/src/providers/ldap/sdap_async_initgroups_ad.c
+++ b/src/providers/ldap/sdap_async_initgroups_ad.c
@@ -24,6 +24,8 @@
#include "providers/ldap/sdap_async.h"
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_async_private.h"
+#include "providers/ldap/sdap_idmap.h"
+#include "lib/idmap/sss_idmap.h"
struct sdap_ad_match_rule_initgr_state {
struct tevent_context *ev;
@@ -290,3 +292,278 @@ sdap_get_ad_match_rule_initgroups_recv(struct tevent_req *req)
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
+
+struct sdap_ad_tokengroups_initgr_state {
+ struct tevent_context *ev;
+ struct sdap_options *opts;
+ struct sysdb_ctx *sysdb;
+ struct sdap_handle *sh;
+ const char *username;
+};
+
+static void
+sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *req);
+
+struct tevent_req *
+sdap_get_ad_tokengroups_initgroups_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sdap_options *opts,
+ struct sysdb_ctx *sysdb,
+ struct sdap_handle *sh,
+ const char *name,
+ const char *orig_dn,
+ int timeout)
+{
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ struct sdap_ad_tokengroups_initgr_state *state;
+ const char *attrs[] = {AD_TOKENGROUPS_ATTR, NULL};
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct sdap_ad_tokengroups_initgr_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->sysdb = sysdb;
+ state->sh = sh;
+ state->username = name;
+
+ subreq = sdap_get_generic_send(
+ state, state->ev, state->opts, state->sh,
+ orig_dn, LDAP_SCOPE_BASE, NULL, attrs,
+ NULL, 0, timeout, false);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ tevent_req_post(req, ev);
+ return req;
+ }
+
+ tevent_req_set_callback(subreq,
+ sdap_get_ad_tokengroups_initgroups_lookup_done,
+ req);
+ return req;
+}
+
+static void
+sdap_get_ad_tokengroups_initgroups_lookup_done(struct tevent_req *subreq)
+{
+ errno_t ret, sret;
+ enum idmap_error_code err;
+ size_t user_count, group_count, i;
+ TALLOC_CTX *tmp_ctx;
+ bool in_transaction = false;
+ char *sid_str;
+ gid_t gid;
+ time_t now;
+ struct sysdb_attrs **users;
+ struct ldb_message_element *el;
+ struct ldb_message *msg;
+ char **ldap_grouplist;
+ char **sysdb_grouplist;
+ char **add_groups;
+ char **del_groups;
+ const char *attrs[] = { SYSDB_NAME, NULL };
+ const char *group_name;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_ad_tokengroups_initgr_state *state =
+ tevent_req_data(req, struct sdap_ad_tokengroups_initgr_state);
+
+ tmp_ctx = talloc_new(NULL);
+ if (!tmp_ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sdap_get_generic_recv(subreq, tmp_ctx, &user_count, &users);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("LDAP search failed: [%s]\n", strerror(ret)));
+ goto done;
+ }
+
+ if (user_count != 1) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("More than one result on a base search!\n"));
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Get the list of group SIDs */
+ ret = sysdb_attrs_get_el_ext(users[0], AD_TOKENGROUPS_ATTR,
+ false, &el);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("No tokenGroups entries for [%s]\n",
+ state->username));
+ /* No groups in LDAP. We need to ensure that the
+ * sysdb matches.
+ */
+ el = talloc_zero(tmp_ctx, struct ldb_message_element);
+ if (!el) {
+ ret = ENOMEM;
+ goto done;
+ }
+ el->num_values = 0;
+
+ /* This will skip the group-processing loop below
+ * and proceed to removing any sysdb groups.
+ */
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not read tokenGroups attribute: [%s]\n",
+ strerror(ret)));
+ goto done;
+ }
+ }
+
+ /* Process the groups */
+ now = time(NULL);
+
+ ret = sysdb_transaction_start(state->sysdb);
+ if (ret != EOK) goto done;
+ in_transaction = true;
+
+ ldap_grouplist = talloc_array(tmp_ctx, char *, el->num_values + 1);
+ if (!ldap_grouplist) {
+ ret = ENOMEM;
+ goto done;
+ }
+ group_count = 0;
+
+ for (i = 0; i < el->num_values; i++) {
+ /* Get the SID and convert it to a GID */
+
+ err = sss_idmap_bin_sid_to_sid(state->opts->idmap_ctx->map,
+ el->values[i].data,
+ el->values[i].length,
+ &sid_str);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not convert binary SID to string: [%s]. Skipping\n",
+ idmap_error_string(err)));
+ continue;
+ }
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("Processing membership SID [%s]\n",
+ sid_str));
+ ret = sdap_idmap_sid_to_unix(state->opts->idmap_ctx, sid_str,
+ &gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not convert SID to GID: [%s]. Skipping\n",
+ strerror(ret)));
+ continue;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("Processing membership GID [%lu]\n",
+ gid));
+
+ /* Check whether this GID already exists in the sysdb */
+ ret = sysdb_search_group_by_gid(tmp_ctx, state->sysdb,
+ gid, attrs, &msg);
+ if (ret == EOK) {
+ group_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+ if (!group_name) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not retrieve group name from sysdb\n"));
+ ret = EINVAL;
+ goto done;
+ }
+ } else if (ret == ENOENT) {
+ /* This is a new group. For now, we will store it
+ * under the name of its SID. When a direct lookup of
+ * the group or its GID occurs, it will replace this
+ * temporary entry.
+ */
+ group_name = sid_str;
+ ret = sysdb_add_incomplete_group(state->sysdb, group_name,
+ gid, NULL, false, now);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not create incomplete group: [%s]\n",
+ strerror(ret)));
+ goto done;
+ }
+ }
+
+ ldap_grouplist[group_count] =
+ talloc_strdup(ldap_grouplist, group_name);
+ if (!ldap_grouplist[group_count]) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ group_count++;
+ }
+ ldap_grouplist[group_count] = NULL;
+
+ /* Get the current sysdb group list for this user
+ * so we can update it.
+ */
+ ret = get_sysdb_grouplist(state, state->sysdb, state->username,
+ &sysdb_grouplist);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not get the list of groups for [%s] in the sysdb: "
+ "[%s]\n",
+ state->username, strerror(ret)));
+ goto done;
+ }
+
+ /* Find the differences between the sysdb and LDAP lists
+ * Groups in the sysdb only must be removed.
+ */
+ ret = diff_string_lists(tmp_ctx, ldap_grouplist, sysdb_grouplist,
+ &add_groups, &del_groups, NULL);
+ if (ret != EOK) goto done;
+
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("Updating memberships for [%s]\n", state->username));
+ ret = sysdb_update_members(state->sysdb, state->username,
+ SYSDB_MEMBER_USER,
+ (const char *const *) add_groups,
+ (const char *const *) del_groups);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Membership update failed [%d]: %s\n",
+ ret, strerror(ret)));
+ goto done;
+ }
+
+ ret = sysdb_transaction_commit(state->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Could not commit transaction! [%s]\n",
+ strerror(ret)));
+ goto done;
+ }
+ in_transaction = false;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(state->sysdb);
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ ("Could not cancel transaction! [%s]\n",
+ sret));
+ }
+
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, ret);
+ }
+ talloc_free(tmp_ctx);
+ return;
+}
+
+errno_t
+sdap_get_ad_tokengroups_initgroups_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ return EOK;
+}