From: Tim Beale Date: Fri, 6 Apr 2018 04:42:50 +0000 (+1200) Subject: dsdb/auth: Use PSO settings for lockOutThreshold/Duration X-Git-Tag: ldb-1.4.0~87 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=442a38c918ae1666b35;p=thirdparty%2Fsamba.git dsdb/auth: Use PSO settings for lockOutThreshold/Duration If a PSO applies to a user, use its lockOutThreshold/Duration settings instead of the domain setting. When we lookup a user, we now include the msDS-ResultantPSO attribute. If the attribute is present for a user, then we lookup the corresponding PSO object to get the lockOutThreshold/ Duration settings. Note: This is not quite enough to make the PSO lockout tests pass, as msDS-User-Account-Control-Computed is still constructed based on the domain lockoutDuration setting rather than the PSO. Updating the password_hash.c code properly will be done in a subsequent commit. Signed-off-by: Tim Beale Reviewed-by: Andrew Bartlett Reviewed-by: Garming Sam --- diff --git a/source4/auth/sam.c b/source4/auth/sam.c index 9d1fc650142..22ec8cef2a1 100644 --- a/source4/auth/sam.c +++ b/source4/auth/sam.c @@ -103,6 +103,7 @@ const char *user_attrs[] = { "badPasswordTime", "lmPwdHistory", "ntPwdHistory", + "msDS-ResultantPSO", NULL, }; @@ -777,6 +778,38 @@ NTSTATUS authsam_get_user_info_dc_principal(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } +/* + * Returns the details for the Password Settings Object (PSO), if one applies + * the user. + */ +static int authsam_get_user_pso(struct ldb_context *sam_ctx, + TALLOC_CTX *mem_ctx, + struct ldb_message *user_msg, + struct ldb_message **pso_msg) +{ + const char *attrs[] = { "msDS-LockoutThreshold", + "msDS-LockoutObservationWindow", + NULL }; + struct ldb_dn *pso_dn = NULL; + struct ldb_result *res = NULL; + int ret; + + /* check if the user has a PSO that applies to it */ + pso_dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, user_msg, + "msDS-ResultantPSO"); + + if (pso_dn != NULL) { + ret = dsdb_search_dn(sam_ctx, mem_ctx, &res, pso_dn, attrs, 0); + if (ret != LDB_SUCCESS) { + return ret; + } + + *pso_msg = res->msgs[0]; + } + + return LDB_SUCCESS; +} + NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, struct ldb_message *msg, struct ldb_dn *domain_dn) @@ -790,6 +823,7 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, NTSTATUS status; struct ldb_result *domain_res; struct ldb_message *msg_mod = NULL; + struct ldb_message *pso_msg = NULL; TALLOC_CTX *mem_ctx; mem_ctx = talloc_new(msg); @@ -803,8 +837,20 @@ NTSTATUS authsam_update_bad_pwd_count(struct ldb_context *sam_ctx, return NT_STATUS_INTERNAL_DB_CORRUPTION; } + ret = authsam_get_user_pso(sam_ctx, mem_ctx, msg, &pso_msg); + if (ret != LDB_SUCCESS) { + + /* + * fallback to using the domain defaults so that we still + * record the bad password attempt + */ + DBG_ERR("Error (%d) checking PSO for %s", + ret, ldb_dn_get_linearized(msg->dn)); + } + status = dsdb_update_bad_pwd_count(mem_ctx, sam_ctx, - msg, domain_res->msgs[0], &msg_mod); + msg, domain_res->msgs[0], pso_msg, + &msg_mod); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(mem_ctx); return status; diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c index ed91bc725df..cc589095294 100644 --- a/source4/dsdb/common/util.c +++ b/source4/dsdb/common/util.c @@ -5266,6 +5266,39 @@ int samdb_result_effective_badPwdCount(struct ldb_context *sam_ldb, return dsdb_effective_badPwdCount(user_msg, lockOutObservationWindow, now); } +/* + * Returns the lockoutThreshold that applies. If a PSO is specified, then that + * setting is used over the domain defaults + */ +static int64_t get_lockout_threshold(struct ldb_message *domain_msg, + struct ldb_message *pso_msg) +{ + if (pso_msg != NULL) { + return ldb_msg_find_attr_as_int(pso_msg, + "msDS-LockoutThreshold", 0); + } else { + return ldb_msg_find_attr_as_int(domain_msg, + "lockoutThreshold", 0); + } +} + +/* + * Returns the lockOutObservationWindow that applies. If a PSO is specified, + * then that setting is used over the domain defaults + */ +static int64_t get_lockout_observation_window(struct ldb_message *domain_msg, + struct ldb_message *pso_msg) +{ + if (pso_msg != NULL) { + return ldb_msg_find_attr_as_int(pso_msg, + "msDS-LockoutObservationWindow", + 0); + } else { + return ldb_msg_find_attr_as_int(domain_msg, + "lockOutObservationWindow", 0); + } +} + /* * Prepare an update to the badPwdCount and associated attributes. * @@ -5278,11 +5311,16 @@ int samdb_result_effective_badPwdCount(struct ldb_context *sam_ldb, * - pwdProperties * - lockoutThreshold * - lockOutObservationWindow + * + * This also requires that the pso_msg have (if present): + * - msDS-LockoutThreshold + * - msDS-LockoutObservationWindow */ NTSTATUS dsdb_update_bad_pwd_count(TALLOC_CTX *mem_ctx, struct ldb_context *sam_ctx, struct ldb_message *user_msg, struct ldb_message *domain_msg, + struct ldb_message *pso_msg, struct ldb_message **_mod_msg) { int i, ret, badPwdCount; @@ -5315,8 +5353,7 @@ NTSTATUS dsdb_update_bad_pwd_count(TALLOC_CTX *mem_ctx, * 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_msg, - "lockoutThreshold", 0); + lockoutThreshold = get_lockout_threshold(domain_msg, pso_msg); if (lockoutThreshold == 0 || (rid == DOMAIN_RID_ADMINISTRATOR)) { DEBUG(5, ("Not updating badPwdCount on %s after wrong password\n", ldb_dn_get_linearized(user_msg->dn))); @@ -5333,8 +5370,8 @@ NTSTATUS dsdb_update_bad_pwd_count(TALLOC_CTX *mem_ctx, return NT_STATUS_NO_MEMORY; } - lockOutObservationWindow = ldb_msg_find_attr_as_int64(domain_msg, - "lockOutObservationWindow", 0); + lockOutObservationWindow = get_lockout_observation_window(domain_msg, + pso_msg); badPwdCount = dsdb_effective_badPwdCount(user_msg, lockOutObservationWindow, now); diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c index 2641cb9c064..80e634895d5 100644 --- a/source4/dsdb/samdb/ldb_modules/password_hash.c +++ b/source4/dsdb/samdb/ldb_modules/password_hash.c @@ -2533,6 +2533,7 @@ static int make_error_and_update_badPwdCount(struct setup_password_fields_io *io status = dsdb_update_bad_pwd_count(io->ac, ldb, io->ac->search_res->message, io->ac->dom_res->message, + NULL, /* TODO: support PSO */ &mod_msg); if (!NT_STATUS_IS_OK(status)) { goto done;