summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/db/sysdb.h2
-rw-r--r--server/db/sysdb_ops.c20
-rw-r--r--server/responder/pam/pamsrv_cmd.c20
-rw-r--r--server/tests/sysdb-tests.c103
-rw-r--r--sss_client/pam_sss.c82
-rw-r--r--sss_client/sss_cli.h5
6 files changed, 219 insertions, 13 deletions
diff --git a/server/db/sysdb.h b/server/db/sysdb.h
index 9b77edfa3..a6d9e69e4 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 c1d996d52..8dd81b3c0 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 = -1;
subreq = sysdb_search_user_by_name_send(state, ev, sysdb, NULL, domain,
name, attrs);
@@ -4821,10 +4823,16 @@ 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;
+ }
+ } else {
+ state->expire_date = 0;
}
ret = check_failed_login_attempts(state, state->cdb, ldb_msg,
@@ -5026,9 +5034,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);
+ *expire_date = state->expire_date;
+
TEVENT_REQ_RETURN_ON_ERROR(req);
return (state->authentication_successful ? EOK : EINVAL);
diff --git a/server/responder/pam/pamsrv_cmd.c b/server/responder/pam/pamsrv_cmd.c
index f89e73c8c..a4573e60d 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 3cd5e7dcc..978764480 100644
--- a/server/tests/sysdb-tests.c
+++ b/server/tests/sysdb-tests.c
@@ -2278,13 +2278,17 @@ START_TEST (test_sysdb_cache_password)
}
END_TEST
-static void cached_authentication(const char *username, const char *password,
- int expected_result)
+static void cached_authentication_without_expiration(const char *username,
+ const char *password,
+ int expected_result)
{
struct sysdb_test_ctx *test_ctx;
struct test_data *data;
struct tevent_req *req;
int ret;
+ time_t expire_date;
+ const char *val[2];
+ val[1] = NULL;
/* Setup */
ret = setup_sysdb_tests(&test_ctx);
@@ -2295,6 +2299,15 @@ static void cached_authentication(const char *username, const char *password,
data->ev = test_ctx->ev;
data->username = username;
+ val[0] = "0";
+ ret = confdb_add_param(test_ctx->confdb, true, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_CRED_TIMEOUT, val);
+ if (ret != EOK) {
+ fail("Could not initialize provider");
+ talloc_free(test_ctx);
+ return;
+ }
+
req = sysdb_cache_auth_send(data, test_ctx->ev, test_ctx->sysdb,
test_ctx->domain, data->username,
(const uint8_t *) password, strlen(password),
@@ -2306,11 +2319,86 @@ 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);
+ fail_unless(expire_date == 0, "Wrong expire date, expected [%d], got [%d]",
+ 0, expire_date);
+
+ talloc_free(test_ctx);
+}
+
+static void cached_authentication_with_expiration(const char *username,
+ const char *password,
+ int expected_result)
+{
+ struct sysdb_test_ctx *test_ctx;
+ struct test_data *data;
+ struct tevent_req *req;
+ int ret;
+ time_t expire_date;
+ const char *val[2];
+ val[1] = NULL;
+ time_t now;
+ time_t expected_expire_date;
+
+ /* Setup */
+ ret = setup_sysdb_tests(&test_ctx);
+ fail_unless(ret == EOK, "Could not set up the test");
+
+ data = talloc_zero(test_ctx, struct test_data);
+ data->ctx = test_ctx;
+ data->ev = test_ctx->ev;
+ data->username = username;
+
+ val[0] = "1";
+ ret = confdb_add_param(test_ctx->confdb, true, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_CRED_TIMEOUT, val);
+ if (ret != EOK) {
+ fail("Could not initialize provider");
+ talloc_free(test_ctx);
+ return;
+ }
+
+ now = time(NULL);
+ expected_expire_date = now + (24 * 60 * 60);
+ DEBUG(9, ("Setting SYSDB_LAST_ONLINE_AUTH to [%lld].\n", (long long) now));
+
+ data->attrs = sysdb_new_attrs(data);
+ ret = sysdb_attrs_add_time_t(data->attrs, SYSDB_LAST_ONLINE_AUTH, now);
+
+ req = sysdb_transaction_send(data, data->ev, test_ctx->sysdb);
+ fail_unless(req != NULL, "sysdb_transaction_send failed.");
+
+ tevent_req_set_callback(req, test_set_user_attr, data);
+
+ ret = test_loop(data);
+ fail_unless(ret == EOK, "Could not modify user %s", data->username);
+ talloc_zfree(req);
+
+ data->finished = false;
+ req = sysdb_cache_auth_send(data, test_ctx->ev, test_ctx->sysdb,
+ test_ctx->domain, data->username,
+ (const uint8_t *) password, strlen(password),
+ test_ctx->confdb);
+ fail_unless(req != NULL, "sysdb_cache_password_send failed.");
+
+ tevent_req_set_callback(req, test_search_done, data);
+
+ ret = test_loop(data);
+ fail_unless(ret == EOK, "test_loop failed.");
+
+ ret = sysdb_cache_auth_recv(req, &expire_date);
+ fail_unless(ret == expected_result, "sysdb_cache_auth request does not "
+ "return expected result [%d], got [%d].",
+ expected_result, ret);
+
+ fail_unless(expire_date == expected_expire_date,
+ "Wrong expire date, expected [%d], got [%d]",
+ expected_expire_date, expire_date);
+
talloc_free(test_ctx);
}
@@ -2325,7 +2413,8 @@ START_TEST (test_sysdb_cached_authentication_missing_password)
username = talloc_asprintf(tmp_ctx, "testuser%d", _i);
fail_unless(username != NULL, "talloc_asprintf failed.");
- cached_authentication(username, "abc", ENOENT);
+ cached_authentication_without_expiration(username, "abc", ENOENT);
+ cached_authentication_with_expiration(username, "abc", ENOENT);
talloc_free(tmp_ctx);
@@ -2343,7 +2432,8 @@ START_TEST (test_sysdb_cached_authentication_wrong_password)
username = talloc_asprintf(tmp_ctx, "testuser%d", _i);
fail_unless(username != NULL, "talloc_asprintf failed.");
- cached_authentication(username, "abc", EINVAL);
+ cached_authentication_without_expiration(username, "abc", EINVAL);
+ cached_authentication_with_expiration(username, "abc", EINVAL);
talloc_free(tmp_ctx);
@@ -2361,7 +2451,8 @@ START_TEST (test_sysdb_cached_authentication)
username = talloc_asprintf(tmp_ctx, "testuser%d", _i);
fail_unless(username != NULL, "talloc_asprintf failed.");
- cached_authentication(username, username, EOK);
+ cached_authentication_without_expiration(username, username, EOK);
+ cached_authentication_with_expiration(username, username, EOK);
talloc_free(tmp_ctx);
diff --git a/sss_client/pam_sss.c b/sss_client/pam_sss.c
index 2b11e26e8..df3dd122f 100644
--- a/sss_client/pam_sss.c
+++ b/sss_client/pam_sss.c
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <syslog.h>
+#include <time.h>
#include <security/pam_modules.h>
#include <security/pam_ext.h>
@@ -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 7d25711b0..c6bb5bd23 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,