mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +03:00
s4:auth: Add password lockout support to the AD DC
Including a fix by Arvid Requate <requate@univention.de> Change-Id: I25d10da50dd6119801cd37349cce970599531c6b Signed-off-by: Andrew Bartlett <abartlet@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
parent
a0de929009
commit
3f07737fd4
@ -213,6 +213,13 @@ static NTSTATUS authsam_authenticate(struct auth4_context *auth_context,
|
||||
nt_status = authsam_password_ok(auth_context, mem_ctx,
|
||||
acct_flags, lm_pwd, nt_pwd,
|
||||
user_info, user_sess_key, lm_sess_key);
|
||||
if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
|
||||
NTSTATUS update_bad_pwd_count_status = authsam_update_bad_pwd_count(auth_context->sam_ctx, msg, domain_dn);
|
||||
if (!NT_STATUS_IS_OK(update_bad_pwd_count_status)) {
|
||||
/* bo! (what can we do here? */
|
||||
}
|
||||
}
|
||||
|
||||
NT_STATUS_NOT_OK_RETURN(nt_status);
|
||||
|
||||
nt_status = authsam_account_ok(mem_ctx, auth_context->sam_ctx,
|
||||
|
@ -83,6 +83,7 @@ const char *user_attrs[] = {
|
||||
"logonCount",
|
||||
"primaryGroupID",
|
||||
"memberOf",
|
||||
"badPasswordTime",
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -634,3 +635,119 @@ NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx,
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx,
|
||||
struct ldb_message *msg,
|
||||
struct ldb_dn *domain_dn)
|
||||
{
|
||||
const char *attrs[] = { "lockoutThreshold",
|
||||
"lockOutObservationWindow",
|
||||
"lockoutDuration",
|
||||
"pwdProperties",
|
||||
NULL };
|
||||
int ret, badPwdCount;
|
||||
int64_t lockoutThreshold, lockOutObservationWindow, badPasswordTime;
|
||||
struct dom_sid *sid;
|
||||
struct ldb_result *domain_res;
|
||||
struct ldb_message *msg_mod;
|
||||
struct timeval tv_now = timeval_current();
|
||||
NTTIME now = timeval_to_nttime(&tv_now);
|
||||
NTSTATUS status;
|
||||
uint32_t pwdProperties, rid = 0;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
mem_ctx = talloc_new(msg);
|
||||
if (mem_ctx == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
|
||||
|
||||
ret = dsdb_search_dn(sam_ctx, mem_ctx, &domain_res, domain_dn, attrs, 0);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
pwdProperties = ldb_msg_find_attr_as_uint(domain_res->msgs[0],
|
||||
"pwdProperties", -1);
|
||||
if (sid && !(pwdProperties & DOMAIN_PASSWORD_LOCKOUT_ADMINS)) {
|
||||
status = dom_sid_split_rid(NULL, sid, NULL, &rid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
/*
|
||||
* This can't happen anyway, but always try
|
||||
* and update the badPwdCount on failure
|
||||
*/
|
||||
rid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Work out if we are doing password lockout on the domain.
|
||||
* Also, the built in administrator account is exempt:
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375371%28v=vs.85%29.aspx
|
||||
*/
|
||||
lockoutThreshold = ldb_msg_find_attr_as_int(domain_res->msgs[0],
|
||||
"lockoutThreshold", 0);
|
||||
if (lockoutThreshold == 0 || (rid == DOMAIN_RID_ADMINISTRATOR)) {
|
||||
DEBUG(5, ("Not updating badPwdCount on %s after wrong password\n",
|
||||
ldb_dn_get_linearized(msg->dn)));
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
lockOutObservationWindow = ldb_msg_find_attr_as_int64(domain_res->msgs[0],
|
||||
"lockOutObservationWindow", 0);
|
||||
|
||||
badPasswordTime = ldb_msg_find_attr_as_int64(msg, "badPasswordTime", 0);
|
||||
|
||||
msg_mod = ldb_msg_new(mem_ctx);
|
||||
if (msg_mod == NULL) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
msg_mod->dn = msg->dn;
|
||||
|
||||
if (badPasswordTime - lockOutObservationWindow >= now) {
|
||||
badPwdCount = ldb_msg_find_attr_as_int(msg, "badPwdCount", 0);
|
||||
} else {
|
||||
badPwdCount = 0;
|
||||
}
|
||||
|
||||
badPwdCount++;
|
||||
|
||||
ret = samdb_msg_add_int(sam_ctx, msg_mod, msg_mod, "badPwdCount", badPwdCount);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod, "badPasswordTime", now);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (badPwdCount >= lockoutThreshold) {
|
||||
ret = samdb_msg_add_int64(sam_ctx, msg_mod, msg_mod, "lockoutTime", now);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
DEBUG(5, ("Locked out user %s after %d wrong passwords\n",
|
||||
ldb_dn_get_linearized(msg->dn), badPwdCount));
|
||||
}
|
||||
|
||||
ret = dsdb_replace(sam_ctx, msg_mod, 0);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
DEBUG(0, ("Failed to upate badPwdCount, badPasswordTime or set lockoutTime on %s: %s\n",
|
||||
ldb_dn_get_linearized(msg_mod->dn), ldb_errstring(sam_ctx)));
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
DEBUG(5, ("Updated badPwdCount on %s after %d wrong passwords\n",
|
||||
ldb_dn_get_linearized(msg->dn), badPwdCount));
|
||||
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
@ -35,6 +35,8 @@
|
||||
#include "includes.h"
|
||||
#include "kdc/kdc-glue.h"
|
||||
#include "kdc/db-glue.h"
|
||||
#include "auth/auth_sam.h"
|
||||
#include <ldb.h>
|
||||
|
||||
static krb5_error_code hdb_samba4_open(krb5_context context, HDB *db, int flags, mode_t mode)
|
||||
{
|
||||
@ -165,6 +167,20 @@ hdb_samba4_check_s4u2self(krb5_context context, HDB *db,
|
||||
target_principal);
|
||||
}
|
||||
|
||||
static krb5_error_code hdb_samba4_auth_status(krb5_context context, HDB *db,
|
||||
hdb_entry_ex *entry,
|
||||
int hdb_auth_status)
|
||||
{
|
||||
struct samba_kdc_db_context *kdc_db_ctx = talloc_get_type_abort(db->hdb_db,
|
||||
struct samba_kdc_db_context);
|
||||
struct samba_kdc_entry *p = talloc_get_type(entry->ctx, struct samba_kdc_entry);
|
||||
|
||||
if (hdb_auth_status == HDB_AUTH_WRONG_PASSWORD) {
|
||||
authsam_update_bad_pwd_count(kdc_db_ctx->samdb, p->msg, ldb_get_default_basedn(kdc_db_ctx->samdb));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This interface is to be called by the KDC and libnet_keytab_dump,
|
||||
* which is expecting Samba calling conventions.
|
||||
* It is also called by a wrapper (hdb_samba4_create) from the
|
||||
@ -216,7 +232,7 @@ NTSTATUS hdb_samba4_create_kdc(struct samba_kdc_base_context *base_ctx,
|
||||
(*db)->hdb__del = NULL;
|
||||
(*db)->hdb_destroy = hdb_samba4_destroy;
|
||||
|
||||
(*db)->hdb_auth_status = NULL;
|
||||
(*db)->hdb_auth_status = hdb_samba4_auth_status;
|
||||
(*db)->hdb_check_constrained_delegation = hdb_samba4_check_constrained_delegation;
|
||||
(*db)->hdb_check_pkinit_ms_upn_match = hdb_samba4_check_pkinit_ms_upn_match;
|
||||
(*db)->hdb_check_s4u2self = hdb_samba4_check_s4u2self;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "libcli/auth/libcli_auth.h"
|
||||
#include "../lib/util/util_ldb.h"
|
||||
#include "rpc_server/samr/proto.h"
|
||||
#include "auth/auth_sam.h"
|
||||
|
||||
/*
|
||||
samr_ChangePasswordUser
|
||||
@ -64,6 +65,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
|
||||
const char * const attrs[] = { "objectSid", "dBCSPwd",
|
||||
"userAccountControl",
|
||||
"msDS-User-Account-Control-Computed",
|
||||
"badPwdCount", "badPasswordTime",
|
||||
NULL };
|
||||
struct samr_Password *lm_pwd;
|
||||
DATA_BLOB lm_pwd_blob;
|
||||
@ -123,6 +125,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
|
||||
|
||||
if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
|
||||
DEBUG(3,("samr: failed to decode password buffer\n"));
|
||||
authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
@ -132,6 +135,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
|
||||
new_password.length,
|
||||
(void **)&new_pass, &converted_size)) {
|
||||
DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
|
||||
authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
@ -141,6 +145,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
|
||||
new_password.length,
|
||||
(void **)&new_unicode_password.data, &unicode_pw_len)) {
|
||||
DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
|
||||
authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
new_unicode_password.length = unicode_pw_len;
|
||||
@ -148,6 +153,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
|
||||
E_deshash(new_pass, new_lm_hash);
|
||||
E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
|
||||
if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
|
||||
authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
|
||||
return NT_STATUS_WRONG_PASSWORD;
|
||||
}
|
||||
|
||||
@ -204,13 +210,14 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
|
||||
NTSTATUS status;
|
||||
DATA_BLOB new_password;
|
||||
struct ldb_context *sam_ctx = NULL;
|
||||
struct ldb_dn *user_dn;
|
||||
struct ldb_dn *user_dn = NULL;
|
||||
int ret;
|
||||
struct ldb_message **res;
|
||||
const char * const attrs[] = { "unicodePwd", "dBCSPwd",
|
||||
"userAccountControl",
|
||||
"msDS-User-Account-Control-Computed",
|
||||
NULL };
|
||||
"badPwdCount", "badPasswordTime",
|
||||
"objectSid", NULL };
|
||||
struct samr_Password *nt_pwd, *lm_pwd;
|
||||
DATA_BLOB nt_pwd_blob;
|
||||
struct samr_DomInfo1 *dominfo = NULL;
|
||||
@ -351,6 +358,11 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
|
||||
return NT_STATUS_OK;
|
||||
|
||||
failed:
|
||||
/* Only update the badPwdCount if we found the user */
|
||||
if (user_dn != NULL && NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
|
||||
authsam_update_bad_pwd_count(sam_ctx, res[0], ldb_get_default_basedn(sam_ctx));
|
||||
}
|
||||
|
||||
reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
|
||||
if (reject != NULL) {
|
||||
reject->extendedFailureReason = reason;
|
||||
|
@ -80,7 +80,7 @@ bld.SAMBA_MODULE('dcesrv_samr',
|
||||
autoproto='samr/proto.h',
|
||||
subsystem='dcerpc_server',
|
||||
init_function='dcerpc_server_samr_init',
|
||||
deps='samdb DCERPC_COMMON ndr-standard'
|
||||
deps='samdb DCERPC_COMMON ndr-standard auth4_sam'
|
||||
)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user