[SCM] Samba Shared Repository - branch master updated
Matthias Dieter Wallnöfer
mdw at samba.org
Sun Aug 15 11:43:09 MDT 2010
The branch, master has been updated
via af3c6a4... s4:passwords.py - proof the most important extended error codes
via 3fcd762... s4:samdb_set_password - implement the extended LDAP error code detection
via 2dbff00... s4:password_hash LDB module - introduce the extended LDAP error codes on the important failure cases
via 33bb063... s4:password_hash LDB module - support this new password set syntax
via 6dc0c07... s4:passwords.py - another special password test
via 28cfae7... s4:password_hash LDB module - allow to compare against both NT and LM hashes on password change operations
via 9476c43... s4:ldap_backend.c - Windows returns WERROR codes in majuscule HEX format
via fb58c0f... s4:ldap_backend.c - map error codes - add a change which allows custom WERROR codes
from 08b628e... s3: Remove some unused code
http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master
- Log -----------------------------------------------------------------
commit af3c6a42426241704580f4440b09a8c155d212df
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date: Sun Aug 15 18:19:52 2010 +0200
s4:passwords.py - proof the most important extended error codes
commit 3fcd76237d1c621e6bb876c4c33706e0db2056e2
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date: Sun Aug 15 18:05:29 2010 +0200
s4:samdb_set_password - implement the extended LDAP error code detection
commit 2dbff00b6dd3affc95c717296d52343daf49361b
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date: Sun Aug 15 17:38:47 2010 +0200
s4:password_hash LDB module - introduce the extended LDAP error codes on the important failure cases
commit 33bb063b053c24a84fdd13b866d1f80a964aeabf
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date: Sun Aug 15 10:20:48 2010 +0200
s4:password_hash LDB module - support this new password set syntax
commit 6dc0c07a51ee3d26ffc44e6178b6ae842190bd19
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date: Sun Aug 15 10:02:43 2010 +0200
s4:passwords.py - another special password test
This looks like a password change but it's rather a password set operation.
commit 28cfae774edf7bd4e2c4d9285b0d0508bee64284
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date: Sun Aug 15 09:36:25 2010 +0200
s4:password_hash LDB module - allow to compare against both NT and LM hashes on password change operations
This is to match the SAMR password change behaviour.
commit 9476c43967af66e854cfda13bf09e21da7e63a44
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date: Sun Aug 15 18:45:09 2010 +0200
s4:ldap_backend.c - Windows returns WERROR codes in majuscule HEX format
commit fb58c0f36575510ca6572e695afdd81469ced3dd
Author: Matthias Dieter Wallnöfer <mdw at samba.org>
Date: Sun Aug 15 09:25:58 2010 +0200
s4:ldap_backend.c - map error codes - add a change which allows custom WERROR codes
This is strictly needed by my recent passwords work, since I want to remove
most of the password change stuff in "samr_password.c". Since AD gives us
CONSTRAINT_VIOLATION on all change problems I cannot distinguish on the SAMR
level which the real cause was about. Therefore I need the extended WERROR codes
here.
-----------------------------------------------------------------------
Summary of changes:
source4/dsdb/common/util.c | 26 +++--
source4/dsdb/samdb/ldb_modules/password_hash.c | 138 ++++++++++++++----------
source4/dsdb/tests/python/passwords.py | 53 +++++++--
source4/ldap_server/ldap_backend.c | 15 +++-
4 files changed, 155 insertions(+), 77 deletions(-)
Changeset truncated at 500 lines:
diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
index be8e3a9..3ce0b2c 100644
--- a/source4/dsdb/common/util.c
+++ b/source4/dsdb/common/util.c
@@ -2021,7 +2021,7 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
struct ldb_request *req;
struct dsdb_control_password_change_status *pwd_stat = NULL;
int ret;
- NTSTATUS status;
+ NTSTATUS status = NT_STATUS_OK;
#define CHECK_RET(x) \
if (x != LDB_SUCCESS) { \
@@ -2141,18 +2141,26 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
talloc_free(pwd_stat);
}
- /* TODO: Error results taken from "password_hash" module. Are they
- correct? */
- if (ret == LDB_ERR_UNWILLING_TO_PERFORM) {
- status = NT_STATUS_WRONG_PASSWORD;
- } else if (ret == LDB_ERR_CONSTRAINT_VIOLATION) {
- status = NT_STATUS_PASSWORD_RESTRICTION;
+ if (ret == LDB_ERR_CONSTRAINT_VIOLATION) {
+ const char *errmsg = ldb_errstring(ldb);
+ char *endptr = NULL;
+ WERROR werr = WERR_GENERAL_FAILURE;
+ status = NT_STATUS_UNSUCCESSFUL;
+ if (errmsg != NULL) {
+ werr = W_ERROR(strtol(errmsg, &endptr, 16));
+ }
+ if (endptr != errmsg) {
+ if (W_ERROR_EQUAL(werr, WERR_INVALID_PASSWORD)) {
+ status = NT_STATUS_WRONG_PASSWORD;
+ }
+ if (W_ERROR_EQUAL(werr, WERR_PASSWORD_RESTRICTION)) {
+ status = NT_STATUS_PASSWORD_RESTRICTION; }
+ }
} else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ /* don't let the caller know if an account doesn't exist */
status = NT_STATUS_WRONG_PASSWORD;
} else if (ret != LDB_SUCCESS) {
status = NT_STATUS_UNSUCCESSFUL;
- } else {
- status = NT_STATUS_OK;
}
return status;
diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
index cf239fb..60f0c3e 100644
--- a/source4/dsdb/samdb/ldb_modules/password_hash.c
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -1439,7 +1439,7 @@ static int check_password_restrictions(struct setup_password_fields_io *io)
if (!io->ac->pwd_reset && !io->ac->change_old_pw_checked) {
bool nt_hash_checked = false;
- /* we need to old nt or lm hash given by the client */
+ /* we need the old nt or lm hash given by the client */
if (!io->og.nt_hash && !io->og.lm_hash) {
ldb_asprintf_errstring(ldb,
"check_password_restrictions: "
@@ -1452,18 +1452,24 @@ static int check_password_restrictions(struct setup_password_fields_io *io)
has no problems at all */
if (io->og.nt_hash) {
if (!io->o.nt_hash) {
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
+ "%08X: %s - check_password_restrictions: "
"There's no old nt_hash, which is needed "
- "in order to change your password!");
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ "in order to change your password!",
+ W_ERROR_V(WERR_INVALID_PASSWORD),
+ ldb_strerror(ret));
+ return ret;
}
if (memcmp(io->og.nt_hash->hash, io->o.nt_hash->hash, 16) != 0) {
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
- "The old password specified doesn't match!");
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ "%08X: %s - check_password_restrictions: "
+ "The old password specified doesn't match!",
+ W_ERROR_V(WERR_INVALID_PASSWORD),
+ ldb_strerror(ret));
+ return ret;
}
nt_hash_checked = true;
@@ -1475,19 +1481,25 @@ static int check_password_restrictions(struct setup_password_fields_io *io)
* (as the SAMR operations request it). */
if (io->og.lm_hash) {
if (!io->o.lm_hash && !nt_hash_checked) {
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
+ "%08X: %s - check_password_restrictions: "
"There's no old lm_hash, which is needed "
- "in order to change your password!");
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ "in order to change your password!",
+ W_ERROR_V(WERR_INVALID_PASSWORD),
+ ldb_strerror(ret));
+ return ret;
}
if (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,
- "check_password_restrictions: "
- "The old password specified doesn't match!");
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ "%08X: %s - check_password_restrictions: "
+ "The old password specified doesn't match!",
+ W_ERROR_V(WERR_INVALID_PASSWORD),
+ ldb_strerror(ret));
+ return ret;
}
}
}
@@ -1512,28 +1524,34 @@ static int check_password_restrictions(struct setup_password_fields_io *io)
break;
case SAMR_VALIDATION_STATUS_PWD_TOO_SHORT:
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
- "the password is too short. It should be equal or longer than %i characters!",
+ "%08X: %s - check_password_restrictions: "
+ "the password is too short. It should be equal or longer than %u characters!",
+ W_ERROR_V(WERR_PASSWORD_RESTRICTION),
+ ldb_strerror(ret),
io->ac->status->domain_data.minPwdLength);
-
io->ac->status->reject_reason = SAM_PWD_CHANGE_PASSWORD_TOO_SHORT;
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ return ret;
case SAMR_VALIDATION_STATUS_NOT_COMPLEX_ENOUGH:
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
- "the password does not meet the complexity criterias!");
+ "%08X: %s - check_password_restrictions: "
+ "the password does not meet the complexity criterias!",
+ W_ERROR_V(WERR_PASSWORD_RESTRICTION),
+ ldb_strerror(ret));
io->ac->status->reject_reason = SAM_PWD_CHANGE_NOT_COMPLEX;
-
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ return ret;
default:
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
- "the password doesn't fit by a certain reason!");
-
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ "%08X: %s - check_password_restrictions: "
+ "the password doesn't fit by a certain reason!",
+ W_ERROR_V(WERR_PASSWORD_RESTRICTION),
+ ldb_strerror(ret));
+ return ret;
}
}
@@ -1548,13 +1566,14 @@ static int check_password_restrictions(struct setup_password_fields_io *io)
for (i = 0; i < io->o.nt_history_len; i++) {
ret = memcmp(io->n.nt_hash, io->o.nt_history[i].hash, 16);
if (ret == 0) {
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
- "the password was already used (in history)!");
-
+ "%08X: %s - check_password_restrictions: "
+ "the password was already used (in history)!",
+ W_ERROR_V(WERR_PASSWORD_RESTRICTION),
+ ldb_strerror(ret));
io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
-
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ return ret;
}
}
}
@@ -1566,39 +1585,49 @@ static int check_password_restrictions(struct setup_password_fields_io *io)
for (i = 0; i < io->o.lm_history_len; i++) {
ret = memcmp(io->n.nt_hash, io->o.lm_history[i].hash, 16);
if (ret == 0) {
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
- "the password was already used (in history)!");
-
+ "%08X: %s - check_password_restrictions: "
+ "the password was already used (in history)!",
+ W_ERROR_V(WERR_PASSWORD_RESTRICTION),
+ ldb_strerror(ret));
io->ac->status->reject_reason = SAM_PWD_CHANGE_PWD_IN_HISTORY;
-
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ return ret;
}
}
}
/* are all password changes disallowed? */
if (io->ac->status->domain_data.pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
- "password changes disabled!");
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ "%08X: %s - check_password_restrictions: "
+ "password changes disabled!",
+ W_ERROR_V(WERR_PASSWORD_RESTRICTION),
+ ldb_strerror(ret));
+ return ret;
}
/* can this user change the password? */
if (io->u.userAccountControl & UF_PASSWD_CANT_CHANGE) {
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
- "password can't be changed on this account!");
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ "%08X: %s - check_password_restrictions: "
+ "password can't be changed on this account!",
+ W_ERROR_V(WERR_PASSWORD_RESTRICTION),
+ ldb_strerror(ret));
+ return ret;
}
/* Password minimum age: yes, this is a minus. The ages are in negative 100nsec units! */
if (io->u.pwdLastSet - io->ac->status->domain_data.minPwdAge > io->g.last_set) {
+ ret = LDB_ERR_CONSTRAINT_VIOLATION;
ldb_asprintf_errstring(ldb,
- "check_password_restrictions: "
- "password is too young to change!");
- return LDB_ERR_CONSTRAINT_VIOLATION;
+ "%08X: %s - check_password_restrictions: "
+ "password is too young to change!",
+ W_ERROR_V(WERR_PASSWORD_RESTRICTION),
+ ldb_strerror(ret));
+ return ret;
}
return LDB_SUCCESS;
@@ -1878,15 +1907,6 @@ static int setup_io(struct ph_context *ac,
return LDB_ERR_UNWILLING_TO_PERFORM;
}
- /* refuse the change if someone wants to compare against both
- * hashes at the same time for a "password modify" operation... */
- if (io->og.nt_hash && io->og.lm_hash) {
- ldb_asprintf_errstring(ldb,
- "setup_io: "
- "it's only allowed to provide the old password in hash format as 'unicodePwd' or as 'dBCSPwd'");
- return LDB_ERR_UNWILLING_TO_PERFORM;
- }
-
/* Decides if we have a password modify or password reset operation */
if (ac->req->operation == LDB_ADD) {
/* On "add" we have only "password reset" */
@@ -2440,10 +2460,18 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
++rep_attr_cnt;
}
if ((passwordAttr->num_values != 1) &&
- (passwordAttr->flags != LDB_FLAG_MOD_REPLACE)) {
+ (passwordAttr->flags == LDB_FLAG_MOD_ADD)) {
+ talloc_free(ac);
+ ldb_asprintf_errstring(ldb,
+ "'%s' attribute must have exactly one value on add operations!",
+ *l);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if ((passwordAttr->num_values > 1) &&
+ (passwordAttr->flags == LDB_FLAG_MOD_DELETE)) {
talloc_free(ac);
ldb_asprintf_errstring(ldb,
- "'%s' attributes must have exactly one value!",
+ "'%s' attribute must have zero or one value(s) on delete operations!",
*l);
return LDB_ERR_CONSTRAINT_VIOLATION;
}
diff --git a/source4/dsdb/tests/python/passwords.py b/source4/dsdb/tests/python/passwords.py
index a8a19e1..314d71b 100755
--- a/source4/dsdb/tests/python/passwords.py
+++ b/source4/dsdb/tests/python/passwords.py
@@ -86,27 +86,50 @@ class PasswordTests(samba.tests.TestCase):
"objectclass": ["user", "person"],
"sAMAccountName": "testuser"})
- # Tests a password change when we don't have a password yet
+ # Tests a password change when we don't have any password yet with a
+ # wrong old password
try:
self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
delete: userPassword
-userPassword: thatsAcomplPASS1
+userPassword: noPassword
add: userPassword
userPassword: thatsAcomplPASS2
""")
self.fail()
- except LdbError, (num, _):
+ except LdbError, (num, msg):
self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
-
- # Sets the initial user password and enables the account
+ # Windows (2008 at least) seems to have some small bug here: it
+ # returns "0000056A" on longer (always wrong) previous passwords.
+ self.assertTrue('00000056' in msg)
+
+ # Sets the initial user password with a "special" password change
+ # I think that this internally is a password set operation and it can
+ # only be performed by someone which has password set privileges on the
+ # account (at least in s4 we do handle it like that).
self.ldb.modify_ldif("""
dn: cn=testuser,cn=users,""" + self.base_dn + """
changetype: modify
-replace: userPassword
+delete: userPassword
+add: userPassword
+userPassword: thatsAcomplPASS1
+""")
+
+ # But in the other way around this special syntax doesn't work
+ try:
+ self.ldb.modify_ldif("""
+dn: cn=testuser,cn=users,""" + self.base_dn + """
+changetype: modify
+delete: userPassword
userPassword: thatsAcomplPASS1
+add: userPassword
""")
+ self.fail()
+ except LdbError, (num, _):
+ self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+
+ # Enables the user account
self.ldb.enable_account("(sAMAccountName=testuser)")
# Open a second LDB connection with the user credentials. Use the
@@ -186,8 +209,9 @@ add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS4\"".encode('utf-16-le')) + """
""")
self.fail()
- except LdbError, (num, _):
+ except LdbError, (num, msg):
self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+ self.assertTrue('00000056' in msg)
# A change to the same password again will not work (password history)
try:
@@ -200,8 +224,9 @@ add: unicodePwd
unicodePwd:: """ + base64.b64encode("\"thatsAcomplPASS2\"".encode('utf-16-le')) + """
""")
self.fail()
- except LdbError, (num, _):
+ except LdbError, (num, msg):
self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+ self.assertTrue('0000052D' in msg)
def test_dBCSPwd_hash_set(self):
print "Performs a password hash set operation on 'dBCSPwd' which should be prevented"
@@ -270,8 +295,9 @@ add: userPassword
userPassword: thatsAcomplPASS4
""")
self.fail()
- except LdbError, (num, _):
+ except LdbError, (num, msg):
self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+ self.assertTrue('00000056' in msg)
# A change to the same password again will not work (password history)
try:
@@ -284,8 +310,9 @@ add: userPassword
userPassword: thatsAcomplPASS2
""")
self.fail()
- except LdbError, (num, _):
+ except LdbError, (num, msg):
self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+ self.assertTrue('0000052D' in msg)
def test_clearTextPassword_clear_set(self):
print "Performs a password cleartext set operation on 'clearTextPassword'"
@@ -333,10 +360,11 @@ add: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS4".encode('utf-16-le')) + """
""")
self.fail()
- except LdbError, (num, _):
+ except LdbError, (num, msg):
# "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
if num != ERR_NO_SUCH_ATTRIBUTE:
self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+ self.assertTrue('00000056' in msg)
# A change to the same password again will not work (password history)
try:
@@ -349,10 +377,11 @@ add: clearTextPassword
clearTextPassword:: """ + base64.b64encode("thatsAcomplPASS2".encode('utf-16-le')) + """
""")
self.fail()
- except LdbError, (num, _):
+ except LdbError, (num, msg):
# "NO_SUCH_ATTRIBUTE" is returned by Windows -> ignore it
if num != ERR_NO_SUCH_ATTRIBUTE:
self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
+ self.assertTrue('0000052D' in msg)
def test_failures(self):
print "Performs some failure testing"
diff --git a/source4/ldap_server/ldap_backend.c b/source4/ldap_server/ldap_backend.c
index d5b1a39..e0f9ee1 100644
--- a/source4/ldap_server/ldap_backend.c
+++ b/source4/ldap_server/ldap_backend.c
@@ -45,6 +45,19 @@ static int map_ldb_error(TALLOC_CTX *mem_ctx, int ldb_err,
{
WERROR err;
+ /* Certain LDB modules need to return very special WERROR codes. Proof
+ * for them here and if they exist skip the rest of the mapping. */
+ if (add_err_string != NULL) {
+ char *endptr;
+ strtol(add_err_string, &endptr, 16);
+ if (endptr != add_err_string) {
+ *errstring = add_err_string;
+ return ldb_err;
+ }
+ }
+
+ /* Otherwise we calculate here a generic, but appropriate WERROR. */
+
switch (ldb_err) {
case LDB_SUCCESS:
err = WERR_OK;
@@ -165,7 +178,7 @@ static int map_ldb_error(TALLOC_CTX *mem_ctx, int ldb_err,
break;
}
- *errstring = talloc_asprintf(mem_ctx, "%08x: %s", W_ERROR_V(err),
+ *errstring = talloc_asprintf(mem_ctx, "%08X: %s", W_ERROR_V(err),
ldb_strerror(ldb_err));
if (add_err_string != NULL) {
*errstring = talloc_asprintf(mem_ctx, "%s - %s", *errstring,
--
Samba Shared Repository
More information about the samba-cvs
mailing list