summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Březina <pbrezina@redhat.com>2012-01-24 13:42:59 +0100
committerStephen Gallagher <sgallagh@redhat.com>2012-02-04 08:27:16 -0500
commit41ef946f3f74a46b9e26118116e4811e259b30ef (patch)
treed88a5b7a94eaee2f2407c1ffa43ff3497d99c90b
parentbd92e8ee315d4da9350b9ef0358c88a7b54aeebe (diff)
downloadsssd-41ef946f3f74a46b9e26118116e4811e259b30ef.tar.gz
sssd-41ef946f3f74a46b9e26118116e4811e259b30ef.tar.bz2
sssd-41ef946f3f74a46b9e26118116e4811e259b30ef.zip
SUDO Integration - in-memory cache in responder
New sudo responder option: cache_timeout https://fedorahosted.org/sssd/ticket/1111
-rw-r--r--Makefile.am1
-rw-r--r--src/confdb/confdb.h2
-rw-r--r--src/man/sssd.conf.5.xml38
-rw-r--r--src/responder/sudo/sudosrv.c21
-rw-r--r--src/responder/sudo/sudosrv_cache.c299
-rw-r--r--src/responder/sudo/sudosrv_cmd.c46
-rw-r--r--src/responder/sudo/sudosrv_get_sudorules.c25
-rw-r--r--src/responder/sudo/sudosrv_private.h33
8 files changed, 456 insertions, 9 deletions
diff --git a/Makefile.am b/Makefile.am
index 710f33c6..409dc84d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -469,6 +469,7 @@ sssd_sudo_SOURCES = \
src/responder/sudo/sudosrv_get_sudorules.c \
src/responder/sudo/sudosrv_query.c \
src/responder/sudo/sudosrv_dp.c \
+ src/responder/sudo/sudosrv_cache.c \
$(SSSD_RESPONDER_OBJ)
sssd_sudo_LDADD = \
$(SSSD_LIBS) \
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 7b5a2c94..fc222aee 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -93,6 +93,8 @@
/* SUDO */
#define CONFDB_SUDO_CONF_ENTRY "config/sudo"
+#define CONFDB_SUDO_CACHE_TIMEOUT "sudo_cache_timeout"
+#define CONFDB_DEFAULT_SUDO_CACHE_TIMEOUT 180
/* Data Provider */
#define CONFDB_DP_CONF_ENTRY "config/dp"
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 94fc591a..e8e8b334 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -594,6 +594,44 @@
</varlistentry>
</variablelist>
</refsect2>
+
+ <refsect2 id='SUDO' condition="with_sudo">
+ <title>SUDO configuration options</title>
+ <para>
+ These options can be used to configure the sudo service.
+ </para>
+ <para>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/experimental.xml" />
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>sudo_cache_timeout (integer)</term>
+ <listitem>
+ <para>
+ For any sudo request that comes while SSSD is
+ online, the SSSD will attempt to update the cached
+ rules in order to ensure that sudo has the latest
+ ruleset.
+ </para>
+ <para>
+ The user may, however, run a couple of sudo commands
+ successively, which would trigger multiple LDAP requests.
+ In order to speed up this use-case, the sudo service
+ maintains an in-memory cache that would be used for
+ performing fast replies.
+ </para>
+ <para>
+ This option controls how long (in seconds) can the sudo
+ service cache rules for a user.
+ </para>
+ <para>
+ Default: 180
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+
</refsect1>
<refsect1 id='domain-sections'>
diff --git a/src/responder/sudo/sudosrv.c b/src/responder/sudo/sudosrv.c
index 841be43e..6b7eae07 100644
--- a/src/responder/sudo/sudosrv.c
+++ b/src/responder/sudo/sudosrv.c
@@ -129,6 +129,27 @@ int sudo_process_init(TALLOC_CTX *mem_ctx,
sudo_dp_reconnect_init, iter);
}
+ /* Get responder options */
+
+ /* Get cache_timeout option */
+ ret = confdb_get_int(sudo_ctx->rctx->cdb, sudo_ctx,
+ CONFDB_SUDO_CONF_ENTRY, CONFDB_SUDO_CACHE_TIMEOUT,
+ CONFDB_DEFAULT_SUDO_CACHE_TIMEOUT,
+ &sudo_ctx->cache_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, ("Error reading from confdb (%d) [%s]\n",
+ ret, strerror(ret)));
+ return ret;
+ }
+
+ /* Initialize in-memory cache */
+ ret = sudosrv_cache_init(sudo_ctx, 10, &sudo_ctx->cache);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ ("Could not create hash table: [%s]", strerror(ret)));
+ return ret;
+ }
+
DEBUG(SSSDBG_TRACE_FUNC, ("SUDO Initialization complete\n"));
return EOK;
diff --git a/src/responder/sudo/sudosrv_cache.c b/src/responder/sudo/sudosrv_cache.c
new file mode 100644
index 00000000..a3a13cad
--- /dev/null
+++ b/src/responder/sudo/sudosrv_cache.c
@@ -0,0 +1,299 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <dhash.h>
+#include <time.h>
+
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "db/sysdb.h"
+#include "responder/sudo/sudosrv_private.h"
+
+static void sudosrv_cache_remove(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *pvt);
+
+struct sudo_cache_entry {
+ hash_table_t *table;
+ hash_key_t *key;
+ size_t num_rules;
+ struct sysdb_attrs **rules;
+
+ struct sudo_ctx *sudo_ctx;
+};
+
+errno_t sudosrv_cache_init(TALLOC_CTX *mem_ctx,
+ unsigned long count,
+ hash_table_t **table)
+{
+ return sss_hash_create(mem_ctx, count, table);
+}
+
+static errno_t
+sudosrv_cache_reinit(struct sudo_ctx *sudo_ctx)
+{
+ errno_t ret;
+
+ talloc_free(sudo_ctx->cache);
+
+ ret = sudosrv_cache_init(sudo_ctx, 10, &sudo_ctx->cache);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ ("Could not re-initialize hash table: [%s]", strerror(ret)));
+ }
+ return ret;
+}
+
+static hash_key_t *sudosrv_cache_create_key(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *username)
+{
+ hash_key_t *key = talloc_zero(NULL, hash_key_t);
+ if (key == NULL) {
+ return NULL;
+ }
+
+ key->type = HASH_KEY_STRING;
+ if (username == NULL) {
+ key->str = talloc_strdup(key, domain->name);
+ } else {
+ key->str = talloc_asprintf(key, "%s:%s", domain->name, username);
+ }
+
+ if (key->str == NULL) {
+ talloc_free(key);
+ return NULL;
+ }
+
+ return talloc_steal(mem_ctx, key);
+}
+
+errno_t sudosrv_cache_set_entry(struct tevent_context *ev,
+ struct sudo_ctx *sudo_ctx,
+ hash_table_t *table,
+ struct sss_domain_info *domain,
+ const char *username,
+ size_t num_rules,
+ struct sysdb_attrs **rules,
+ time_t timeout)
+{
+ struct sudo_cache_entry *cache_entry = NULL;
+ hash_key_t *key = NULL;
+ hash_value_t value;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct tevent_timer *timer = NULL;
+ struct timeval tv;
+ errno_t ret;
+ int hret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ goto done;
+ }
+
+ /* create key */
+ key = sudosrv_cache_create_key(tmp_ctx, domain, username);
+ if (key == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to create hash key.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* create value */
+ cache_entry = talloc_zero(tmp_ctx, struct sudo_cache_entry);
+ if (cache_entry == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to create hash value.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+ cache_entry->table = table;
+ cache_entry->key = key;
+ cache_entry->num_rules = num_rules;
+ cache_entry->rules = rules;
+ cache_entry->sudo_ctx = sudo_ctx;
+
+ value.type = HASH_VALUE_PTR;
+ value.ptr = cache_entry;
+
+ /* insert value */
+ hret = hash_enter(table, key, &value);
+ if (hret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ ("Unable to add [%s] to SUDO cache", key->str));
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("Hash error [%d][%s]", hret, hash_error_string(hret)));
+ ret = EIO;
+ goto done;
+ }
+
+ /* Create a timer event to remove the entry from the cache */
+ tv = tevent_timeval_current_ofs(timeout, 0);
+ timer = tevent_add_timer(ev, cache_entry, tv,
+ sudosrv_cache_remove,
+ cache_entry);
+ if (timer == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* everythig is ok, steal the pointers */
+ talloc_steal(cache_entry, key);
+ talloc_steal(cache_entry, rules);
+ talloc_steal(table, cache_entry);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static void free_cache_entry_cb(struct tevent_context *ev,
+ struct tevent_immediate *imm,
+ void *pvt)
+{
+ struct sudo_cache_entry *cache_entry =
+ talloc_get_type(pvt, struct sudo_cache_entry);
+ talloc_free(cache_entry);
+}
+
+static void sudosrv_cache_remove(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *pvt)
+{
+ int hret;
+ hash_key_t *key;
+ struct sudo_cache_entry *cache_entry;
+ struct tevent_immediate *imm;
+
+ cache_entry = talloc_get_type(pvt, struct sudo_cache_entry);
+ key = cache_entry->key;
+
+ hret = hash_delete(cache_entry->table, key);
+ if (hret != HASH_SUCCESS && hret != HASH_ERROR_KEY_NOT_FOUND
+ && hret != HASH_ERROR_BAD_KEY_TYPE) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ ("Could not clear [%s] from SUDO cache.\n", key->str));
+ DEBUG(SSSDBG_TRACE_LIBS,
+ ("Hash error [%d][%s]", hret, hash_error_string(hret)));
+
+ /* corrupted memory, re-initialize table */
+ sudosrv_cache_reinit(cache_entry->sudo_ctx);
+ } else {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ ("[%s] removed from SUDO cache\n", key->str));
+
+ imm = tevent_create_immediate(ev);
+ if (imm == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Out of memory\n"));
+ return;
+ }
+ tevent_schedule_immediate(imm, ev, free_cache_entry_cb, cache_entry);
+ }
+}
+
+static errno_t sudosrv_cache_lookup_internal(hash_table_t *table,
+ struct sss_domain_info *domain,
+ const char *username,
+ size_t *num_rules,
+ struct sysdb_attrs ***rules)
+{
+ struct sudo_cache_entry *cache_entry = NULL;
+ hash_key_t *key = NULL;
+ hash_value_t value;
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ int hret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ goto done;
+ }
+
+ /* create key */
+ key = sudosrv_cache_create_key(tmp_ctx, domain, username);
+ if (key == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to create hash key.\n"));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ hret = hash_lookup(table, key, &value);
+ if (hret == HASH_SUCCESS) {
+ /* cache hit */
+ cache_entry = value.ptr;
+ *num_rules = cache_entry->num_rules;
+ *rules = cache_entry->rules;
+ ret = EOK;
+ } else if (hret == HASH_ERROR_KEY_NOT_FOUND) {
+ /* cache miss */
+ ret = ENOENT;
+ } else {
+ /* error */
+ ret = EIO;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t sudosrv_cache_lookup(hash_table_t *table,
+ struct sudo_dom_ctx *dctx,
+ bool check_next,
+ const char *username,
+ size_t *num_rules,
+ struct sysdb_attrs ***rules)
+{
+ struct sss_domain_info *domain = dctx->domain;
+ errno_t ret;
+
+ if (!check_next) {
+ return sudosrv_cache_lookup_internal(table, dctx->domain, username,
+ num_rules, rules);
+ }
+
+ while (domain != NULL) {
+ if (domain->fqnames) {
+ domain = domain->next;
+ continue;
+ }
+
+ ret = sudosrv_cache_lookup_internal(table, domain, username,
+ num_rules, rules);
+ if (ret == EOK) {
+ /* user is in this domain */
+ dctx->domain = domain;
+ return ret;
+ } else if (ret != ENOENT) {
+ /* error */
+ return ret;
+ }
+
+ /* user is not in this domain cache, check next */
+ domain = domain->next;
+ }
+
+ /* user is not in cache */
+ return ENOENT;
+}
diff --git a/src/responder/sudo/sudosrv_cmd.c b/src/responder/sudo/sudosrv_cmd.c
index 3550e8ba..cef245fe 100644
--- a/src/responder/sudo/sudosrv_cmd.c
+++ b/src/responder/sudo/sudosrv_cmd.c
@@ -151,6 +151,15 @@ static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
cmd_ctx->cli_ctx = cli_ctx;
cmd_ctx->type = SSS_DP_SUDO_USER;
+ /* get responder ctx */
+ cmd_ctx->sudo_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sudo_ctx);
+ if (!cmd_ctx->sudo_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, ("sudo_ctx not set\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ /* create domain ctx */
dctx = talloc_zero(cmd_ctx, struct sudo_dom_ctx);
if (!dctx) {
ret = ENOMEM;
@@ -201,8 +210,18 @@ static int sudosrv_cmd_get_sudorules(struct cli_ctx *cli_ctx)
cmd_ctx->check_next = true;
}
- /* ok, find it ! */
- ret = sudosrv_get_sudorules(dctx);
+ /* try to find rules in in-memory cache */
+ ret = sudosrv_cache_lookup(cmd_ctx->sudo_ctx->cache, dctx,
+ cmd_ctx->check_next, cmd_ctx->username,
+ &dctx->res_count, &dctx->res);
+ if (ret == EOK) {
+ /* cache hit */
+ DEBUG(SSSDBG_FUNC_DATA, ("Returning rules for [%s@%s] "
+ "from in-memory cache\n", cmd_ctx->username, dctx->domain->name));
+ } else if (ret == ENOENT) {
+ /* cache expired or missed */
+ ret = sudosrv_get_sudorules(dctx);
+ } /* else error */
done:
return sudosrv_cmd_done(dctx, ret);
@@ -224,6 +243,15 @@ static int sudosrv_cmd_get_defaults(struct cli_ctx *cli_ctx)
cmd_ctx->username = NULL;
cmd_ctx->check_next = false;
+ /* get responder ctx */
+ cmd_ctx->sudo_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sudo_ctx);
+ if (!cmd_ctx->sudo_ctx) {
+ DEBUG(SSSDBG_FATAL_FAILURE, ("sudo_ctx not set\n"));
+ ret = EFAULT;
+ goto done;
+ }
+
+ /* create domain ctx */
dctx = talloc_zero(cmd_ctx, struct sudo_dom_ctx);
if (!dctx) {
ret = ENOMEM;
@@ -246,8 +274,18 @@ static int sudosrv_cmd_get_defaults(struct cli_ctx *cli_ctx)
goto done;
}
- /* ok, find it ! */
- ret = sudosrv_get_rules(dctx);
+ /* try to find rules in in-memory cache */
+ ret = sudosrv_cache_lookup(cmd_ctx->sudo_ctx->cache, dctx,
+ cmd_ctx->check_next, cmd_ctx->username,
+ &dctx->res_count, &dctx->res);
+ if (ret == EOK) {
+ /* cache hit */
+ DEBUG(SSSDBG_FUNC_DATA, ("Returning defaults settings for [%s] "
+ "from in-memory cache\n", dctx->domain->name));
+ } else if (ret == ENOENT) {
+ /* cache expired or missed */
+ ret = sudosrv_get_rules(dctx);
+ } /* else error */
done:
return sudosrv_cmd_done(dctx, ret);
diff --git a/src/responder/sudo/sudosrv_get_sudorules.c b/src/responder/sudo/sudosrv_get_sudorules.c
index 8cb10e9a..c53638a3 100644
--- a/src/responder/sudo/sudosrv_get_sudorules.c
+++ b/src/responder/sudo/sudosrv_get_sudorules.c
@@ -210,7 +210,6 @@ static void sudosrv_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
("Data Provider returned, check the cache again\n"));
dctx->check_provider = false;
ret = sudosrv_get_user(dctx);
- /* FIXME - set entry into cache so that we don't perform initgroups too often */
if (ret == EAGAIN) {
goto done;
} else if (ret != EOK) {
@@ -250,8 +249,9 @@ errno_t sudosrv_get_rules(struct sudo_dom_ctx *dctx)
struct sudo_cmd_ctx *cmd_ctx = dctx->cmd_ctx;
struct dp_callback_ctx *cb_ctx = NULL;
- /* FIXME - cache logic will be here. For now, just refresh
- * the cache unconditionally */
+ DEBUG(SSSDBG_TRACE_FUNC, ("getting rules for %s\n",
+ cmd_ctx->username ? cmd_ctx->username : "default options"));
+
dpreq = sss_dp_get_sudoers_send(cmd_ctx->cli_ctx,
cmd_ctx->cli_ctx->rctx,
dctx->domain, false,
@@ -319,7 +319,6 @@ sudosrv_get_sudorules_dp_callback(uint16_t err_maj, uint32_t err_min,
"Will try to return what we have in cache\n",
(unsigned int)err_maj, (unsigned int)err_min, err_msg));
- /* FIXME - cache or next domain? */
/* Loop to the next domain if possible */
if (dctx->domain->next && dctx->cmd_ctx->check_next) {
dctx->domain = dctx->domain->next;
@@ -355,8 +354,11 @@ static errno_t sudosrv_get_sudorules_from_cache(struct sudo_dom_ctx *dctx)
errno_t ret;
struct sysdb_ctx *sysdb;
struct cli_ctx *cli_ctx = dctx->cmd_ctx->cli_ctx;
+ struct sudo_ctx *sudo_ctx = dctx->cmd_ctx->sudo_ctx;
uid_t uid;
char **groupnames;
+ const char *safe_name = dctx->cmd_ctx->username ?
+ dctx->cmd_ctx->username : "default rules";
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) return ENOMEM;
@@ -393,8 +395,21 @@ static errno_t sudosrv_get_sudorules_from_cache(struct sudo_dom_ctx *dctx)
goto done;
}
+ /* Store result in in-memory cache */
+ ret = sudosrv_cache_set_entry(sudo_ctx->rctx->ev, sudo_ctx,
+ sudo_ctx->cache, dctx->domain,
+ dctx->cmd_ctx->username, dctx->res_count,
+ dctx->res, sudo_ctx->cache_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, ("Unable to store rules in cache for "
+ "[%s@%s]\n", safe_name, dctx->domain->name));
+ } else {
+ DEBUG(SSSDBG_FUNC_DATA, ("Rules for [%s@%s] stored in in-memory cache\n",
+ safe_name, dctx->domain->name));
+ }
+
DEBUG(SSSDBG_TRACE_FUNC, ("Returning rules for [%s@%s]\n",
- dctx->cmd_ctx->username, dctx->domain->name));
+ safe_name, dctx->domain->name));
ret = EOK;
done:
diff --git a/src/responder/sudo/sudosrv_private.h b/src/responder/sudo/sudosrv_private.h
index b59aca4a..c3feb19b 100644
--- a/src/responder/sudo/sudosrv_private.h
+++ b/src/responder/sudo/sudosrv_private.h
@@ -38,10 +38,23 @@ enum sss_dp_sudo_type {
struct sudo_ctx {
struct resp_ctx *rctx;
+
+ /*
+ * options
+ */
+ int cache_timeout;
+
+ /*
+ * Key: domain for SSS_DP_SUDO_DEFAULTS
+ * domain:username for SSS_DP_SUDO_USER
+ * Val: struct sudo_cache_entry *
+ */
+ hash_table_t *cache;
};
struct sudo_cmd_ctx {
struct cli_ctx *cli_ctx;
+ struct sudo_ctx *sudo_ctx;
enum sss_dp_sudo_type type;
char *username;
bool check_next;
@@ -121,4 +134,24 @@ sss_dp_get_sudoers_recv(TALLOC_CTX *mem_ctx,
dbus_uint32_t *err_min,
char **err_msg);
+errno_t sudosrv_cache_init(TALLOC_CTX *mem_ctx,
+ unsigned long count,
+ hash_table_t **table);
+
+errno_t sudosrv_cache_lookup(hash_table_t *table,
+ struct sudo_dom_ctx *dctx,
+ bool check_next,
+ const char *username,
+ size_t *res_count,
+ struct sysdb_attrs ***res);
+
+errno_t sudosrv_cache_set_entry(struct tevent_context *ev,
+ struct sudo_ctx *sudo_ctx,
+ hash_table_t *table,
+ struct sss_domain_info *domain,
+ const char *username,
+ size_t res_count,
+ struct sysdb_attrs **res,
+ time_t timeout);
+
#endif /* _SUDOSRV_PRIVATE_H_ */