From 838ab215caf4674222ce5ef4497137fb9aee4650 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Thu, 28 Jan 2010 17:19:03 +0100 Subject: [PATCH] Warn the user if authentication happens offline --- server/db/sysdb.h | 2 +- server/db/sysdb_ops.c | 17 +++++-- server/responder/pam/pamsrv_cmd.c | 20 +++++++++- server/tests/sysdb-tests.c | 3 +- sss_client/pam_sss.c | 82 +++++++++++++++++++++++++++++++++++++ sss_client/sss_cli.h | 5 ++ 6 files changed, 121 insertions(+), 8 deletions(-) diff --git a/server/db/sysdb.h b/server/db/sysdb.h index 9b77edf..a6d9e69 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -557,7 +557,7 @@ struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx, const uint8_t *authtok, size_t authtok_size, struct confdb_ctx *cdb); -int sysdb_cache_auth_recv(struct tevent_req *req); +int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date); struct tevent_req *sysdb_store_custom_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, diff --git a/server/db/sysdb_ops.c b/server/db/sysdb_ops.c index 9a8c32e..33a3fb5 100644 --- a/server/db/sysdb_ops.c +++ b/server/db/sysdb_ops.c @@ -4648,6 +4648,7 @@ struct sysdb_cache_auth_state { struct sysdb_attrs *update_attrs; bool authentication_successful; struct sysdb_handle *handle; + time_t expire_date; }; errno_t check_failed_login_attempts(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, @@ -4766,6 +4767,7 @@ struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx, state->update_attrs = NULL; state->authentication_successful = false; state->handle = NULL; + state->expire_date = 0; subreq = sysdb_search_user_by_name_send(state, ev, sysdb, NULL, domain, name, attrs); @@ -4821,10 +4823,14 @@ static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq) DEBUG(9, ("Offline credentials expiration is [%d] days.\n", cred_expiration)); - if (cred_expiration && lastLogin + (cred_expiration * 86400) < time(NULL)) { - DEBUG(4, ("Cached user entry is too old.\n")); - ret = EACCES; - goto done; + if (cred_expiration) { + state->expire_date = lastLogin + (cred_expiration * 86400); + if (state->expire_date < time(NULL)) { + DEBUG(4, ("Cached user entry is too old.\n")); + state->expire_date = 0; + ret = EACCES; + goto done; + } } ret = check_failed_login_attempts(state, state->cdb, ldb_msg, @@ -5024,10 +5030,11 @@ static void sysdb_cache_auth_done(struct tevent_req *subreq) return; } -int sysdb_cache_auth_recv(struct tevent_req *req) { +int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date) { struct sysdb_cache_auth_state *state = tevent_req_data(req, struct sysdb_cache_auth_state); TEVENT_REQ_RETURN_ON_ERROR(req); + *expire_date = state->expire_date; return (state->authentication_successful ? EOK : EINVAL); } diff --git a/server/responder/pam/pamsrv_cmd.c b/server/responder/pam/pamsrv_cmd.c index f89e73c..a4573e6 100644 --- a/server/responder/pam/pamsrv_cmd.c +++ b/server/responder/pam/pamsrv_cmd.c @@ -626,13 +626,31 @@ static void pam_cache_auth_done(struct tevent_req *req) int ret; struct pam_auth_req *preq = tevent_req_callback_data(req, struct pam_auth_req); + const uint32_t resp_type = SSS_PAM_USER_INFO_OFFLINE_AUTH; + const size_t resp_len = sizeof(uint32_t) + sizeof(long long); + uint8_t *resp; + time_t expire_date = 0; + long long dummy; - ret = sysdb_cache_auth_recv(req); + ret = sysdb_cache_auth_recv(req, &expire_date); talloc_zfree(req); switch (ret) { case EOK: preq->pd->pam_status = PAM_SUCCESS; + resp = talloc_size(preq->pd, resp_len); + if (resp == NULL) { + DEBUG(1, ("talloc_size failed, cannot prepare user info.\n")); + } else { + memcpy(resp, &resp_type, sizeof(uint32_t)); + dummy = (long long) expire_date; + memcpy(resp+sizeof(uint32_t), &dummy, sizeof(long long)); + ret = pam_add_response(preq->pd, SSS_PAM_USER_INFO, resp_len, + (const uint8_t *) resp); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + } + } break; case ENOENT: preq->pd->pam_status = PAM_AUTHINFO_UNAVAIL; diff --git a/server/tests/sysdb-tests.c b/server/tests/sysdb-tests.c index 3cd5e7d..a456211 100644 --- a/server/tests/sysdb-tests.c +++ b/server/tests/sysdb-tests.c @@ -2285,6 +2285,7 @@ static void cached_authentication(const char *username, const char *password, struct test_data *data; struct tevent_req *req; int ret; + time_t expire_date; /* Setup */ ret = setup_sysdb_tests(&test_ctx); @@ -2306,7 +2307,7 @@ static void cached_authentication(const char *username, const char *password, ret = test_loop(data); fail_unless(ret == EOK, "test_loop failed."); - ret = sysdb_cache_auth_recv(req); + ret = sysdb_cache_auth_recv(req, &expire_date); fail_unless(ret == expected_result, "sysdb_cache_auth request does not " "return expected result [%d].", expected_result); diff --git a/sss_client/pam_sss.c b/sss_client/pam_sss.c index 2b11e26..df3dd12 100644 --- a/sss_client/pam_sss.c +++ b/sss_client/pam_sss.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -369,6 +370,79 @@ static int do_pam_conversation(pam_handle_t *pamh, const int msg_style, return PAM_SUCCESS; } +static int user_info_offline_auth(pam_handle_t *pamh, size_t buflen, + uint8_t *buf) +{ + int ret; + long long expire_date; + struct tm tm; + char expire_str[128]; + char user_msg[256]; + + expire_str[0] = '\0'; + + if (buflen != sizeof(uint32_t) + sizeof(long long)) { + D(("User info response data has the wrong size")); + return PAM_BUF_ERR; + } + + memcpy(&expire_date, buf + sizeof(uint32_t), sizeof(long long)); + + if (expire_date > 0) { + if (localtime_r((time_t *) &expire_date, &tm) != NULL) { + ret = strftime(expire_str, sizeof(expire_str), "%c", &tm); + if (ret == 0) { + D(("strftime failed.")); + expire_str[0] = '\0'; + } + } else { + D(("localtime_r failed")); + } + } + + ret = snprintf(user_msg, sizeof(user_msg), "%s%s%s.", + _("Offline authentication"), + expire_str[0] ? _(", your cached password will expire at: ") : "", + expire_str[0] ? expire_str : ""); + if (ret < 0 || ret >= sizeof(user_msg)) { + D(("snprintf failed.")); + return PAM_SYSTEM_ERR; + } + + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, user_msg, NULL, NULL); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return PAM_SYSTEM_ERR; + } + + return PAM_SUCCESS; +} + +static int eval_user_info_response(pam_handle_t *pamh, size_t buflen, + uint8_t *buf) +{ + int ret; + uint32_t type; + + if (buflen < sizeof(uint32_t)) { + D(("User info response data is too short")); + return PAM_BUF_ERR; + } + + memcpy(&type, buf, sizeof(uint32_t)); + + switch(type) { + case SSS_PAM_USER_INFO_OFFLINE_AUTH: + ret = user_info_offline_auth(pamh, buflen, buf); + break; + default: + D(("Unknown user info type [%d]", type)); + ret = PAM_SYSTEM_ERR; + } + + return ret; +} + static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf) { int ret; @@ -449,6 +523,14 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf) } } break; + case SSS_PAM_USER_INFO: + ret = eval_user_info_response(pamh, len, &buf[p]); + if (ret != PAM_SUCCESS) { + D(("eval_user_info_response failed")); + } + break; + default: + D(("Unknown response type [%d]", type)); } p += len; diff --git a/sss_client/sss_cli.h b/sss_client/sss_cli.h index 7d25711..c6bb5bd 100644 --- a/sss_client/sss_cli.h +++ b/sss_client/sss_cli.h @@ -174,6 +174,11 @@ enum response_type { SSS_PAM_ENV_ITEM, /* only pam environment */ SSS_ENV_ITEM, /* only user environment */ SSS_ALL_ENV_ITEM, /* pam and user environment */ + SSS_PAM_USER_INFO +}; + +enum user_info_type { + SSS_PAM_USER_INFO_OFFLINE_AUTH = 0x01 }; enum nss_status sss_nss_make_request(enum sss_cli_command cmd, -- 1.6.6