summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPavel Reichl <preichl@redhat.com>2015-04-16 03:41:58 -0400
committerJakub Hrozek <jhrozek@redhat.com>2015-07-06 20:19:09 +0200
commit0aa18cc0bf3447ca734476926724f1632e160807 (patch)
tree5dd2d0e24e80753849e41fa3e574d1fa1743fa08
parent32cc237aa0f3c70a4e0bc0491ec0cba0016aaf5a (diff)
downloadsssd-0aa18cc0bf3447ca734476926724f1632e160807.tar.gz
sssd-0aa18cc0bf3447ca734476926724f1632e160807.tar.xz
sssd-0aa18cc0bf3447ca734476926724f1632e160807.zip
PAM: authenticate agains cache
Enable authenticating users from cache even when SSSD is in online mode. Introduce new option `cached_auth_timeout`. Resolves: https://fedorahosted.org/sssd/ticket/1807 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-rw-r--r--src/confdb/confdb.c62
-rw-r--r--src/confdb/confdb.h2
-rw-r--r--src/config/SSSDConfig/__init__.py.in1
-rwxr-xr-xsrc/config/SSSDConfigTest.py6
-rw-r--r--src/config/etc/sssd.api.conf1
-rw-r--r--src/man/sssd.conf.5.xml24
-rw-r--r--src/responder/pam/pamsrv.h3
-rw-r--r--src/responder/pam/pamsrv_cmd.c170
8 files changed, 261 insertions, 8 deletions
diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
index 9af754912..3a8a1c01b 100644
--- a/src/confdb/confdb.c
+++ b/src/confdb/confdb.c
@@ -760,6 +760,59 @@ static uint32_t confdb_get_min_id(struct sss_domain_info *domain)
return defval;
}
+static errno_t init_cached_auth_timeout(struct confdb_ctx *cdb,
+ struct ldb_message *msg,
+ uint32_t *_cached_auth_timeout)
+{
+ int cred_expiration;
+ int id_timeout;
+ errno_t ret;
+ uint32_t cached_auth_timeout;
+
+ ret = get_entry_as_uint32(msg, &cached_auth_timeout,
+ CONFDB_DOMAIN_CACHED_AUTH_TIMEOUT, 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Invalid value for [%s]\n", CONFDB_DOMAIN_CACHED_AUTH_TIMEOUT);
+ goto done;
+ }
+
+ ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_CRED_TIMEOUT, 0, &cred_expiration);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read expiration time of offline credentials.\n");
+ goto done;
+ }
+
+ /* convert from days to seconds */
+ cred_expiration *= 3600 * 24;
+ if (cred_expiration != 0 &&
+ cred_expiration < cached_auth_timeout) {
+ cached_auth_timeout = cred_expiration;
+ }
+
+ /* Set up the PAM identity timeout */
+ ret = confdb_get_int(cdb, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_ID_TIMEOUT, 5,
+ &id_timeout);
+ if (ret != EOK) goto done;
+
+ if (cached_auth_timeout > id_timeout) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "cached_auth_timeout is greater than pam_id_timeout so be aware "
+ "that back end could be called to handle initgroups.\n");
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *_cached_auth_timeout = cached_auth_timeout;
+ }
+ return ret;
+}
+
static int confdb_get_domain_internal(struct confdb_ctx *cdb,
TALLOC_CTX *mem_ctx,
const char *name,
@@ -1277,6 +1330,15 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
goto done;
}
+ ret = init_cached_auth_timeout(cdb, res->msgs[0],
+ &domain->cached_auth_timeout);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "init_cached_auth_timeout failed: %s:[%d].\n",
+ sss_strerror(ret), ret);
+ goto done;
+ }
+
domain->has_views = false;
domain->view_name = NULL;
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 801a13fc2..b2ec2e0b9 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -188,6 +188,7 @@
#define CONFDB_DOMAIN_REFRESH_EXPIRED_INTERVAL "refresh_expired_interval"
#define CONFDB_DOMAIN_OFFLINE_TIMEOUT "offline_timeout"
#define CONFDB_DOMAIN_SUBDOMAIN_INHERIT "subdomain_inherit"
+#define CONFDB_DOMAIN_CACHED_AUTH_TIMEOUT "cached_auth_timeout"
/* Local Provider */
#define CONFDB_LOCAL_DEFAULT_SHELL "default_shell"
@@ -248,6 +249,7 @@ struct sss_domain_info {
uint32_t refresh_expired_interval;
uint32_t subdomain_refresh_interval;
+ uint32_t cached_auth_timeout;
int pwd_expiration_warning;
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index f2d9bf019..4d45e42af 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -149,6 +149,7 @@ option_strings = {
'subdomain_enumerate' : _('Control enumeration of trusted domains'),
'subdomain_refresh_interval' : _('How often should subdomains list be refreshed'),
'subdomain_inherit' : _('List of options that should be inherited into a subdomain'),
+ 'cached_auth_timeout' : _('How long can cached credentials be used for cached authentication'),
# [provider/ipa]
'ipa_domain' : _('IPA domain'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index c6ba9f051..1d6107cea 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -547,7 +547,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
'subdomains_provider',
'realmd_tags',
'subdomain_refresh_interval',
- 'subdomain_inherit']
+ 'subdomain_inherit',
+ 'cached_auth_timeout']
self.assertTrue(type(options) == dict,
"Options should be a dictionary")
@@ -910,7 +911,8 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
'subdomains_provider',
'realmd_tags',
'subdomain_refresh_interval',
- 'subdomain_inherit']
+ 'subdomain_inherit',
+ 'cached_auth_timeout']
self.assertTrue(type(options) == dict,
"Options should be a dictionary")
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 7ad84cd82..29fd896cc 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -133,6 +133,7 @@ description = str, None, false
realmd_tags = str, None, false
subdomain_refresh_interval = int, None, false
subdomain_inherit = str, None, false
+cached_auth_timeout = int, None, false
#Entry cache timeouts
entry_cache_user_timeout = int, None, false
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 75d13a631..7d3a57b0e 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -2176,6 +2176,30 @@ pam_account_expired_message = Account expired, please call help desk.
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>cached_auth_timeout (int)</term>
+ <listitem>
+ <para>
+ Specifies time in seconds since last successful
+ online authentication for which user will be
+ authenticated using cached credentials while
+ SSSD is in the online mode.
+ </para>
+ <para>
+ Special value 0 implies that this feature is
+ disabled.
+ </para>
+ <para>
+ Please note that if <quote>cached_auth_timeout</quote>
+ is longer than <quote>pam_id_timeout</quote> then the
+ back end could be called to handle
+ <quote>initgroups.</quote>
+ </para>
+ <para>
+ Default: 0
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index 066f35a42..027800646 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -60,6 +60,9 @@ struct pam_auth_req {
bool is_uid_trusted;
bool check_provider;
void *data;
+ bool use_cached_auth;
+ /* whether cached authentication was tried and failed */
+ bool cached_auth_failed;
struct pam_auth_dp_req *dpreq_spy;
};
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 3bd676395..c144406aa 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -45,6 +45,10 @@ enum pam_verbosity {
static errno_t
pam_null_last_online_auth_with_curr_token(struct sss_domain_info *domain,
const char *username);
+static errno_t
+pam_get_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+ const char *name,
+ uint64_t *_value);
static void pam_reply(struct pam_auth_req *preq);
static errno_t pack_user_info_account_expired(TALLOC_CTX *mem_ctx,
@@ -568,7 +572,7 @@ static errno_t get_password_for_cache_auth(struct sss_auth_token *authtok,
static int pam_forwarder(struct cli_ctx *cctx, int pam_cmd);
static void pam_handle_cached_login(struct pam_auth_req *preq, int ret,
- time_t expire_date, time_t delayed_until);
+ time_t expire_date, time_t delayed_until, bool cached_auth);
static void pam_reply(struct pam_auth_req *preq)
{
@@ -606,14 +610,21 @@ static void pam_reply(struct pam_auth_req *preq)
DEBUG(SSSDBG_FUNC_DATA,
"pam_reply called with result [%d]: %s.\n",
pd->pam_status, pam_strerror(NULL, pd->pam_status));
+ if (pd->pam_status == PAM_AUTHINFO_UNAVAIL || preq->use_cached_auth) {
- if (pd->pam_status == PAM_AUTHINFO_UNAVAIL) {
switch(pd->cmd) {
case SSS_PAM_AUTHENTICATE:
if ((preq->domain != NULL) &&
(preq->domain->cache_credentials == true) &&
(pd->offline_auth == false)) {
const char *password = NULL;
+ bool use_cached_auth;
+
+ /* backup value of preq->use_cached_auth*/
+ use_cached_auth = preq->use_cached_auth;
+ /* set to false to avoid entering this branch when pam_reply()
+ * is recursively called from pam_handle_cached_login() */
+ preq->use_cached_auth = false;
/* do auth with offline credentials */
pd->offline_auth = true;
@@ -637,7 +648,8 @@ static void pam_reply(struct pam_auth_req *preq)
pctx->rctx->cdb, false,
&exp_date, &delay_until);
- pam_handle_cached_login(preq, ret, exp_date, delay_until);
+ pam_handle_cached_login(preq, ret, exp_date, delay_until,
+ use_cached_auth);
return;
}
break;
@@ -805,8 +817,11 @@ done:
sss_cmd_done(cctx, preq);
}
+static void pam_dom_forwarder(struct pam_auth_req *preq);
+
static void pam_handle_cached_login(struct pam_auth_req *preq, int ret,
- time_t expire_date, time_t delayed_until)
+ time_t expire_date, time_t delayed_until,
+ bool use_cached_auth)
{
uint32_t resp_type;
size_t resp_len;
@@ -855,6 +870,18 @@ static void pam_handle_cached_login(struct pam_auth_req *preq, int ret,
}
}
break;
+ case PAM_AUTH_ERR:
+ /* Was this attempt to authenticate from cache? */
+ if (use_cached_auth) {
+ /* Don't try cached authentication again, try online check. */
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Cached authentication failed for: %s\n",
+ preq->pd->user);
+ preq->cached_auth_failed = true;
+ pam_dom_forwarder(preq);
+ return;
+ }
+ break;
default:
DEBUG(SSSDBG_TRACE_LIBS,
"cached login returned: %d\n", preq->pd->pam_status);
@@ -869,7 +896,6 @@ static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
const char *err_msg, void *ptr);
static int pam_check_user_search(struct pam_auth_req *preq);
static int pam_check_user_done(struct pam_auth_req *preq, int ret);
-static void pam_dom_forwarder(struct pam_auth_req *preq);
/* TODO: we should probably return some sort of cookie that is set in the
* PAM_ENVIRONMENT, so that we can save performing some calls and cache
@@ -1423,6 +1449,76 @@ static void pam_check_user_dp_callback(uint16_t err_maj, uint32_t err_min,
}
}
+static errno_t pam_is_last_online_login_fresh(struct sss_domain_info *domain,
+ const char* user,
+ struct confdb_ctx *cdb,
+ int cached_auth_timeout,
+ bool *_result)
+{
+ errno_t ret;
+ bool result;
+ uint64_t last_login;
+
+ ret = pam_get_last_online_auth_with_curr_token(domain, user, &last_login);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "sysdb_get_last_online_auth_with_curr_token failed: %s:[%d]\n",
+ sss_strerror(ret), ret);
+ goto done;
+ }
+
+ result = time(NULL) < (last_login + cached_auth_timeout);
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *_result = result;
+ }
+ return ret;
+}
+
+static bool pam_is_cmd_cachable(int cmd)
+{
+ bool is_cachable;
+
+ switch(cmd) {
+ case SSS_PAM_AUTHENTICATE:
+ is_cachable = true;
+ break;
+ default:
+ is_cachable = false;
+ }
+
+ return is_cachable;
+}
+
+static bool pam_can_user_cache_auth(struct confdb_ctx *cdb,
+ struct sss_domain_info *domain,
+ int pam_cmd, const char* user,
+ bool cached_auth_failed)
+{
+ errno_t ret;
+ bool result = false;
+
+ if (!cached_auth_failed /* don't try cached auth again */
+ && domain->cache_credentials
+ && domain->cached_auth_timeout > 0
+ && pam_is_cmd_cachable(pam_cmd)) {
+
+ ret = pam_is_last_online_login_fresh(domain, user, cdb,
+ domain->cached_auth_timeout,
+ &result);
+ if (ret != EOK) {
+ /* non-critical, consider fail as 'non-fresh value' */
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "pam_is_last_online_login_fresh failed: %s:[%d]\n",
+ sss_strerror(ret), ret);
+ }
+ }
+
+ return result;
+}
+
static void pam_dom_forwarder(struct pam_auth_req *preq)
{
int ret;
@@ -1454,7 +1550,17 @@ static void pam_dom_forwarder(struct pam_auth_req *preq)
return;
}
- if (!NEED_CHECK_PROVIDER(preq->domain->provider)) {
+ if (pam_can_user_cache_auth(pctx->rctx->cdb,
+ preq->domain,
+ preq->pd->cmd,
+ preq->pd->user,
+ preq->cached_auth_failed)) {
+ preq->use_cached_auth = true;
+ pam_reply(preq);
+ return;
+ }
+
+ if (!NEED_CHECK_PROVIDER(preq->domain->provider) ) {
preq->callback = pam_reply;
ret = LOCAL_pam_handler(preq);
}
@@ -1585,3 +1691,55 @@ pam_null_last_online_auth_with_curr_token(struct sss_domain_info *domain,
{
return pam_set_last_online_auth_with_curr_token(domain, username, 0);
}
+
+static errno_t
+pam_get_last_online_auth_with_curr_token(struct sss_domain_info *domain,
+ const char *name,
+ uint64_t *_value)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *attrs[] = { SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN, NULL };
+ struct ldb_message *ldb_msg;
+ uint64_t value;
+ errno_t ret;
+
+ if (name == NULL || *name == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing user name.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (domain->sysdb == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing sysdb db context.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_user_by_name(tmp_ctx, domain, name, attrs, &ldb_msg);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sysdb_search_user_by_name failed [%d][%s].\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ /* Check offline_auth_cache_timeout */
+ value = ldb_msg_find_attr_as_uint64(ldb_msg,
+ SYSDB_LAST_ONLINE_AUTH_WITH_CURR_TOKEN,
+ 0);
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ *_value = value;
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}