From 1beeb7cb1ef07e9f30249084ee344d525f776df2 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Mon, 8 Feb 2010 09:25:53 +0100 Subject: [PATCH] Send a message to the user if the login is delayed --- server/db/sysdb.h | 6 ++- server/db/sysdb_ops.c | 30 ++++++++++++++----- server/responder/pam/pamsrv_cmd.c | 27 +++++++++++++++-- server/tests/auth-tests.c | 51 +++++++++++++++++++------------ server/tests/sysdb-tests.c | 12 ++++++- sss_client/pam_sss.c | 59 +++++++++++++++++++++++++++++++++++++ sss_client/sss_cli.h | 3 +- 7 files changed, 152 insertions(+), 36 deletions(-) diff --git a/server/db/sysdb.h b/server/db/sysdb.h index a6d9e69..cf97ed6 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -548,7 +548,8 @@ int sysdb_cache_password_recv(struct tevent_req *req); errno_t check_failed_login_attempts(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, struct ldb_message *ldb_msg, - uint32_t *failed_login_attempts); + uint32_t *failed_login_attempts, + time_t *delayed_until); struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, @@ -557,7 +558,8 @@ 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, time_t *expire_date); +int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date, + time_t *delayed_until); 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 ccb5815..33cfd91 100644 --- a/server/db/sysdb_ops.c +++ b/server/db/sysdb_ops.c @@ -4649,17 +4649,21 @@ struct sysdb_cache_auth_state { bool authentication_successful; struct sysdb_handle *handle; time_t expire_date; + time_t delayed_until; }; errno_t check_failed_login_attempts(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, struct ldb_message *ldb_msg, - uint32_t *failed_login_attempts) + uint32_t *failed_login_attempts, + time_t *delayed_until) { int ret; int allowed_failed_login_attempts; int failed_login_delay; time_t last_failed_login; + time_t end; + *delayed_until = -1; *failed_login_attempts = ldb_msg_find_attr_as_uint(ldb_msg, SYSDB_FAILED_LOGIN_ATTEMPTS, 0); last_failed_login = (time_t) ldb_msg_find_attr_as_int64(ldb_msg, @@ -4687,11 +4691,17 @@ errno_t check_failed_login_attempts(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb, if (allowed_failed_login_attempts) { if (*failed_login_attempts >= allowed_failed_login_attempts) { - if (failed_login_delay && - last_failed_login + (failed_login_delay * 60) < time(NULL)) { - DEBUG(7, ("failed_login_delay has passed, " - "resetting failed_login_attempts.\n")); - *failed_login_attempts = 0; + if (failed_login_delay) { + end = last_failed_login + (failed_login_delay * 60); + if (end < time(NULL)) { + DEBUG(7, ("failed_login_delay has passed, " + "resetting failed_login_attempts.\n")); + *failed_login_attempts = 0; + } else { + DEBUG(7, ("login delayed until %lld.\n", (long long) end)); + *delayed_until = end; + return EACCES; + } } else { DEBUG(4, ("Too many failed logins.\n")); return EACCES; @@ -4768,6 +4778,7 @@ struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx, state->authentication_successful = false; state->handle = NULL; state->expire_date = -1; + state->delayed_until = -1; subreq = sysdb_search_user_by_name_send(state, ev, sysdb, NULL, domain, name, attrs); @@ -4836,7 +4847,8 @@ static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq) } ret = check_failed_login_attempts(state, state->cdb, ldb_msg, - &failed_login_attempts); + &failed_login_attempts, + &state->delayed_until); if (ret != EOK) { goto done; } @@ -5034,10 +5046,12 @@ static void sysdb_cache_auth_done(struct tevent_req *subreq) return; } -int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date) { +int sysdb_cache_auth_recv(struct tevent_req *req, time_t *expire_date, + time_t *delayed_until) { struct sysdb_cache_auth_state *state = tevent_req_data(req, struct sysdb_cache_auth_state); *expire_date = state->expire_date; + *delayed_until = state->delayed_until; TEVENT_REQ_RETURN_ON_ERROR(req); diff --git a/server/responder/pam/pamsrv_cmd.c b/server/responder/pam/pamsrv_cmd.c index a4573e6..3172a97 100644 --- a/server/responder/pam/pamsrv_cmd.c +++ b/server/responder/pam/pamsrv_cmd.c @@ -626,18 +626,22 @@ 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); + uint32_t resp_type; + size_t resp_len; uint8_t *resp; time_t expire_date = 0; + time_t delayed_until = -1; long long dummy; - ret = sysdb_cache_auth_recv(req, &expire_date); + ret = sysdb_cache_auth_recv(req, &expire_date, &delayed_until); talloc_zfree(req); switch (ret) { case EOK: preq->pd->pam_status = PAM_SUCCESS; + + resp_type = SSS_PAM_USER_INFO_OFFLINE_AUTH; + resp_len = sizeof(uint32_t) + sizeof(long long); resp = talloc_size(preq->pd, resp_len); if (resp == NULL) { DEBUG(1, ("talloc_size failed, cannot prepare user info.\n")); @@ -660,6 +664,23 @@ static void pam_cache_auth_done(struct tevent_req *req) break; case EACCES: preq->pd->pam_status = PAM_PERM_DENIED; + if (delayed_until >= 0) { + resp_type = SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED; + resp_len = sizeof(uint32_t) + sizeof(long long); + 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) delayed_until; + 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; default: preq->pd->pam_status = PAM_SYSTEM_ERR; diff --git a/server/tests/auth-tests.c b/server/tests/auth-tests.c index 0cb6453..71215bc 100644 --- a/server/tests/auth-tests.c +++ b/server/tests/auth-tests.c @@ -157,7 +157,8 @@ static void do_failed_login_test(uint32_t failed_login_attempts, int offline_failed_login_attempts, int offline_failed_login_delay, int expected_result, - int expected_counter) + int expected_counter, + time_t expected_delay) { struct sysdb_test_ctx *test_ctx; int ret; @@ -165,6 +166,7 @@ static void do_failed_login_test(uint32_t failed_login_attempts, val[1] = NULL; struct ldb_message *ldb_msg; uint32_t returned_failed_login_attempts; + time_t delayed_until; /* Setup */ ret = setup_sysdb_tests(&test_ctx); @@ -193,48 +195,57 @@ static void do_failed_login_test(uint32_t failed_login_attempts, fail_unless(ret == EOK, "ldb_msg_add_string failed"); ret = check_failed_login_attempts(test_ctx, test_ctx->confdb, ldb_msg, - &returned_failed_login_attempts); + &returned_failed_login_attempts, + &delayed_until); fail_unless(ret == expected_result, "check_failed_login_attempts returned wrong error code, " - "excected [%d], got [%d]", expected_result, ret); + "expected [%d], got [%d]", expected_result, ret); + fail_unless(returned_failed_login_attempts == expected_counter, "check_failed_login_attempts returned wrong number of failed " - "login attempts, excected [%d], got [%d]", + "login attempts, expected [%d], got [%d]", expected_counter, failed_login_attempts); + fail_unless(delayed_until == expected_delay, + "check_failed_login_attempts wrong delay, " + "expected [%d], got [%d]", + expected_delay, delayed_until); + talloc_free(test_ctx); } START_TEST(test_failed_login_attempts) { + time_t now; /* if offline_failed_login_attempts == 0 a login is never denied */ - do_failed_login_test(0, 0, 0, 5, EOK, 0); - do_failed_login_test(0, time(NULL), 0, 5, EOK, 0); - do_failed_login_test(2, 0, 0, 5, EOK, 2); - do_failed_login_test(2, time(NULL), 0, 5, EOK, 2); + do_failed_login_test(0, 0, 0, 5, EOK, 0, -1); + do_failed_login_test(0, time(NULL), 0, 5, EOK, 0, -1); + do_failed_login_test(2, 0, 0, 5, EOK, 2, -1); + do_failed_login_test(2, time(NULL), 0, 5, EOK, 2, -1); - do_failed_login_test(0, 0, 0, 0, EOK, 0); - do_failed_login_test(0, time(NULL), 0, 0, EOK, 0); - do_failed_login_test(2, 0, 0, 0, EOK, 2); - do_failed_login_test(2, time(NULL), 0, 0, EOK, 2); + do_failed_login_test(0, 0, 0, 0, EOK, 0, -1); + do_failed_login_test(0, time(NULL), 0, 0, EOK, 0, -1); + do_failed_login_test(2, 0, 0, 0, EOK, 2, -1); + do_failed_login_test(2, time(NULL), 0, 0, EOK, 2, -1); /* if offline_failed_login_attempts != 0 and * offline_failed_login_delay == 0 a login is denied if the number of * failed attempts >= offline_failed_login_attempts */ - do_failed_login_test(0, 0, 2, 0, EOK, 0); - do_failed_login_test(0, time(NULL), 2, 0, EOK, 0); - do_failed_login_test(2, 0, 2, 0, EACCES, 2); - do_failed_login_test(2, time(NULL), 2, 0, EACCES, 2); + do_failed_login_test(0, 0, 2, 0, EOK, 0, -1); + do_failed_login_test(0, time(NULL), 2, 0, EOK, 0, -1); + do_failed_login_test(2, 0, 2, 0, EACCES, 2, -1); + do_failed_login_test(2, time(NULL), 2, 0, EACCES, 2, -1); /* if offline_failed_login_attempts != 0 and * offline_failed_login_delay != 0 a login is denied only if the number of * failed attempts >= offline_failed_login_attempts AND the last failed * login attempt is not longer than offline_failed_login_delay ago */ - do_failed_login_test(0, 0, 2, 5, EOK, 0); - do_failed_login_test(0, time(NULL), 2, 5, EOK, 0); - do_failed_login_test(2, 0, 2, 5, EOK, 0); - do_failed_login_test(2, time(NULL), 2, 5, EACCES, 2); + do_failed_login_test(0, 0, 2, 5, EOK, 0, -1); + do_failed_login_test(0, time(NULL), 2, 5, EOK, 0, -1); + do_failed_login_test(2, 0, 2, 5, EOK, 0, -1); + now = time(NULL); + do_failed_login_test(2, now, 2, 5, EACCES, 2, (now + 5 * 60)); } END_TEST diff --git a/server/tests/sysdb-tests.c b/server/tests/sysdb-tests.c index 9787644..8b486b6 100644 --- a/server/tests/sysdb-tests.c +++ b/server/tests/sysdb-tests.c @@ -2287,6 +2287,7 @@ static void cached_authentication_without_expiration(const char *username, struct tevent_req *req; int ret; time_t expire_date; + time_t delayed_until; const char *val[2]; val[1] = NULL; @@ -2319,7 +2320,7 @@ static void cached_authentication_without_expiration(const char *username, ret = test_loop(data); fail_unless(ret == EOK, "test_loop failed."); - ret = sysdb_cache_auth_recv(req, &expire_date); + ret = sysdb_cache_auth_recv(req, &expire_date, &delayed_until); fail_unless(ret == expected_result, "sysdb_cache_auth request does not " "return expected result [%d].", expected_result); @@ -2327,6 +2328,9 @@ static void cached_authentication_without_expiration(const char *username, fail_unless(expire_date == 0, "Wrong expire date, expected [%d], got [%d]", 0, expire_date); + fail_unless(delayed_until == -1, "Wrong delay, expected [%d], got [%d]", + -1, delayed_until); + talloc_free(test_ctx); } @@ -2343,6 +2347,7 @@ static void cached_authentication_with_expiration(const char *username, val[1] = NULL; time_t now; time_t expected_expire_date; + time_t delayed_until; /* Setup */ ret = setup_sysdb_tests(&test_ctx); @@ -2390,7 +2395,7 @@ static void cached_authentication_with_expiration(const char *username, ret = test_loop(data); fail_unless(ret == EOK, "test_loop failed."); - ret = sysdb_cache_auth_recv(req, &expire_date); + ret = sysdb_cache_auth_recv(req, &expire_date, &delayed_until); fail_unless(ret == expected_result, "sysdb_cache_auth request does not " "return expected result [%d], got [%d].", expected_result, ret); @@ -2399,6 +2404,9 @@ static void cached_authentication_with_expiration(const char *username, "Wrong expire date, expected [%d], got [%d]", expected_expire_date, expire_date); + fail_unless(delayed_until == -1, "Wrong delay, expected [%d], got [%d]", + -1, delayed_until); + talloc_free(test_ctx); } diff --git a/sss_client/pam_sss.c b/sss_client/pam_sss.c index 91014bb..6e238ec 100644 --- a/sss_client/pam_sss.c +++ b/sss_client/pam_sss.c @@ -290,6 +290,12 @@ static int do_pam_conversation(pam_handle_t *pamh, const int msg_style, msg_style == PAM_PROMPT_ECHO_ON) && (msg == NULL || answer == NULL)) return PAM_SYSTEM_ERR; + if (msg_style == PAM_TEXT_INFO || msg_style == PAM_ERROR_MSG) { + logger(pamh, LOG_INFO, "User %s message: %s", + msg_style == PAM_TEXT_INFO ? "info" : "error", + msg); + } + ret=pam_get_item(pamh, PAM_CONV, (const void **) &conv); if (ret != PAM_SUCCESS) return ret; @@ -419,6 +425,56 @@ static int user_info_offline_auth(pam_handle_t *pamh, size_t buflen, return PAM_SUCCESS; } +static int user_info_offline_auth_delayed(pam_handle_t *pamh, size_t buflen, + uint8_t *buf) +{ + int ret; + long long delayed_until; + struct tm tm; + char delay_str[128]; + char user_msg[256]; + + delay_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(&delayed_until, buf + sizeof(uint32_t), sizeof(long long)); + + if (delayed_until <= 0) { + D(("User info response data has an invalid value")); + return PAM_BUF_ERR; + } + + if (localtime_r((time_t *) &delayed_until, &tm) != NULL) { + ret = strftime(delay_str, sizeof(delay_str), "%c", &tm); + if (ret == 0) { + D(("strftime failed.")); + delay_str[0] = '\0'; + } + } else { + D(("localtime_r failed")); + } + + ret = snprintf(user_msg, sizeof(user_msg), "%s%s.", + _("Offline authentication, authentication is denied until: "), + delay_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) { @@ -436,6 +492,9 @@ static int eval_user_info_response(pam_handle_t *pamh, size_t buflen, case SSS_PAM_USER_INFO_OFFLINE_AUTH: ret = user_info_offline_auth(pamh, buflen, buf); break; + case SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED: + ret = user_info_offline_auth_delayed(pamh, buflen, buf); + break; default: D(("Unknown user info type [%d]", type)); ret = PAM_SYSTEM_ERR; diff --git a/sss_client/sss_cli.h b/sss_client/sss_cli.h index c6bb5bd..9546961 100644 --- a/sss_client/sss_cli.h +++ b/sss_client/sss_cli.h @@ -178,7 +178,8 @@ enum response_type { }; enum user_info_type { - SSS_PAM_USER_INFO_OFFLINE_AUTH = 0x01 + SSS_PAM_USER_INFO_OFFLINE_AUTH = 0x01, + SSS_PAM_USER_INFO_OFFLINE_AUTH_DELAYED }; enum nss_status sss_nss_make_request(enum sss_cli_command cmd, -- 1.6.6