From 7ea48433b3bab77813b11c60e1ea82cb9793dc33 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Mon, 25 Jan 2010 13:45:16 +0100 Subject: Add offline failed login counter --- server/db/sysdb.h | 6 ++ server/db/sysdb_ops.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 237 insertions(+), 9 deletions(-) (limited to 'server/db') diff --git a/server/db/sysdb.h b/server/db/sysdb.h index 4c2554927..9b77edfa3 100644 --- a/server/db/sysdb.h +++ b/server/db/sysdb.h @@ -22,6 +22,7 @@ #ifndef __SYS_DB_H__ #define __SYS_DB_H__ +#include "util/util.h" #include "confdb/confdb.h" #include @@ -66,6 +67,8 @@ #define SYSDB_LAST_LOGIN "lastLogin" #define SYSDB_LAST_ONLINE_AUTH "lastOnlineAuth" #define SYSDB_USERPIC "userPicture" +#define SYSDB_LAST_FAILED_LOGIN "lastFailedLogin" +#define SYSDB_FAILED_LOGIN_ATTEMPTS "failedLoginAttempts" #define SYSDB_LAST_UPDATE "lastUpdate" #define SYSDB_CACHE_EXPIRE "dataExpireTimestamp" @@ -543,6 +546,9 @@ struct tevent_req *sysdb_cache_password_send(TALLOC_CTX *mem_ctx, 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); struct tevent_req *sysdb_cache_auth_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct sysdb_ctx *sysdb, 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); } -- cgit