s4:dsdb Allow unicodePwd to be set when adding a user
authorAndrew Bartlett <abartlet@samba.org>
Thu, 9 Jul 2009 04:53:26 +0000 (14:53 +1000)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 9 Jul 2009 04:56:14 +0000 (14:56 +1000)
Windows 7 sets it's join password using the unicodePwd attribute (as a
quoted, utf16 string), and does so during the LDAPAdd of the object.
Previously, this code only handled unicodePwd for modifies.

Andrew Bartlett

source4/dsdb/samdb/ldb_modules/password_hash.c

index 5a9926b6d18c6e8113512e346b69b77941db3191..44b7ef91e9f92b5aa5a3c83fdd783bcc31b87d59 100644 (file)
@@ -1432,6 +1432,67 @@ static int setup_password_fields(struct setup_password_fields_io *io)
        return LDB_SUCCESS;
 }
 
+static int setup_io(struct ph_context *ac, 
+                   const struct ldb_message *new_msg, 
+                   const struct ldb_message *searched_msg, 
+                   struct setup_password_fields_io *io) 
+{ 
+       const struct ldb_val *quoted_utf16;
+       struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+
+       ZERO_STRUCTP(io);
+
+       /* Some operations below require kerberos contexts */
+       if (smb_krb5_init_context(ac,
+                                 ldb_get_event_context(ldb),
+                                 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
+                                 &io->smb_krb5_context) != 0) {
+               return LDB_ERR_OPERATIONS_ERROR;
+       }
+
+       io->ac                          = ac;
+       io->domain                      = ac->domain;
+
+       io->u.user_account_control      = samdb_result_uint(searched_msg, "userAccountControl", 0);
+       io->u.sAMAccountName            = samdb_result_string(searched_msg, "samAccountName", NULL);
+       io->u.user_principal_name       = samdb_result_string(searched_msg, "userPrincipalName", NULL);
+       io->u.is_computer               = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
+
+       io->n.cleartext_utf8            = ldb_msg_find_ldb_val(new_msg, "userPassword");
+       io->n.cleartext_utf16           = ldb_msg_find_ldb_val(new_msg, "clearTextPassword");
+
+       /* this rather strange looking piece of code is there to
+          handle a ldap client setting a password remotely using the
+          unicodePwd ldap field. The syntax is that the password is
+          in UTF-16LE, with a " at either end. Unfortunately the
+          unicodePwd field is also used to store the nt hashes
+          internally in Samba, and is used in the nt hash format on
+          the wire in DRS replication, so we have a single name for
+          two distinct values. The code below leaves us with a small
+          chance (less than 1 in 2^32) of a mixup, if someone manages
+          to create a MD4 hash which starts and ends in 0x22 0x00, as
+          that would then be treated as a UTF16 password rather than
+          a nthash */
+       quoted_utf16                    = ldb_msg_find_ldb_val(new_msg, "unicodePwd");
+       if (quoted_utf16 && 
+           quoted_utf16->length >= 4 &&
+           quoted_utf16->data[0] == '"' && 
+           quoted_utf16->data[1] == 0 && 
+           quoted_utf16->data[quoted_utf16->length-2] == '"' && 
+           quoted_utf16->data[quoted_utf16->length-1] == 0) {
+               io->n.quoted_utf16.data = talloc_memdup(io->ac, quoted_utf16->data+2, quoted_utf16->length-4);
+               io->n.quoted_utf16.length = quoted_utf16->length-4;
+               io->n.cleartext_utf16 = &io->n.quoted_utf16;
+               io->n.nt_hash = NULL;
+       } else {
+               io->n.nt_hash           = samdb_result_hash(io->ac, new_msg, "unicodePwd");
+       }
+
+       io->n.lm_hash                   = samdb_result_hash(io->ac, new_msg, "dBCSPwd");
+
+       return LDB_SUCCESS;
+}
+
 static struct ph_context *ph_init_context(struct ldb_module *module,
                                          struct ldb_request *req)
 {
@@ -1743,50 +1804,32 @@ static int password_hash_add_do_add(struct ph_context *ac)
 {
        struct ldb_context *ldb;
        struct ldb_request *down_req;
-       struct smb_krb5_context *smb_krb5_context;
        struct ldb_message *msg;
        struct setup_password_fields_io io;
        int ret;
 
-       ldb = ldb_module_get_ctx(ac->module);
+       /* Prepare the internal data structure containing the passwords */
+       ret = setup_io(ac, ac->req->op.add.message, ac->req->op.add.message, &io);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
 
        msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
        if (msg == NULL) {
                return LDB_ERR_OPERATIONS_ERROR;
        }
 
-       /* Some operations below require kerberos contexts */
-       if (smb_krb5_init_context(ac,
-                                 ldb_get_event_context(ldb),
-                                 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
-                                 &smb_krb5_context) != 0) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       ZERO_STRUCT(io);
-       io.ac                           = ac;
-       io.domain                       = ac->domain;
-       io.smb_krb5_context             = smb_krb5_context;
-
-       io.u.user_account_control       = samdb_result_uint(msg, "userAccountControl", 0);
-       io.u.sAMAccountName             = samdb_result_string(msg, "samAccountName", NULL);
-       io.u.user_principal_name        = samdb_result_string(msg, "userPrincipalName", NULL);
-       io.u.is_computer                = ldb_msg_check_string_attribute(msg, "objectClass", "computer");
-
-       io.n.cleartext_utf8             = ldb_msg_find_ldb_val(msg, "userPassword");
-       io.n.cleartext_utf16            = ldb_msg_find_ldb_val(msg, "clearTextPassword");
-       io.n.nt_hash                    = samdb_result_hash(io.ac, msg, "unicodePwd");
-       io.n.lm_hash                    = samdb_result_hash(io.ac, msg, "dBCSPwd");
-
-       /* remove attributes */
-       if (io.n.cleartext_utf8) ldb_msg_remove_attr(msg, "userPassword");
-       if (io.n.cleartext_utf16) ldb_msg_remove_attr(msg, "clearTextPassword");
-       if (io.n.nt_hash) ldb_msg_remove_attr(msg, "unicodePwd");
-       if (io.n.lm_hash) ldb_msg_remove_attr(msg, "dBCSPwd");
+       /* remove attributes that we just read into 'io' */
+       ldb_msg_remove_attr(msg, "userPassword");
+       ldb_msg_remove_attr(msg, "clearTextPassword");
+       ldb_msg_remove_attr(msg, "unicodePwd");
+       ldb_msg_remove_attr(msg, "dBCSPwd");
        ldb_msg_remove_attr(msg, "pwdLastSet");
        io.o.kvno = samdb_result_uint(msg, "msDs-KeyVersionNumber", 1) - 1;
        ldb_msg_remove_attr(msg, "msDs-KeyVersionNumber");
 
+       ldb = ldb_module_get_ctx(ac->module);
+
        ret = setup_password_fields(&io);
        if (ret != LDB_SUCCESS) {
                return ret;
@@ -2096,12 +2139,9 @@ static int password_hash_mod_do_mod(struct ph_context *ac)
 {
        struct ldb_context *ldb;
        struct ldb_request *mod_req;
-       struct smb_krb5_context *smb_krb5_context;
        struct ldb_message *msg;
-       struct ldb_message *orig_msg;
-       struct ldb_message *searched_msg;
+       const struct ldb_message *searched_msg;
        struct setup_password_fields_io io;
-       const struct ldb_val *quoted_utf16;
        int ret;
 
        ldb = ldb_module_get_ctx(ac->module);
@@ -2115,59 +2155,18 @@ static int password_hash_mod_do_mod(struct ph_context *ac)
        /* modify dn */
        msg->dn = ac->req->op.mod.message->dn;
 
-       /* Some operations below require kerberos contexts */
-       if (smb_krb5_init_context(ac,
-                                 ldb_get_event_context(ldb),
-                                 (struct loadparm_context *)ldb_get_opaque(ldb, "loadparm"),
-                                 &smb_krb5_context) != 0) {
-               return LDB_ERR_OPERATIONS_ERROR;
-       }
-
-       orig_msg        = discard_const(ac->req->op.mod.message);
-       searched_msg    = ac->search_res->message;
-
-       ZERO_STRUCT(io);
-       io.ac                           = ac;
-       io.domain                       = ac->domain;
-       io.smb_krb5_context             = smb_krb5_context;
-
-       io.u.user_account_control       = samdb_result_uint(searched_msg, "userAccountControl", 0);
-       io.u.sAMAccountName             = samdb_result_string(searched_msg, "samAccountName", NULL);
-       io.u.user_principal_name        = samdb_result_string(searched_msg, "userPrincipalName", NULL);
-       io.u.is_computer                = ldb_msg_check_string_attribute(searched_msg, "objectClass", "computer");
-
-       io.n.cleartext_utf8             = ldb_msg_find_ldb_val(orig_msg, "userPassword");
-       io.n.cleartext_utf16            = ldb_msg_find_ldb_val(orig_msg, "clearTextPassword");
-
-       /* this rather strange looking piece of code is there to
-          handle a ldap client setting a password remotely using the
-          unicodePwd ldap field. The syntax is that the password is
-          in UTF-16LE, with a " at either end. Unfortunately the
-          unicodePwd field is also used to store the nt hashes
-          internally in Samba, and is used in the nt hash format on
-          the wire in DRS replication, so we have a single name for
-          two distinct values. The code below leaves us with a small
-          chance (less than 1 in 2^32) of a mixup, if someone manages
-          to create a MD4 hash which starts and ends in 0x22 0x00, as
-          that would then be treated as a UTF16 password rather than
-          a nthash */
-       quoted_utf16                    = ldb_msg_find_ldb_val(orig_msg, "unicodePwd");
-       if (quoted_utf16 && 
-           quoted_utf16->length >= 4 &&
-           quoted_utf16->data[0] == '"' && 
-           quoted_utf16->data[1] == 0 && 
-           quoted_utf16->data[quoted_utf16->length-2] == '"' && 
-           quoted_utf16->data[quoted_utf16->length-1] == 0) {
-               io.n.quoted_utf16.data = talloc_memdup(orig_msg, quoted_utf16->data+2, quoted_utf16->length-4);
-               io.n.quoted_utf16.length = quoted_utf16->length-4;
-               io.n.cleartext_utf16 = &io.n.quoted_utf16;
-               io.n.nt_hash = NULL;
-       } else {
-               io.n.nt_hash            = samdb_result_hash(io.ac, orig_msg, "unicodePwd");
+       /* Prepare the internal data structure containing the passwords */
+       ret = setup_io(ac, 
+                      ac->req->op.mod.message, 
+                      ac->search_res->message, 
+                      &io);
+       if (ret != LDB_SUCCESS) {
+               return ret;
        }
+       
+       searched_msg = ac->search_res->message;
 
-       io.n.lm_hash                    = samdb_result_hash(io.ac, orig_msg, "dBCSPwd");
-
+       /* Fill in some final details (only relevent once the password has been set) */
        io.o.kvno                       = samdb_result_uint(searched_msg, "msDs-KeyVersionNumber", 0);
        io.o.nt_history_len             = samdb_result_hashes(io.ac, searched_msg, "ntPwdHistory", &io.o.nt_history);
        io.o.lm_history_len             = samdb_result_hashes(io.ac, searched_msg, "lmPwdHistory", &io.o.lm_history);