#include "param/param.h"
#include "librpc/gen_ndr/ndr_irpc_c.h"
#include "lib/messaging/irpc.h"
+#include "libcli/auth/libcli_auth.h"
NTSTATUS auth_sam_init(void);
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? */
+ NTSTATUS update_bad_pwd_count_status = nt_status;
+ int history_len;
+ const char *attrs[] = { "pwdHistoryLength", NULL };
+ struct ldb_message *dom_msg;
+ /* pull the attributes */
+ int ret = dsdb_search_one(sam_ctx, mem_ctx, &dom_msg, domain_dn, LDB_SCOPE_BASE,
+ attrs,
+ 0,
+ "objectClass=domain");
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ DEBUG(3,("Couldn't find domain %s!\n",
+ ldb_dn_get_linearized(domain_dn)));
+ TALLOC_FREE(tmp_ctx);
+ /* This looks odd, but we just want to return the original wrong password */
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(tmp_ctx);
+ /* This looks odd, but we just want to return the original wrong password */
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+
+
+ history_len = ldb_msg_find_attr_as_uint(dom_msg, "pwdHistoryLength", -1);
+ if (history_len > 0) {
+ int i;
+ for (i = 1; i < MIN(history_len, 3); i++) {
+ DATA_BLOB user_sess_key_from_history, lm_sess_key_from_history;
+ static const struct samr_Password zero_hash;
+ struct samr_Password zero_string_hash;
+ struct samr_Password zero_string_des_hash;
+
+ /*
+ * We choose to avoid any issues
+ * around different LM and NT history
+ * lengths by only checking the NT
+ * history
+ */
+ struct samr_Password *nt_history_pwd;
+ struct samr_Password *lm_history_pwd;
+ update_bad_pwd_count_status
+ = samdb_result_passwords_from_history(tmp_ctx, auth_context->lp_ctx, msg, i,
+ &lm_history_pwd, &nt_history_pwd);
+
+ if (!NT_STATUS_IS_OK(update_bad_pwd_count_status)) {
+ continue;
+ }
+
+ /* Skip over NULL (not present) or all-zero hashes in the history */
+ if (!nt_history_pwd || memcmp(nt_history_pwd->hash, zero_hash.hash, 16) == 0) {
+ continue;
+ }
+
+ /*
+ * This looks odd, but the password_hash module writes this in if
+ * (somehow) we didn't have an old NT hash
+ */
+
+ E_md4hash("", zero_string_hash.hash);
+ if (memcmp(nt_history_pwd->hash, zero_string_hash.hash, 16) == 0) {
+ continue;
+ }
+
+ E_deshash("", zero_string_des_hash.hash);
+ if (!lm_history_pwd || memcmp(lm_history_pwd->hash, zero_string_des_hash.hash, 16) == 0) {
+ lm_history_pwd = NULL;
+ }
+
+ update_bad_pwd_count_status = authsam_password_ok(auth_context, tmp_ctx,
+ acct_flags, lm_history_pwd,
+ nt_history_pwd,
+ user_info,
+ &user_sess_key_from_history,
+ &lm_sess_key_from_history);
+
+
+ /*
+ * If the password was OK, came from
+ * NTLM, and it was the previous
+ * password, then see if it is within
+ * the grace period, so that we don't
+ * break cached sessions on other
+ * computers before the user can lock
+ * and unlock their other screens
+ * (resetting their cached password)
+ */
+ if (NT_STATUS_IS_OK(update_bad_pwd_count_status)
+ && i == 1
+ && user_info->password_state == AUTH_PASSWORD_RESPONSE) {
+
+ NTTIME pwdLastSet = samdb_result_nttime(msg,
+ "pwdLastSet", 0);
+ NTTIME now;
+ NTTIME grace_period = lpcfg_old_password_grace_period(auth_context->lp_ctx)
+ * 60 * 1000*1000*10;
+ /* Test account expire time */
+ unix_to_nt_time(&now, time(NULL));
+ if (now >= pwdLastSet && ((now - pwdLastSet) < grace_period)) {
+ nt_status = NT_STATUS_OK;
+ *user_sess_key = user_sess_key_from_history;
+ *lm_sess_key = lm_sess_key_from_history;
+ break;
+ }
+ }
+
+ /*
+ * This looks odd, but we just want to
+ * return the original wrong password.
+ * This just ensures we skip the
+ * update of the bad pwd count,
+ * because this is almost certainly user error, not an attack.
+ */
+
+ if (NT_STATUS_IS_OK(update_bad_pwd_count_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
+ }
+ }
+
+ /*
+ * If we are not in the grace period (which resets
+ * nt_status), and we didn't return early because we
+ * matched an old password, update the badPwdCount et
+ * al.
+ */
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
+ 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)) {
+ /* This looks odd, but we just want to return the original wrong password */
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_WRONG_PASSWORD;
+ }
}
}