From c783c15da789d06100f61ba7efd3da489a13786c Mon Sep 17 00:00:00 2001 From: Adrian Cochrane Date: Tue, 19 Jan 2016 12:21:50 +1300 Subject: [PATCH 3/3] samdb: Fixing pwdLastSet to behave more like Windows This patch adds the pwdLastSet attribute to the password_hash checks and introduces a restriction where that attribute can only be set to 0 and -1, where -1 translates to the current time. Bug: https://bugzilla.samba.org/show_bug.cgi?id=9654 Signed-off-by: Adrian Cochrane --- selftest/knownfail | 2 -- source4/dsdb/samdb/ldb_modules/password_hash.c | 45 ++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/selftest/knownfail b/selftest/knownfail index 54b3e68..7ee42c8 100644 --- a/selftest/knownfail +++ b/selftest/knownfail @@ -305,5 +305,3 @@ # Fails on s4 ^samba4.ldap.passwords.python\(.*\).__main__.PasswordTests.test_failure18\(.*\) ^samba4.ldap.passwords.python\(.*\).__main__.PasswordTests.test_failure19\(.*\) -# Fixed in next patch () -^samba4.ldap.passwords.python\(.*\).__main__.PasswordTests.test_pwdLastSet\(.*\) diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c index 05b0854..c39c9e5 100644 --- a/source4/dsdb/samdb/ldb_modules/password_hash.c +++ b/source4/dsdb/samdb/ldb_modules/password_hash.c @@ -1684,9 +1684,19 @@ static int setup_supplemental_field(struct setup_password_fields_io *io) return LDB_SUCCESS; } +static int msg_find_old_and_new_pwd_val(const struct ldb_message *msg, + const char *name, + enum ldb_request_type operation, + const struct ldb_val **new_val, + const struct ldb_val **old_val); + static int setup_last_set_field(struct setup_password_fields_io *io) { + struct ldb_context *ldb = ldb_module_get_ctx(io->ac->module); const struct ldb_message *msg = NULL; + const struct ldb_val *o = NULL; + const struct ldb_val *n = NULL; + int ret; switch (io->ac->req->operation) { case LDB_ADD: @@ -1716,8 +1726,31 @@ static int setup_last_set_field(struct setup_password_fields_io *io) return LDB_SUCCESS; } - /* set it as now */ - unix_to_nt_time(&io->g.last_set, time(NULL)); + /* + * We can't use ldb_msg_find_attr_as_int64(), + * we have to use msg_find_old_and_new_pwd_val() + */ + ret = msg_find_old_and_new_pwd_val(msg, "pwdLastSet", + io->ac->req->operation, + &n, &o); + if (ret != LDB_SUCCESS) { + return ret; + } + + /* check/correct the pwdLastSet value */ + if (n == NULL || ldb_val_string_cmp(n, "-1") == 0) { + unix_to_nt_time(&io->g.last_set, time(NULL)); + } else if (ldb_val_string_cmp(n, "0") == 0) { + io->g.last_set = 0; + } else { + ret = LDB_ERR_OTHER; + ldb_asprintf_errstring(ldb, + "%08X: %s - setup_last_set_field: " + "pwdLastSet must be 0 or -1 only!", + W_ERROR_V(WERR_INVALID_PARAM), + ldb_strerror(ret)); + return ret; + } return LDB_SUCCESS; } @@ -2535,10 +2568,10 @@ static int setup_io(struct ph_context *ac, * the lanman hash alone and we've deactivated that mechanism. This * would end in an account without any password! */ if ((!io->n.cleartext_utf8) && (!io->n.cleartext_utf16) - && (!io->n.nt_hash) && (!io->n.lm_hash)) { + && (!io->n.nt_hash) && (!io->n.lm_hash) && (lm_hash)) { ldb_asprintf_errstring(ldb, "setup_io: " - "It' not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!"); + "It's not possible to delete the password (changes using the LAN Manager hash alone could be deactivated)!"); /* on "userPassword" and "clearTextPassword" we've to return * something different, since these are virtual attributes */ if ((ldb_msg_find_element(orig_msg, "userPassword") != NULL) || @@ -3057,7 +3090,7 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r struct ldb_context *ldb; struct ph_context *ac; const char *passwordAttrs[] = { "userPassword", "clearTextPassword", - "unicodePwd", "dBCSPwd", NULL }, **l; + "unicodePwd", "dBCSPwd", "pwdLastSet", NULL }, **l; unsigned int attr_cnt, del_attr_cnt, add_attr_cnt, rep_attr_cnt; struct ldb_message_element *passwordAttr; struct ldb_message *msg; @@ -3100,7 +3133,7 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r * on these attributes. */ attr_cnt = 0; for (l = passwordAttrs; *l != NULL; l++) { - if ((!userPassword) && (ldb_attr_cmp(*l, "userPassword") == 0)) { + if ((!userPassword) && (ldb_attr_cmp(*l, "userPassword") == 0 || ldb_attr_cmp(*l, "pwdLastSet") == 0)) { continue; } -- 1.9.1