summaryrefslogtreecommitdiffstats
path: root/server/db/sysdb_ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'server/db/sysdb_ops.c')
-rw-r--r--server/db/sysdb_ops.c240
1 files changed, 231 insertions, 9 deletions
diff --git a/server/db/sysdb_ops.c b/server/db/sysdb_ops.c
index 469ed8d4d..c1d996d52 100644
--- a/server/db/sysdb_ops.c
+++ b/server/db/sysdb_ops.c
@@ -3168,6 +3168,9 @@ struct tevent_req *sysdb_cache_password_send(TALLOC_CTX *mem_ctx,
(long)time(NULL));
if (ret) goto fail;
+ ret = sysdb_attrs_add_uint32(state->attrs, SYSDB_FAILED_LOGIN_ATTEMPTS, 0U);
+ if (ret) goto fail;
+
state->handle = NULL;
if (handle) {
@@ -4642,9 +4645,66 @@ struct sysdb_cache_auth_state {
struct sss_domain_info *domain;
struct sysdb_ctx *sysdb;
struct confdb_ctx *cdb;
+ struct sysdb_attrs *update_attrs;
+ bool authentication_successful;
+ struct sysdb_handle *handle;
};
+errno_t check_failed_login_attempts(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
+ struct ldb_message *ldb_msg,
+ uint32_t *failed_login_attempts)
+{
+ int ret;
+ int allowed_failed_login_attempts;
+ int failed_login_delay;
+ time_t last_failed_login;
+
+ *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,
+ SYSDB_LAST_FAILED_LOGIN, 0);
+ ret = confdb_get_int(cdb, mem_ctx, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_FAILED_LOGIN_ATTEMPTS,
+ CONFDB_DEFAULT_PAM_FAILED_LOGIN_ATTEMPTS,
+ &allowed_failed_login_attempts);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to read the number of allowed failed login "
+ "attempts.\n"));
+ return EIO;
+ }
+ ret = confdb_get_int(cdb, mem_ctx, CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAM_FAILED_LOGIN_DELAY,
+ CONFDB_DEFAULT_PAM_FAILED_LOGIN_DELAY,
+ &failed_login_delay);
+ if (ret != EOK) {
+ DEBUG(1, ("Failed to read the failed login delay.\n"));
+ return EIO;
+ }
+ DEBUG(9, ("Failed login attempts [%d], allowed failed login attempts [%d], "
+ "failed login delay [%d].\n", *failed_login_attempts,
+ allowed_failed_login_attempts, failed_login_delay));
+
+ 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;
+ } else {
+ DEBUG(4, ("Too many failed logins.\n"));
+ return EACCES;
+ }
+ }
+ }
+
+ return EOK;
+}
+
static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq);
+static void sysdb_cache_auth_transaction_start_done(struct tevent_req *subreq);
+static void sysdb_cache_auth_attr_update_done(struct tevent_req *subreq);
+static void sysdb_cache_auth_done(struct tevent_req *subreq);
struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
@@ -4686,8 +4746,8 @@ struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx,
SYSDB_LAST_ONLINE_AUTH,
"lastCachedPasswordChange",
"accountExpires",
- "failedLoginAttempts",
- "lastFailedLogin",
+ SYSDB_FAILED_LOGIN_ATTEMPTS,
+ SYSDB_LAST_FAILED_LOGIN,
NULL};
req = tevent_req_create(mem_ctx, &state, struct sysdb_cache_auth_state);
@@ -4703,6 +4763,9 @@ struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx,
state->domain = domain;
state->sysdb = sysdb;
state->cdb = cdb;
+ state->update_attrs = NULL;
+ state->authentication_successful = false;
+ state->handle = NULL;
subreq = sysdb_search_user_by_name_send(state, ev, sysdb, NULL, domain,
name, attrs);
@@ -4726,6 +4789,7 @@ static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq)
int ret;
uint64_t lastLogin = 0;
int cred_expiration;
+ uint32_t failed_login_attempts = 0;
struct tevent_req *req = tevent_req_callback_data(subreq,
struct tevent_req);
@@ -4758,12 +4822,18 @@ static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq)
cred_expiration));
if (cred_expiration && lastLogin + (cred_expiration * 86400) < time(NULL)) {
- DEBUG(4, ("Cached user entry is too old."));
+ DEBUG(4, ("Cached user entry is too old.\n"));
ret = EACCES;
goto done;
}
- /* TODO: verify user account (failed logins, disabled, expired ...) */
+ ret = check_failed_login_attempts(state, state->cdb, ldb_msg,
+ &failed_login_attempts);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ /* TODO: verify user account (disabled, expired ...) */
password = talloc_strndup(state, (const char *) state->authtok,
state->authtok_size);
@@ -4787,15 +4857,67 @@ static void sysdb_cache_auth_get_attrs_done(struct tevent_req *subreq)
goto done;
}
+ state->update_attrs = sysdb_new_attrs(state);
+ if (state->update_attrs == NULL) {
+ DEBUG(1, ("sysdb_new_attrs failed.\n"));
+ goto done;
+ }
+
if (strcmp(userhash, comphash) == 0) {
/* TODO: probable good point for audit logging */
DEBUG(4, ("Hashes do match!\n"));
- ret = EOK;
- goto done;
+ state->authentication_successful = true;
+
+ ret = sysdb_attrs_add_time_t(state->update_attrs, SYSDB_LAST_LOGIN,
+ time(NULL));
+ if (ret != EOK) {
+ DEBUG(3, ("sysdb_attrs_add_time_t failed, "
+ "but authentication is successful.\n"));
+ ret = EOK;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_uint32(state->update_attrs,
+ SYSDB_FAILED_LOGIN_ATTEMPTS, 0U);
+ if (ret != EOK) {
+ DEBUG(3, ("sysdb_attrs_add_uint32 failed, "
+ "but authentication is successful.\n"));
+ ret = EOK;
+ goto done;
+ }
+
+
+ } else {
+ DEBUG(4, ("Authentication failed.\n"));
+ state->authentication_successful = false;
+
+ ret = sysdb_attrs_add_time_t(state->update_attrs,
+ SYSDB_LAST_FAILED_LOGIN,
+ time(NULL));
+ if (ret != EOK) {
+ DEBUG(3, ("sysdb_attrs_add_time_t failed\n."));
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_uint32(state->update_attrs,
+ SYSDB_FAILED_LOGIN_ATTEMPTS,
+ ++failed_login_attempts);
+ if (ret != EOK) {
+ DEBUG(3, ("sysdb_attrs_add_uint32 failed.\n"));
+ ret = EINVAL;
+ goto done;
+ }
}
- DEBUG(4, ("Authentication failed.\n"));
- ret = EINVAL;
+ subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+ if (subreq == NULL) {
+ DEBUG(1, ("sysdb_transaction_send failed.\n"));
+ goto done;
+ }
+ tevent_req_set_callback(subreq, sysdb_cache_auth_transaction_start_done,
+ req);
+ return;
done:
if (password) for (i = 0; password[i]; i++) password[i] = 0;
@@ -4807,7 +4929,107 @@ done:
return;
}
+static void sysdb_cache_auth_transaction_start_done(struct tevent_req *subreq)
+{
+ int ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+
+ struct sysdb_cache_auth_state *state = tevent_req_data(req,
+ struct sysdb_cache_auth_state);
+
+ ret = sysdb_transaction_recv(subreq, state, &state->handle);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_transaction_send failed [%d][%s].\n",
+ ret, strerror(ret)));
+ goto done;
+ }
+
+
+ subreq = sysdb_set_user_attr_send(state, state->ev, state->handle,
+ state->domain, state->name,
+ state->update_attrs,
+ LDB_FLAG_MOD_REPLACE);
+ if (subreq == NULL) {
+ DEBUG(1, ("sysdb_set_user_attr_send failed.\n"));
+ goto done;
+ }
+ tevent_req_set_callback(subreq, sysdb_cache_auth_attr_update_done,
+ req);
+ return;
+
+done:
+ if (state->authentication_successful) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, EINVAL);
+ }
+ return;
+}
+
+static void sysdb_cache_auth_attr_update_done(struct tevent_req *subreq)
+{
+ int ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+
+ struct sysdb_cache_auth_state *state = tevent_req_data(req,
+ struct sysdb_cache_auth_state);
+
+ ret = sysdb_set_user_attr_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_set_user_attr request failed [%d][%s].\n",
+ ret, strerror(ret)));
+ goto done;
+ }
+
+ subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+ if (subreq == NULL) {
+ DEBUG(1, ("sysdb_transaction_commit_send failed.\n"));
+ goto done;
+ }
+ tevent_req_set_callback(subreq, sysdb_cache_auth_done, req);
+ return;
+
+done:
+ if (state->authentication_successful) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, EINVAL);
+ }
+ return;
+}
+
+static void sysdb_cache_auth_done(struct tevent_req *subreq)
+{
+ int ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+
+ struct sysdb_cache_auth_state *state = tevent_req_data(req,
+ struct sysdb_cache_auth_state);
+
+ ret = sysdb_transaction_commit_recv(subreq);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(1, ("sysdb_transaction_commit_send failed [%d][%s].\n",
+ ret, strerror(ret)));
+ }
+
+ if (state->authentication_successful) {
+ tevent_req_done(req);
+ } else {
+ tevent_req_error(req, EINVAL);
+ }
+ return;
+}
+
int sysdb_cache_auth_recv(struct tevent_req *req) {
+ struct sysdb_cache_auth_state *state = tevent_req_data(req,
+ struct sysdb_cache_auth_state);
TEVENT_REQ_RETURN_ON_ERROR(req);
- return EOK;
+
+ return (state->authentication_successful ? EOK : EINVAL);
}