summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/confdb/confdb.h1
-rw-r--r--server/config/etc/sssd.api.conf1
-rw-r--r--server/db/sysdb.h1
-rw-r--r--server/man/sssd.conf.5.xml21
-rw-r--r--server/providers/data_provider.h1
-rw-r--r--server/responder/pam/pamsrv.c3
-rw-r--r--server/responder/pam/pamsrv.h1
-rw-r--r--server/responder/pam/pamsrv_cache.c20
-rw-r--r--server/responder/pam/pamsrv_cmd.c171
9 files changed, 217 insertions, 3 deletions
diff --git a/server/confdb/confdb.h b/server/confdb/confdb.h
index 4d4e8c29..e535286f 100644
--- a/server/confdb/confdb.h
+++ b/server/confdb/confdb.h
@@ -64,6 +64,7 @@
/* PAM */
#define CONFDB_PAM_CONF_ENTRY "config/pam"
+#define CONFDB_PAM_CRED_TIMEOUT "offline_credentials_expiration"
/* Data Provider */
#define CONFDB_DP_CONF_ENTRY "config/dp"
diff --git a/server/config/etc/sssd.api.conf b/server/config/etc/sssd.api.conf
index 99e87b91..8ec6d9c2 100644
--- a/server/config/etc/sssd.api.conf
+++ b/server/config/etc/sssd.api.conf
@@ -31,6 +31,7 @@ filter_users_in_groups = bool, None, true
[pam]
# Authentication service
+offline_credentials_expiration = int, None
[provider]
#Available provider types
diff --git a/server/db/sysdb.h b/server/db/sysdb.h
index 55852c55..dfb53aaf 100644
--- a/server/db/sysdb.h
+++ b/server/db/sysdb.h
@@ -61,6 +61,7 @@
#define SYSDB_KEYBOARD "keyboard"
#define SYSDB_SESSION "session"
#define SYSDB_LAST_LOGIN "lastLogin"
+#define SYSDB_LAST_ONLINE_AUTH "lastOnlineAuth"
#define SYSDB_USERPIC "userPicture"
#define SYSDB_LAST_UPDATE "lastUpdate"
diff --git a/server/man/sssd.conf.5.xml b/server/man/sssd.conf.5.xml
index 9baed088..f735b076 100644
--- a/server/man/sssd.conf.5.xml
+++ b/server/man/sssd.conf.5.xml
@@ -327,6 +327,27 @@
</varlistentry>
</variablelist>
</refsect2>
+ <refsect2 id='PAM'>
+ <title>PAM configuration options</title>
+ <para>
+ These options can be used to configure the
+ Pluggable Authentication Module (PAM) service.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>offline_credentials_expiration (integer)</term>
+ <listitem>
+ <para>
+ If the authentication provider is offline, how
+ long should we allow cached logins (in days).
+ </para>
+ <para>
+ Default: 0 (No limit)
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
</refsect1>
<refsect1 id='domain-sections'>
diff --git a/server/providers/data_provider.h b/server/providers/data_provider.h
index 57b318fd..1e97d04c 100644
--- a/server/providers/data_provider.h
+++ b/server/providers/data_provider.h
@@ -109,6 +109,7 @@ struct pam_data {
struct response_data *resp_list;
bool offline_auth;
+ bool last_auth_saved;
int priv;
uid_t pw_uid;
gid_t gr_gid;
diff --git a/server/responder/pam/pamsrv.c b/server/responder/pam/pamsrv.c
index 352c0469..626d2c55 100644
--- a/server/responder/pam/pamsrv.c
+++ b/server/responder/pam/pamsrv.c
@@ -116,6 +116,9 @@ static errno_t pam_get_config(struct pam_ctx *pctx,
struct confdb_ctx *cdb)
{
int ret = EOK;
+ ret = confdb_get_int(cdb, pctx, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_CRED_TIMEOUT, 0,
+ &pctx->cred_expiration);
return ret;
}
diff --git a/server/responder/pam/pamsrv.h b/server/responder/pam/pamsrv.h
index 74115bcc..d87e166f 100644
--- a/server/responder/pam/pamsrv.h
+++ b/server/responder/pam/pamsrv.h
@@ -32,6 +32,7 @@ struct pam_auth_req;
typedef void (pam_dp_callback_t)(struct pam_auth_req *preq);
struct pam_ctx {
+ int cred_expiration;
struct resp_ctx *rctx;
};
diff --git a/server/responder/pam/pamsrv_cache.c b/server/responder/pam/pamsrv_cache.c
index 9c5c209f..1e1c5444 100644
--- a/server/responder/pam/pamsrv_cache.c
+++ b/server/responder/pam/pamsrv_cache.c
@@ -61,17 +61,21 @@ static void pam_cache_auth_callback(void *pvt, int ldb_status,
struct ldb_result *res)
{
struct pam_auth_req *preq;
+ struct pam_ctx *pctx;
struct pam_data *pd;
const char *userhash;
char *comphash;
char *password = NULL;
int i, ret;
+ uint64_t lastLogin = 0;
preq = talloc_get_type(pvt, struct pam_auth_req);
pd = preq->pd;
+ pctx = talloc_get_type(preq->cctx->rctx->pvt_ctx, struct pam_ctx);
+
if (ldb_status != LDB_SUCCESS) {
- DEBUG(4, ("User info retireval failed! (%d [%s])\n",
+ DEBUG(4, ("User info retrieval failed! (%d [%s])\n",
ldb_status, sysdb_error_to_errno(ldb_status)));
ret = PAM_SYSTEM_ERR;
@@ -86,12 +90,23 @@ static void pam_cache_auth_callback(void *pvt, int ldb_status,
}
if (res->count != 1) {
- DEBUG(4, ("Too manyt results for user [%s@%s].\n",
+ DEBUG(4, ("Too many results for user [%s@%s].\n",
pd->user, preq->domain->name));
ret = PAM_SYSTEM_ERR;
goto done;
}
+ /* Check offline_auth_cache_timeout */
+ lastLogin = ldb_msg_find_attr_as_uint64(res->msgs[0],
+ SYSDB_LAST_ONLINE_AUTH,
+ 0);
+ if (pctx->cred_expiration &&
+ lastLogin + (pctx->cred_expiration * 86400) < time(NULL)) {
+ DEBUG(4, ("Cached user entry is too old."));
+ ret = PAM_AUTHINFO_UNAVAIL;
+ goto done;
+ }
+
/* TODO: verify user account (failed logins, disabled, expired ...) */
ret = authtok2str(preq, pd->authtok, pd->authtok_size, &password);
@@ -139,6 +154,7 @@ int pam_cache_auth(struct pam_auth_req *preq)
SYSDB_CACHEDPWD,
SYSDB_DISABLED,
SYSDB_LAST_LOGIN,
+ SYSDB_LAST_ONLINE_AUTH,
"lastCachedPasswordChange",
"accountExpires",
"failedLoginAttempts",
diff --git a/server/responder/pam/pamsrv_cmd.c b/server/responder/pam/pamsrv_cmd.c
index cfc973d9..f05709ff 100644
--- a/server/responder/pam/pamsrv_cmd.c
+++ b/server/responder/pam/pamsrv_cmd.c
@@ -30,6 +30,20 @@
#include "responder/pam/pamsrv.h"
#include "db/sysdb.h"
+struct last_login_request {
+ struct tevent_context *ev;
+ struct sysdb_ctx *dbctx;
+ struct sysdb_attrs *mod_attrs;
+ struct sysdb_handle *handle;
+
+ struct ldb_result *res;
+ int error;
+
+ struct pam_auth_req *preq;
+};
+
+static void pam_reply(struct pam_auth_req *preq);
+
static int extract_authtok(uint32_t *type, uint32_t *size, uint8_t **tok, uint8_t *body, size_t blen, size_t *c) {
size_t data_size;
@@ -266,7 +280,146 @@ static int pam_parse_in_data(struct sss_names_ctx *snctx,
return EOK;
}
-static void pam_reply(struct pam_auth_req *preq);
+static void prepare_reply(struct last_login_request *llreq)
+{
+ struct pam_data *pd;
+
+ pd = llreq->preq->pd;
+
+ if (llreq->error != EOK && pd->pam_status == PAM_SUCCESS)
+ pd->pam_status = PAM_SYSTEM_ERR;
+
+ llreq->preq->callback(llreq->preq);
+}
+
+static void set_user_last_login_done(struct tevent_req *req)
+{
+ struct last_login_request *llreq =
+ tevent_req_callback_data(req,
+ struct last_login_request);
+ int ret;
+
+ ret = sysdb_transaction_commit_recv(req);
+ if(ret != EOK) {
+ DEBUG(2, ("set_last_login failed.\n"));
+ llreq->error = ret;
+ }
+
+ llreq->preq->pd->last_auth_saved = true;
+
+ prepare_reply(llreq);
+}
+
+static void set_user_last_login_req_done(struct tevent_req *subreq);
+static void set_user_last_login_req(struct tevent_req *req)
+{
+ struct last_login_request *llreq =
+ tevent_req_callback_data(req,
+ struct last_login_request);
+ struct tevent_req *subreq;
+ int ret;
+
+ ret = sysdb_transaction_recv(req, llreq, &llreq->handle);
+ if (ret != EOK) {
+ llreq->error = ret;
+ return prepare_reply(llreq);
+ }
+
+ subreq = sysdb_set_user_attr_send(llreq, llreq->ev, llreq->handle,
+ llreq->preq->domain,
+ llreq->preq->pd->user,
+ llreq->mod_attrs, SYSDB_MOD_REP);
+ if (!subreq) {
+ /* Cancel transaction */
+ talloc_zfree(llreq->handle);
+ llreq->error = EIO;
+ return prepare_reply(llreq);
+ }
+ tevent_req_set_callback(subreq, set_user_last_login_req_done, llreq);
+}
+
+static void set_user_last_login_req_done(struct tevent_req *subreq)
+{
+ struct last_login_request *llreq =
+ tevent_req_callback_data(subreq,
+ struct last_login_request);
+ struct tevent_req *req;
+ int ret;
+
+ ret = sysdb_set_user_attr_recv(subreq);
+ talloc_zfree(subreq);
+
+ DEBUG(4, ("set_user_attr_callback, status [%d][%s]\n", ret, strerror(ret)));
+
+ if (ret) {
+ llreq->error = ret;
+ goto fail;
+ }
+
+ req = sysdb_transaction_commit_send(llreq, llreq->ev, llreq->handle);
+ if (!req) {
+ llreq->error = ENOMEM;
+ goto fail;
+ }
+ tevent_req_set_callback(req, set_user_last_login_done, llreq);
+
+fail:
+ DEBUG(2, ("set_last_login failed.\n"));
+
+ /* cancel transaction */
+ talloc_zfree(llreq->handle);
+
+ prepare_reply(llreq);
+}
+
+static errno_t set_last_login(struct pam_auth_req *preq)
+{
+ struct last_login_request *llreq;
+ struct tevent_req *req;
+ errno_t ret;
+
+ llreq = talloc_zero(preq, struct last_login_request);
+ if (!llreq) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ llreq->ev = preq->cctx->ev;
+ llreq->preq = preq;
+
+ llreq->mod_attrs = sysdb_new_attrs(llreq);
+ if (!llreq->mod_attrs) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = sysdb_attrs_add_long(llreq->mod_attrs, SYSDB_LAST_ONLINE_AUTH,
+ (long)time(NULL));
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ ret = sysdb_get_ctx_from_list(preq->cctx->rctx->db_list,
+ preq->domain, &llreq->dbctx);
+ if (ret != EOK) {
+ DEBUG(0, ("Fatal: Sysdb CTX not found for this domain!\n"));
+ goto fail;
+ }
+
+ req = sysdb_transaction_send(llreq, llreq->ev, llreq->dbctx);
+ if (!req) {
+ DEBUG(1, ("Unable to acquire sysdb transaction lock\n"));
+ ret = EIO;
+ goto fail;
+ }
+
+ tevent_req_set_callback(req, set_user_last_login_req, llreq);
+ return EOK;
+
+fail:
+ talloc_free(llreq);
+ return ret;
+}
+
static void pam_reply_delay(struct tevent_context *ev, struct tevent_timer *te,
struct timeval tv, void *pvt)
{
@@ -352,6 +505,22 @@ static void pam_reply(struct pam_auth_req *preq)
return;
}
+ /* If this was a successful login, save the lastLogin time */
+ if (preq->domain->cache_credentials &&
+ pd->cmd == SSS_PAM_AUTHENTICATE &&
+ pd->pam_status == PAM_SUCCESS &&
+ !pd->offline_auth &&
+ !pd->last_auth_saved &&
+ NEED_CHECK_PROVIDER(preq->domain->provider)) {
+ ret = set_last_login(preq);
+ if (ret != EOK) {
+ err = ret;
+ goto done;
+ }
+
+ return;
+ }
+
ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in),
&cctx->creq->out);
if (ret != EOK) {