s4:password_hash LDB module - allow empty ("") passwords
authorMatthias Dieter Wallnöfer <mdw@samba.org>
Thu, 2 Dec 2010 08:55:56 +0000 (09:55 +0100)
committerMatthias Dieter Wallnöfer <mdw@samba.org>
Thu, 2 Dec 2010 10:51:06 +0000 (11:51 +0100)
This seems to have been broken some time ago - till someone on the
mailing list noticed it.

I've also added a testsuite (and some additional SamDB python helpers) which
should prove this.

source4/dsdb/samdb/ldb_modules/password_hash.c
source4/dsdb/tests/python/passwords.py
source4/scripting/python/samba/samdb.py

index 35d2729297008ca35c9113df2334749eb232f02e..4e2e96fa6507f92c82fee4b23f07b914e84833ae 100644 (file)
@@ -1298,18 +1298,22 @@ static int setup_given_passwords(struct setup_password_fields_io *io,
                                           (void *)&cleartext_utf16_blob->data,
                                           &cleartext_utf16_blob->length,
                                           false)) {
-                       talloc_free(cleartext_utf16_blob);
-                       ldb_asprintf_errstring(ldb,
-                                              "setup_password_fields: "
-                                              "failed to generate UTF16 password from cleartext UTF8 password for user %s", io->u.sAMAccountName);
-                       return LDB_ERR_CONSTRAINT_VIOLATION;
-               } else {
-                       g->cleartext_utf16 = cleartext_utf16_blob;
+                       if (g->cleartext_utf8->length != 0) {
+                               talloc_free(cleartext_utf16_blob);
+                               ldb_asprintf_errstring(ldb,
+                                                      "setup_password_fields: "
+                                                      "failed to generate UTF16 password from cleartext UTF8 one for user '%s'!",
+                                                      io->u.sAMAccountName);
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       } else {
+                               /* passwords with length "0" are valid! */
+                               cleartext_utf16_blob->data = NULL;
+                               cleartext_utf16_blob->length = 0;
+                       }
                }
+               g->cleartext_utf16 = cleartext_utf16_blob;
        } else if (g->cleartext_utf16) {
-               char *cleartext_utf8_str;
                struct ldb_val *cleartext_utf8_blob;
-               size_t converted_pw_len;
 
                cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
                if (!cleartext_utf8_blob) {
@@ -1319,20 +1323,25 @@ static int setup_given_passwords(struct setup_password_fields_io *io,
                                           CH_UTF16MUNGED, CH_UTF8,
                                           g->cleartext_utf16->data,
                                           g->cleartext_utf16->length,
-                                          (void *)&cleartext_utf8_str,
-                                          &converted_pw_len, false)) {
-                       /* We must bail out here, the input wasn't even a
-                        * multiple of 2 bytes */
-                       talloc_free(cleartext_utf8_blob);
-                       ldb_asprintf_errstring(ldb,
-                                              "setup_password_fields: "
-                                              "UTF16 password for user %s had odd length (length must be a multiple of 2)", io->u.sAMAccountName);
-                       return LDB_ERR_CONSTRAINT_VIOLATION;
-               } else {
-                       *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str,
-                                                              converted_pw_len);
-                       g->cleartext_utf8 = cleartext_utf8_blob;
+                                          (void *)&cleartext_utf8_blob->data,
+                                          &cleartext_utf8_blob->length,
+                                          false)) {
+                       if (g->cleartext_utf16->length != 0) {
+                               /* We must bail out here, the input wasn't even
+                                * a multiple of 2 bytes */
+                               talloc_free(cleartext_utf8_blob);
+                               ldb_asprintf_errstring(ldb,
+                                                      "setup_password_fields: "
+                                                      "failed to generate UTF8 password from cleartext UTF 16 one for user '%s' - the latter had odd length (length must be a multiple of 2)!",
+                                                      io->u.sAMAccountName);
+                               return LDB_ERR_CONSTRAINT_VIOLATION;
+                       } else {
+                               /* passwords with length "0" are valid! */
+                               cleartext_utf8_blob->data = NULL;
+                               cleartext_utf8_blob->length = 0;
+                       }
                }
+               g->cleartext_utf8 = cleartext_utf8_blob;
        }
 
        if (g->cleartext_utf16) {
index 198b314cc05417b0002825dc468a0f187c6ac572..e43298e34d81813c1e82f3d9a31d83e040c71c00 100755 (executable)
@@ -887,6 +887,25 @@ userPassword: thatsAcomplPASS4
         # Reset the test "dSHeuristics" (reactivate "userPassword" pwd changes)
         ldb.set_dsheuristics("000000001")
 
+    def test_zero_length(self):
+        # Get the old "minPwdLength"
+        minPwdLength = ldb.get_minPwdLength()
+        # Set it temporarely to "0"
+        ldb.set_minPwdLength("0")
+
+        # Get the old "pwdProperties"
+        pwdProperties = ldb.get_pwdProperties()
+        # Set them temporarely to "0" (to deactivate eventually the complexity)
+        ldb.set_pwdProperties("0")
+
+        ldb.setpassword("(sAMAccountName=testuser)", "")
+
+        # Reset the "pwdProperties" as they were before
+        ldb.set_pwdProperties(pwdProperties)
+
+        # Reset the "minPwdLength" as it was before
+        ldb.set_minPwdLength(minPwdLength)
+
     def tearDown(self):
         super(PasswordTests, self).tearDown()
         delete_force(self.ldb, "cn=testuser,cn=users," + self.base_dn)
index a7f474d1341d7c2545399348dc1d32710dd6a144..99f141e664233d24f231f5e70f9a5c354290e64a 100644 (file)
@@ -637,6 +637,36 @@ accountExpires: %u
         else:
             return res[0]["minPwdAge"][0]
 
+    def set_minPwdLength(self, value):
+        m = ldb.Message()
+        m.dn = ldb.Dn(self, self.domain_dn())
+        m["minPwdLength"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdLength")
+        self.modify(m)
+
+    def get_minPwdLength(self):
+        res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdLength"])
+        if len(res) == 0:
+            return None
+        elif not "minPwdLength" in res[0]:
+            return None
+        else:
+            return res[0]["minPwdLength"][0]
+
+    def set_pwdProperties(self, value):
+        m = ldb.Message()
+        m.dn = ldb.Dn(self, self.domain_dn())
+        m["pwdProperties"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "pwdProperties")
+        self.modify(m)
+
+    def get_pwdProperties(self):
+        res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["pwdProperties"])
+        if len(res) == 0:
+            return None
+        elif not "pwdProperties" in res[0]:
+            return None
+        else:
+            return res[0]["pwdProperties"][0]
+
     def set_dsheuristics(self, dsheuristics):
         m = ldb.Message()
         m.dn = ldb.Dn(self, "CN=Directory Service,CN=Windows NT,CN=Services,%s"