return LDB_SUCCESS;
}
+static int make_error_and_update_badPwdCount(struct setup_password_fields_io *io)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module);
+ struct ldb_message *mod_msg = NULL;
+ NTSTATUS status;
+ int ret;
+
+ status = dsdb_update_bad_pwd_count(io->ac, ldb,
+ io->ac->search_res->message,
+ io->ac->dom_res->message,
+ &mod_msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (mod_msg == NULL) {
+ goto done;
+ }
+
+ /*
+ * OK, horrible semantics ahead.
+ *
+ * - We need to abort any existing transaction
+ * - create a transaction arround the badPwdCount update
+ * - re-open the transaction so the upper layer
+ * doesn't know what happened.
+ *
+ * This is needed because returning an error to the upper
+ * layer will cancel the transaction and undo the badPwdCount
+ * update.
+ */
+
+ /*
+ * Checking errors here is a bit pointless.
+ * What can we do if we can't end the transaction?
+ */
+ ret = ldb_next_del_trans(io->ac->module);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_FATAL,
+ "Failed to abort transaction prior to update of badPwdCount of %s: %s",
+ ldb_dn_get_linearized(io->ac->search_res->message->dn),
+ ldb_errstring(ldb));
+ /*
+ * just return the original error
+ */
+ goto done;
+ }
+
+ /* Likewise, what should we do if we can't open a new transaction? */
+ ret = ldb_next_start_trans(io->ac->module);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Failed to open transaction to update badPwdCount of %s: %s",
+ ldb_dn_get_linearized(io->ac->search_res->message->dn),
+ ldb_errstring(ldb));
+ /*
+ * just return the original error
+ */
+ goto done;
+ }
+
+ ret = dsdb_module_modify(io->ac->module, mod_msg,
+ DSDB_FLAG_NEXT_MODULE,
+ io->ac->req);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Failed to update badPwdCount of %s: %s",
+ ldb_dn_get_linearized(io->ac->search_res->message->dn),
+ ldb_errstring(ldb));
+ /*
+ * We can only ignore this...
+ */
+ }
+
+ ret = ldb_next_end_trans(io->ac->module);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Failed to close transaction to update badPwdCount of %s: %s",
+ ldb_dn_get_linearized(io->ac->search_res->message->dn),
+ ldb_errstring(ldb));
+ /*
+ * We can only ignore this...
+ */
+ }
+
+ ret = ldb_next_start_trans(io->ac->module);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR,
+ "Failed to open transaction after update of badPwdCount of %s: %s",
+ ldb_dn_get_linearized(io->ac->search_res->message->dn),
+ ldb_errstring(ldb));
+ /*
+ * We can only ignore this...
+ */
+ }
+
+done:
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
+ ldb_asprintf_errstring(ldb,
+ "%08X: %s - check_password_restrictions: "
+ "The old password specified doesn't match!",
+ W_ERROR_V(WERR_INVALID_PASSWORD),
+ ldb_strerror(ret));
+ return ret;
+}
+
static int check_password_restrictions(struct setup_password_fields_io *io)
{
struct ldb_context *ldb;
has no problems at all */
if (io->og.nt_hash) {
if (!io->o.nt_hash || memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
- ret = LDB_ERR_CONSTRAINT_VIOLATION;
- ldb_asprintf_errstring(ldb,
- "%08X: %s - check_password_restrictions: "
- "The old password specified doesn't match!",
- W_ERROR_V(WERR_INVALID_PASSWORD),
- ldb_strerror(ret));
- return ret;
+ return make_error_and_update_badPwdCount(io);
}
nt_hash_checked = true;
if (io->og.lm_hash) {
if ((!io->o.lm_hash && !nt_hash_checked)
|| (io->o.lm_hash && memcmp(io->og.lm_hash->hash, io->o.lm_hash->hash, 16) != 0)) {
- ret = LDB_ERR_CONSTRAINT_VIOLATION;
- ldb_asprintf_errstring(ldb,
- "%08X: %s - check_password_restrictions: "
- "The old password specified doesn't match!",
- W_ERROR_V(WERR_INVALID_PASSWORD),
- ldb_strerror(ret));
- return ret;
+ return make_error_and_update_badPwdCount(io);
}
}
}
ac->status->domain_data.store_cleartext =
ac->status->domain_data.pwdProperties & DOMAIN_PASSWORD_STORE_CLEARTEXT;
- talloc_free(ares);
-
/* For a domain DN, this puts things in dotted notation */
/* For builtin domains, this will give details for the host,
* but that doesn't really matter, as it's just used for salt
ac->status->reject_reason = SAM_PWD_CHANGE_NO_ERROR;
+ if (ac->dom_res != NULL) {
+ talloc_free(ares);
+
+ ldb_set_errstring(ldb, "Too many results");
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ac->dom_res = talloc_steal(ac, ares);
ret = LDB_SUCCESS;
break;
"maxPwdAge",
"minPwdAge",
"minPwdLength",
+ "lockoutThreshold",
+ "lockOutObservationWindow",
NULL };
int ret;
"ntPwdHistory",
"dBCSPwd",
"unicodePwd",
+ "badPasswordTime",
+ "badPwdCount",
+ "lockoutTime",
NULL };
struct ldb_request *search_req;
int ret;
&io.o.lm_hash, &io.o.nt_hash);
}
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
+ ldb_asprintf_errstring(ldb,
+ "%08X: check_password: "
+ "Password change not permitted, account locked out!",
+ W_ERROR_V(WERR_ACCOUNT_LOCKED_OUT));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * This only happens if the database has gone weird,
+ * not if we are just missing the passwords
+ */
return ldb_operr(ldb);
}