diff options
author | Stephen Gallagher <sgallagh@redhat.com> | 2009-10-22 11:58:06 -0400 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2009-10-22 15:43:01 -0400 |
commit | c2d7b2271eafd27b41736624e4e5da121073279d (patch) | |
tree | 517d165f3f229b4783d5568fd06a1b8a80d089ad /server/responder/pam | |
parent | ff75b1a0e342f694589c46d9d59c509ac69be980 (diff) | |
download | sssd-c2d7b2271eafd27b41736624e4e5da121073279d.tar.gz sssd-c2d7b2271eafd27b41736624e4e5da121073279d.tar.bz2 sssd-c2d7b2271eafd27b41736624e4e5da121073279d.zip |
Add support for offline auth cache timeout
This adds a new option (offline_credentials_expiration) to the
[PAM] section of the sssd.conf
If the user does not perform an online authentication within the
timeout (in days), they will be denied auth once the timeout
passes.
Diffstat (limited to 'server/responder/pam')
-rw-r--r-- | server/responder/pam/pamsrv.c | 3 | ||||
-rw-r--r-- | server/responder/pam/pamsrv.h | 1 | ||||
-rw-r--r-- | server/responder/pam/pamsrv_cache.c | 20 | ||||
-rw-r--r-- | server/responder/pam/pamsrv_cmd.c | 171 |
4 files changed, 192 insertions, 3 deletions
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) { |