Hi,

if the option offline_failed_login_delay is set the authentication is
denied for the specified number of minutes if the user the given a wrong
password for offline_failed_login_attempts times. This patch sends a
message to the user telling him when a new authentication will be
accepted.

bye,
Sumit
From 1beeb7cb1ef07e9f30249084ee344d525f776df2 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
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

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://fedorahosted.org/mailman/listinfo/sssd-devel

Reply via email to