From 513b0fa11a3df3fa630685d06014c4595e7af87d Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Thu, 22 Oct 2009 11:58:06 -0400 Subject: [PATCH 4/4] 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. --- server/confdb/confdb.h | 1 + server/config/etc/sssd.api.conf | 1 + server/db/sysdb.h | 1 + server/man/sssd.conf.5.xml | 21 +++++ server/providers/data_provider.h | 1 + server/responder/pam/pamsrv.c | 3 + server/responder/pam/pamsrv.h | 1 + server/responder/pam/pamsrv_cache.c | 20 ++++- server/responder/pam/pamsrv_cmd.c | 171 ++++++++++++++++++++++++++++++++++- 9 files changed, 217 insertions(+), 3 deletions(-) diff --git a/server/confdb/confdb.h b/server/confdb/confdb.h index 6069940..fd32528 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 99e87b9..8ec6d9c 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 9afb957..b390e14 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -60,6 +60,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 4b8a92f..257a940 100644 --- a/server/man/sssd.conf.5.xml +++ b/server/man/sssd.conf.5.xml @@ -327,6 +327,27 @@ + + PAM configuration options + + These options can be used to configure the + Pluggable Authentication Module (PAM) service. + + + + offline_credentials_expiration (integer) + + + If the authentication provider is offline, how + long should we allow cached logins (in days). + + + Default: 0 (No limit) + + + + + diff --git a/server/providers/data_provider.h b/server/providers/data_provider.h index 57b318f..1e97d04 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 352c046..626d2c5 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 74115bc..d87e166 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 9c5c209..43ee14d 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 < 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 cfc973d..f05709f 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) { -- 1.6.2.5