summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/db/sysdb.h6
-rw-r--r--server/db/sysdb_ops.c30
-rw-r--r--server/responder/pam/pamsrv_cmd.c27
-rw-r--r--server/tests/auth-tests.c51
-rw-r--r--server/tests/sysdb-tests.c12
-rw-r--r--sss_client/pam_sss.c59
-rw-r--r--sss_client/sss_cli.h3
7 files changed, 152 insertions, 36 deletions
diff --git a/server/db/sysdb.h b/server/db/sysdb.h
index a6d9e69e..cf97ed62 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 ccb58159..33cfd91f 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 a4573e60..3172a97d 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 0cb64533..71215bcd 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 97876448..8b486b69 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 91014bb6..6e238ecc 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 c6bb5bd2..95469611 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,