Create a 'straight paper path' for UTF16 passwords.
authorAndrew Bartlett <abartlet@samba.org>
Thu, 16 Oct 2008 01:48:16 +0000 (12:48 +1100)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 16 Oct 2008 01:48:16 +0000 (12:48 +1100)
This uses a virtual attribute 'clearTextPassword' (name chosen to
match references in MS-SAMR) that contains the length-limited blob
containing an allegidly UTF16 password.  This ensures we do no
validation or filtering of the password before we get a chance to MD4
it.  We can then do the required munging into UTF8, and in future
implement the rules Microsoft has provided us with for invalid inputs.

All layers in the process now deal with the strings as length-limited
inputs, incluing the krb5 string2key calls.

This commit also includes a small change to samdb_result_passwords()
to ensure that LM passwords are not returned to the application logic
if LM authentication is disabled.

The objectClass module has been modified to allow the
clearTextPassword attribute to pass down the stack.

Andrew Bartlett

source4/auth/ntlm/auth_sam.c
source4/dsdb/common/util.c
source4/dsdb/samdb/ldb_modules/objectclass.c
source4/dsdb/samdb/ldb_modules/password_hash.c
source4/kdc/kpasswdd.c
source4/libcli/auth/smbencrypt.c
source4/librpc/idl/drsblobs.idl
source4/rpc_server/lsa/dcesrv_lsa.c
source4/rpc_server/netlogon/dcerpc_netlogon.c
source4/rpc_server/samr/samr_password.c

index ac36d4f1efd32fcf2c880e2dd28f4c1fa47801c0..78429106f634732b79e45858e1d48f3774231501 100644 (file)
@@ -248,7 +248,7 @@ static NTSTATUS authsam_authenticate(struct auth_context *auth_context,
                }
        }
 
-       nt_status = samdb_result_passwords(mem_ctx, msgs[0], &lm_pwd, &nt_pwd);
+       nt_status = samdb_result_passwords(mem_ctx, auth_context->lp_ctx, msgs[0], &lm_pwd, &nt_pwd);
        NT_STATUS_NOT_OK_RETURN(nt_status);
 
        nt_status = authsam_password_ok(auth_context, mem_ctx, 
index 5b93efb316b88927777bcb59544bd2fb6b86b004..0a87c5ffe12f946d5b609ad907dc7331cf5df605 100644 (file)
@@ -26,6 +26,7 @@
 #include "ldb.h"
 #include "ldb_errors.h"
 #include "../lib/util/util_ldb.h"
+#include "../lib/crypto/crypto.h"
 #include "dsdb/samdb/samdb.h"
 #include "libcli/security/security.h"
 #include "librpc/gen_ndr/ndr_security.h"
@@ -571,7 +572,7 @@ uint_t samdb_result_hashes(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
        return count;
 }
 
-NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg, 
+NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, struct ldb_message *msg, 
                                struct samr_Password **lm_pwd, struct samr_Password **nt_pwd) 
 {
        struct samr_Password *lmPwdHash, *ntPwdHash;
@@ -587,14 +588,21 @@ NTSTATUS samdb_result_passwords(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
                }
        }
        if (lm_pwd) {
-               int num_lm;
-               num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash);
-               if (num_lm == 0) {
-                       *lm_pwd = NULL;
-               } else if (num_lm > 1) {
-                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               /* Ensure that if we have turned off LM
+                * authentication, that we never use the LM hash, even
+                * if we store it */
+               if (lp_lanman_auth(lp_ctx)) {
+                       int num_lm;
+                       num_lm = samdb_result_hashes(mem_ctx, msg, "dBCSPwd", &lmPwdHash);
+                       if (num_lm == 0) {
+                               *lm_pwd = NULL;
+                       } else if (num_lm > 1) {
+                               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+                       } else {
+                               *lm_pwd = &lmPwdHash[0];
+                       }
                } else {
-                       *lm_pwd = &lmPwdHash[0];
+                       *lm_pwd = NULL;
                }
        }
        return NT_STATUS_OK;
@@ -1531,7 +1539,7 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
                            struct ldb_dn *user_dn,
                            struct ldb_dn *domain_dn,
                            struct ldb_message *mod,
-                           const char *new_pass,
+                           const DATA_BLOB *new_password,
                            struct samr_Password *lmNewHash, 
                            struct samr_Password *ntNewHash,
                            bool user_change,
@@ -1632,40 +1640,47 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
                *_dominfo = dominfo;
        }
 
-       if (restrictions && new_pass) {
-
+       if (restrictions && new_password) {
+               char *new_pass;
+               
                /* check the various password restrictions */
-               if (restrictions && minPwdLength > strlen_m(new_pass)) {
+               if (restrictions && minPwdLength > utf16_len_n(new_password->data, (new_password->length / 2))) {
                        if (reject_reason) {
                                *reject_reason = SAMR_REJECT_TOO_SHORT;
                        }
                        return NT_STATUS_PASSWORD_RESTRICTION;
                }
+
+               /* Create the NT hash */
+               mdfour(local_ntNewHash.hash, new_password->data, new_password->length);
                
-               /* possibly check password complexity */
-               if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
-                   !samdb_password_complexity_ok(new_pass)) {
-                       if (reject_reason) {
-                               *reject_reason = SAMR_REJECT_COMPLEXITY;
+               ntNewHash = &local_ntNewHash;
+
+               /* Only check complexity if we can convert it at all.  Assuming unconvertable passwords are 'strong' */
+               if (convert_string_talloc(mem_ctx, lp_iconv_convenience(ldb_get_opaque(ctx, "loadparm")), 
+                                         CH_UTF16, CH_UNIX, 
+                                         new_password->data, new_password->length, 
+                                         (void **)&new_pass) != -1) {
+                       
+                       
+                       /* possibly check password complexity */
+                       if (restrictions && pwdProperties & DOMAIN_PASSWORD_COMPLEX &&
+                           !samdb_password_complexity_ok(new_pass)) {
+                               if (reject_reason) {
+                                       *reject_reason = SAMR_REJECT_COMPLEXITY;
+                               }
+                               return NT_STATUS_PASSWORD_RESTRICTION;
                        }
-                       return NT_STATUS_PASSWORD_RESTRICTION;
-               }
-               
-               /* compute the new nt and lm hashes */
-               if (E_deshash(new_pass, local_lmNewHash.hash)) {
-                       lmNewHash = &local_lmNewHash;
-               }
-               if (!E_md4hash(new_pass, local_ntNewHash.hash)) {
-                       /* If we can't convert this password to UCS2, then we should not accept it */
-                       if (reject_reason) {
-                               *reject_reason = SAMR_REJECT_OTHER;
+                       
+                       /* compute the new lm hashes (for checking history - case insenitivly!) */
+                       if (E_deshash(new_pass, local_lmNewHash.hash)) {
+                               lmNewHash = &local_lmNewHash;
                        }
-                       return NT_STATUS_PASSWORD_RESTRICTION;
+                       
                }
-               ntNewHash = &local_ntNewHash;
        }
 
-       if (user_change) {
+       if (restrictions && user_change) {
                /* are all password changes disallowed? */
                if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
                        if (reject_reason) {
@@ -1731,16 +1746,15 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
 #define CHECK_RET(x) do { if (x != 0) return NT_STATUS_NO_MEMORY; } while(0)
 
        /* the password is acceptable. Start forming the new fields */
-       if (new_pass) {
-               /* if we know the cleartext, then only set it.
+       if (new_password) {
+               /* if we know the cleartext UTF16 password, then set it.
                 * Modules in ldb will set all the appropriate
                 * hashes */
-               CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, 
-                                              "userPassword", new_pass));
+               CHECK_RET(ldb_msg_add_value(mod, "clearTextPassword", new_password, NULL));
        } else {
                /* We don't have the cleartext, so delete the old one
                 * and set what we have of the hashes */
-               CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "userPassword"));
+               CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "clearTextPassword"));
 
                if (lmNewHash) {
                        CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "dBCSPwd", lmNewHash));
@@ -1769,7 +1783,7 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
 */
 NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
                                const struct dom_sid *user_sid,
-                               const char *new_pass,
+                               const DATA_BLOB *new_pass,
                                struct samr_Password *lmNewHash, 
                                struct samr_Password *ntNewHash,
                                bool user_change,
index e610c358f6d014f8f096eddc51aeba990538389a..7d00851792c04053c2b5cb3628eda2d4ce8bcc81 100644 (file)
@@ -382,11 +382,17 @@ static int fix_attributes(struct ldb_context *ldb, const struct dsdb_schema *sch
        int i;
        for (i=0; i < msg->num_elements; i++) {
                const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(schema, msg->elements[i].name);
+               /* Add in a very special case for 'clearTextPassword',
+                * which is used for internal processing only, and is
+                * not presented in the schema */
                if (!attribute) {
-                       ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", msg->elements[i].name);
-                       return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
+                       if (strcasecmp(msg->elements[i].name, "clearTextPassword") != 0) {
+                               ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", msg->elements[i].name);
+                               return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
+                       }
+               } else {
+                       msg->elements[i].name = attribute->lDAPDisplayName;
                }
-               msg->elements[i].name = attribute->lDAPDisplayName;
        }
 
        return LDB_SUCCESS;
index e36de3c5c408e0f2b7851ba5bc7ce720f03c4188..c4451d1355f80522e79c6a4647cf48ceccb7065f 100644 (file)
@@ -109,7 +109,8 @@ struct setup_password_fields_io {
 
        /* new credentials */
        struct {
-               const char *cleartext;
+               const struct ldb_val *cleartext_utf8;
+               const struct ldb_val *cleartext_utf16;
                struct samr_Password *nt_hash;
                struct samr_Password *lm_hash;
        } n;
@@ -144,6 +145,9 @@ struct setup_password_fields_io {
        } g;
 };
 
+/* Get the NT hash, and fill it in as an entry in the password history, 
+   and specify it into io->g.nt_hash */
+
 static int setup_nt_fields(struct setup_password_fields_io *io)
 {
        uint32_t i;
@@ -181,6 +185,9 @@ static int setup_nt_fields(struct setup_password_fields_io *io)
        return LDB_SUCCESS;
 }
 
+/* Get the LANMAN hash, and fill it in as an entry in the password history, 
+   and specify it into io->g.lm_hash */
+
 static int setup_lm_fields(struct setup_password_fields_io *io)
 {
        uint32_t i;
@@ -220,6 +227,10 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
        Principal *salt_principal;
        krb5_salt salt;
        krb5_keyblock key;
+       krb5_data cleartext_data;
+
+       cleartext_data.data = io->n.cleartext_utf8->data;
+       cleartext_data.length = io->n.cleartext_utf8->length;
 
        /* Many, many thanks to lukeh@padl.com for this
         * algorithm, described in his Nov 10 2004 mail to
@@ -314,11 +325,11 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
         * create ENCTYPE_AES256_CTS_HMAC_SHA1_96 key out of
         * the salt and the cleartext password
         */
-       krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context,
-                                          ENCTYPE_AES256_CTS_HMAC_SHA1_96,
-                                          io->n.cleartext,
-                                          salt,
-                                          &key);
+       krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+                                               ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+                                               cleartext_data,
+                                               salt,
+                                               &key);
        if (krb5_ret) {
                ldb_asprintf_errstring(io->ac->module->ldb,
                                       "setup_kerberos_keys: "
@@ -339,11 +350,11 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
         * create ENCTYPE_AES128_CTS_HMAC_SHA1_96 key out of
         * the salt and the cleartext password
         */
-       krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context,
-                                          ENCTYPE_AES128_CTS_HMAC_SHA1_96,
-                                          io->n.cleartext,
-                                          salt,
-                                          &key);
+       krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+                                               ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+                                               cleartext_data,
+                                               salt,
+                                               &key);
        if (krb5_ret) {
                ldb_asprintf_errstring(io->ac->module->ldb,
                                       "setup_kerberos_keys: "
@@ -364,11 +375,11 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
         * create ENCTYPE_DES_CBC_MD5 key out of
         * the salt and the cleartext password
         */
-       krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context,
-                                          ENCTYPE_DES_CBC_MD5,
-                                          io->n.cleartext,
-                                          salt,
-                                          &key);
+       krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+                                               ENCTYPE_DES_CBC_MD5,
+                                               cleartext_data,
+                                               salt,
+                                               &key);
        if (krb5_ret) {
                ldb_asprintf_errstring(io->ac->module->ldb,
                                       "setup_kerberos_keys: "
@@ -389,11 +400,11 @@ static int setup_kerberos_keys(struct setup_password_fields_io *io)
         * create ENCTYPE_DES_CBC_CRC key out of
         * the salt and the cleartext password
         */
-       krb5_ret = krb5_string_to_key_salt(io->smb_krb5_context->krb5_context,
-                                          ENCTYPE_DES_CBC_CRC,
-                                          io->n.cleartext,
-                                          salt,
-                                          &key);
+       krb5_ret = krb5_string_to_key_data_salt(io->smb_krb5_context->krb5_context,
+                                               ENCTYPE_DES_CBC_CRC,
+                                               cleartext_data,
+                                               salt,
+                                               &key);
        if (krb5_ret) {
                ldb_asprintf_errstring(io->ac->module->ldb,
                                       "setup_kerberos_keys: "
@@ -648,7 +659,6 @@ static int setup_primary_wdigest(struct setup_password_fields_io *io,
        DATA_BLOB dns_domain;
        DATA_BLOB dns_domain_l;
        DATA_BLOB dns_domain_u;
-       DATA_BLOB cleartext;
        DATA_BLOB digest;
        DATA_BLOB delim;
        DATA_BLOB backslash;
@@ -929,8 +939,6 @@ static int setup_primary_wdigest(struct setup_password_fields_io *io,
        dns_domain_l            = data_blob_string_const(io->domain->dns_domain);
        dns_domain_u            = data_blob_string_const(io->domain->realm);
 
-       cleartext               = data_blob_string_const(io->n.cleartext);
-
        digest                  = data_blob_string_const("Digest");
 
        delim                   = data_blob_string_const(":");
@@ -956,7 +964,7 @@ static int setup_primary_wdigest(struct setup_password_fields_io *io,
                        MD5Update(&md5, wdigest[i].realm->data, wdigest[i].realm->length);
                }
                MD5Update(&md5, delim.data, delim.length);
-               MD5Update(&md5, cleartext.data, cleartext.length);
+               MD5Update(&md5, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length);
                MD5Final(pdb->hashes[i].hash, &md5);
        }
 
@@ -1011,7 +1019,7 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
        ZERO_STRUCT(zero16);
        ZERO_STRUCT(names);
 
-       if (!io->n.cleartext) {
+       if (!io->n.cleartext_utf8) {
                /* 
                 * when we don't have a cleartext password
                 * we can't setup a supplementalCredential value
@@ -1193,7 +1201,7 @@ static int setup_supplemental_field(struct setup_password_fields_io *io)
        if (pc) {
                *nc             = "CLEARTEXT";
 
-               pcb.cleartext   = io->n.cleartext;
+               pcb.cleartext   = *io->n.cleartext_utf16;
 
                ndr_err = ndr_push_struct_blob(&pcb_blob, io->ac, 
                                               lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")),
@@ -1285,58 +1293,97 @@ static int setup_password_fields(struct setup_password_fields_io *io)
 {
        bool ok;
        int ret;
-
+       ssize_t converted_pw_len;
+               
        /*
         * refuse the change if someone want to change the cleartext
         * and supply his own hashes at the same time...
         */
-       if (io->n.cleartext && (io->n.nt_hash || io->n.lm_hash)) {
+       if ((io->n.cleartext_utf8 || io->n.cleartext_utf16) && (io->n.nt_hash || io->n.lm_hash)) {
                ldb_asprintf_errstring(io->ac->module->ldb,
                                       "setup_password_fields: "
                                       "it's only allowed to set the cleartext password or the password hashes");
                return LDB_ERR_UNWILLING_TO_PERFORM;
        }
-
-       if (io->n.cleartext) {
-               struct samr_Password *hash;
-
-               hash = talloc(io->ac, struct samr_Password);
-               if (!hash) {
+       
+       if (io->n.cleartext_utf8 && io->n.cleartext_utf16) {
+               ldb_asprintf_errstring(io->ac->module->ldb,
+                                      "setup_password_fields: "
+                                      "it's only allowed to set the cleartext password as userPassword or clearTextPasssword, not both at once");
+               return LDB_ERR_UNWILLING_TO_PERFORM;
+       }
+       
+       if (io->n.cleartext_utf8) {
+               char **cleartext_utf16_str;
+               struct ldb_val *cleartext_utf16_blob;
+               io->n.cleartext_utf16 = cleartext_utf16_blob = talloc(io->ac, struct ldb_val);
+               if (!io->n.cleartext_utf16) {
                        ldb_oom(io->ac->module->ldb);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
-
-               /* compute the new nt hash */
-               ok = E_md4hash(io->n.cleartext, hash->hash);
-               if (ok) {
-                       io->n.nt_hash = hash;
-               } else {
+               converted_pw_len = convert_string_talloc(io->ac, lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")), 
+                                                        CH_UTF8, CH_UTF16, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length, 
+                                                        (void **)&cleartext_utf16_str);
+               if (converted_pw_len == -1) {
                        ldb_asprintf_errstring(io->ac->module->ldb,
                                               "setup_password_fields: "
-                                              "failed to generate nthash from cleartext password");
+                                              "failed to generate UTF16 password from cleartext UTF8 password");
+                       return LDB_ERR_OPERATIONS_ERROR;
+               }
+               *cleartext_utf16_blob = data_blob_const(cleartext_utf16_str, converted_pw_len);
+       } else if (io->n.cleartext_utf16) {
+               char *cleartext_utf8_str;
+               struct ldb_val *cleartext_utf8_blob;
+               io->n.cleartext_utf8 = cleartext_utf8_blob = talloc(io->ac, struct ldb_val);
+               if (!io->n.cleartext_utf8) {
+                       ldb_oom(io->ac->module->ldb);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
+               converted_pw_len = convert_string_talloc(io->ac, lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")), 
+                                                        CH_UTF16, CH_UTF8, io->n.cleartext_utf16->data, io->n.cleartext_utf16->length, 
+                                                        (void **)&cleartext_utf8_str);
+               if (converted_pw_len == -1) {
+                       /* We can't bail out entirely, as these unconvertable passwords are frustratingly valid */
+                       io->n.cleartext_utf8 = NULL;    
+                       talloc_free(cleartext_utf8_blob);
+               }
+               *cleartext_utf8_blob = data_blob_const(cleartext_utf8_str, converted_pw_len);
        }
-
-       if (io->n.cleartext) {
-               struct samr_Password *hash;
-
-               hash = talloc(io->ac, struct samr_Password);
-               if (!hash) {
+       if (io->n.cleartext_utf16) {
+               struct samr_Password *nt_hash;
+               nt_hash = talloc(io->ac, struct samr_Password);
+               if (!nt_hash) {
                        ldb_oom(io->ac->module->ldb);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
+               io->n.nt_hash = nt_hash;
 
-               /* compute the new lm hash */
-               ok = E_deshash(io->n.cleartext, hash->hash);
-               if (ok) {
-                       io->n.lm_hash = hash;
-               } else {
-                       talloc_free(hash->hash);
-               }
+               /* compute the new nt hash */
+               mdfour(nt_hash->hash, io->n.cleartext_utf16->data, io->n.cleartext_utf16->length);
        }
 
-       if (io->n.cleartext) {
+       if (io->n.cleartext_utf8) {
+               struct samr_Password *lm_hash;
+               char *cleartext_unix;
+               converted_pw_len = convert_string_talloc(io->ac, lp_iconv_convenience(ldb_get_opaque(io->ac->module->ldb, "loadparm")), 
+                                                        CH_UTF8, CH_UNIX, io->n.cleartext_utf8->data, io->n.cleartext_utf8->length, 
+                                                        (void **)&cleartext_unix);
+               if (converted_pw_len != -1) {
+                       lm_hash = talloc(io->ac, struct samr_Password);
+                       if (!lm_hash) {
+                               ldb_oom(io->ac->module->ldb);
+                               return LDB_ERR_OPERATIONS_ERROR;
+                       }
+                       
+                       /* compute the new lm hash.   */
+                       ok = E_deshash((char *)cleartext_unix, lm_hash->hash);
+                       if (ok) {
+                               io->n.lm_hash = lm_hash;
+                       } else {
+                               talloc_free(lm_hash->hash);
+                       }
+               }
+
                ret = setup_kerberos_keys(io);
                if (ret != 0) {
                        return ret;
@@ -1560,6 +1607,7 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
 {
        struct ph_context *ac;
        struct ldb_message_element *sambaAttr;
+       struct ldb_message_element *clearTextPasswordAttr;
        struct ldb_message_element *ntAttr;
        struct ldb_message_element *lmAttr;
        int ret;
@@ -1591,6 +1639,7 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
         * or LM hashes, then we don't need to make any changes.  */
 
        sambaAttr = ldb_msg_find_element(req->op.mod.message, "userPassword");
+       clearTextPasswordAttr = ldb_msg_find_element(req->op.mod.message, "clearTextPassword");
        ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd");
        lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd");
 
@@ -1611,6 +1660,10 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
                ldb_set_errstring(module->ldb, "mupltiple values for userPassword not allowed!\n");
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
+       if (clearTextPasswordAttr && clearTextPasswordAttr->num_values > 1) {
+               ldb_set_errstring(module->ldb, "mupltiple values for clearTextPassword not allowed!\n");
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
 
        if (ntAttr && (ntAttr->num_values > 1)) {
                ldb_set_errstring(module->ldb, "mupltiple values for unicodePwd not allowed!\n");
@@ -1626,6 +1679,11 @@ static int password_hash_add(struct ldb_module *module, struct ldb_request *req)
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
 
+       if (clearTextPasswordAttr && clearTextPasswordAttr->num_values == 0) {
+               ldb_set_errstring(module->ldb, "clearTextPassword must have a value!\n");
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
+
        if (ntAttr && (ntAttr->num_values == 0)) {
                ldb_set_errstring(module->ldb, "unicodePwd must have a value!\n");
                return LDB_ERR_CONSTRAINT_VIOLATION;
@@ -1687,12 +1745,14 @@ static int password_hash_add_do_add(struct ph_context *ac) {
        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                  = samdb_result_string(msg, "userPassword", NULL);
+       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) ldb_msg_remove_attr(msg, "userPassword");
+       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");
        ldb_msg_remove_attr(msg, "pwdLastSet");
@@ -1772,6 +1832,7 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
 {
        struct ph_context *ac;
        struct ldb_message_element *sambaAttr;
+       struct ldb_message_element *clearTextAttr;
        struct ldb_message_element *ntAttr;
        struct ldb_message_element *lmAttr;
        struct ldb_message *msg;
@@ -1802,13 +1863,16 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
        }
 
        sambaAttr = ldb_msg_find_element(req->op.mod.message, "userPassword");
+       clearTextAttr = ldb_msg_find_element(req->op.mod.message, "clearTextPassword");
        ntAttr = ldb_msg_find_element(req->op.mod.message, "unicodePwd");
        lmAttr = ldb_msg_find_element(req->op.mod.message, "dBCSPwd");
 
-       /* If no part of this touches the userPassword OR unicodePwd and/or dBCSPwd, then we don't
-        * need to make any changes.  For password changes/set there should
-        * be a 'delete' or a 'modify' on this attribute. */
-       if ((!sambaAttr) && (!ntAttr) && (!lmAttr)) {
+       /* If no part of this touches the userPassword OR
+        * clearTextPassword OR unicodePwd and/or dBCSPwd, then we
+        * don't need to make any changes.  For password changes/set
+        * there should be a 'delete' or a 'modify' on this
+        * attribute. */
+       if ((!sambaAttr) && (!clearTextAttr) && (!ntAttr) && (!lmAttr)) {
                return ldb_next_request(module, req);
        }
 
@@ -1817,6 +1881,9 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
        if (sambaAttr && (sambaAttr->num_values > 1)) {
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
+       if (clearTextAttr && (clearTextAttr->num_values > 1)) {
+               return LDB_ERR_CONSTRAINT_VIOLATION;
+       }
        if (ntAttr && (ntAttr->num_values > 1)) {
                return LDB_ERR_CONSTRAINT_VIOLATION;
        }
@@ -1839,6 +1906,7 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
        /* - remove any modification to the password from the first commit
         *   we will make the real modification later */
        if (sambaAttr) ldb_msg_remove_attr(msg, "userPassword");
+       if (clearTextAttr) ldb_msg_remove_attr(msg, "clearTextPassword");
        if (ntAttr) ldb_msg_remove_attr(msg, "unicodePwd");
        if (lmAttr) ldb_msg_remove_attr(msg, "dBCSPwd");
 
@@ -2028,7 +2096,8 @@ static int password_hash_mod_do_mod(struct ph_context *ac) {
        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                  = samdb_result_string(orig_msg, "userPassword", NULL);
+       io.n.cleartext_utf8             = ldb_msg_find_ldb_val(orig_msg, "userPassword");
+       io.n.cleartext_utf16            = ldb_msg_find_ldb_val(orig_msg, "clearTextPassword");
        io.n.nt_hash                    = samdb_result_hash(io.ac, orig_msg, "unicodePwd");
        io.n.lm_hash                    = samdb_result_hash(io.ac, orig_msg, "dBCSPwd");
 
index d662844c4eb287f3cbe4b87e70cce0b0f71b72e7..1336b0157ecc03eda7e7b915d2128b05d55419da 100644 (file)
@@ -175,7 +175,7 @@ static bool kpasswd_make_pwchange_reply(struct kdc_server *kdc,
 static bool kpasswdd_change_password(struct kdc_server *kdc,
                                     TALLOC_CTX *mem_ctx, 
                                     struct auth_session_info *session_info,
-                                    const char *password,
+                                    const DATA_BLOB *password,
                                     DATA_BLOB *reply)
 {
        NTSTATUS status;
@@ -219,6 +219,8 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
                                    DATA_BLOB *reply)
 {
        struct auth_session_info *session_info;
+       ssize_t pw_len;
+
        if (!NT_STATUS_IS_OK(gensec_session_info(gensec_security, 
                                                 &session_info))) {
                return kpasswdd_make_error_reply(kdc, mem_ctx, 
@@ -230,12 +232,20 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
        switch (version) {
        case KRB5_KPASSWD_VERS_CHANGEPW:
        {
-               char *password = talloc_strndup(mem_ctx, (const char *)input->data, input->length);
-               if (!password) {
+               DATA_BLOB password;
+               pw_len = convert_string_talloc(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx), 
+                                              CH_UTF8, CH_UTF16, 
+                                              (const char *)input->data, 
+                                              input->length,
+                                              (void **)&password.data);
+
+               if (pw_len == -1) {
                        return false;
                }
+               password.length = pw_len;
+       
                return kpasswdd_change_password(kdc, mem_ctx, session_info, 
-                                               password, reply);
+                                               &password, reply);
                break;
        }
        case KRB5_KPASSWD_VERS_SETPW:
@@ -248,7 +258,7 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
                krb5_context context = kdc->smb_krb5_context->krb5_context;
 
                ChangePasswdDataMS chpw;
-               char *password;
+               DATA_BLOB password;
 
                krb5_principal principal;
                char *set_password_on_princ;
@@ -271,13 +281,18 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
                                                        reply);
                }
                
-               password = talloc_strndup(mem_ctx, 
-                                         (const char *)chpw.newpasswd.data, 
-                                         chpw.newpasswd.length);
-               if (!password) {
+               pw_len = convert_string_talloc(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx), 
+                                              CH_UTF8, CH_UTF16, 
+                                              (const char *)chpw.newpasswd.data, 
+                                              chpw.newpasswd.length,
+                                              (void **)&password.data);
+               if (pw_len == -1) {
                        free_ChangePasswdDataMS(&chpw);
                        return false;
                }
+               
+               password.length = pw_len;
+       
                if ((chpw.targname && !chpw.targrealm) 
                    || (!chpw.targname && chpw.targrealm)) {
                        return kpasswdd_make_error_reply(kdc, mem_ctx, 
@@ -306,7 +321,7 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
                } else {
                        free_ChangePasswdDataMS(&chpw);
                        return kpasswdd_change_password(kdc, mem_ctx, session_info, 
-                                                       password, reply);
+                                                       &password, reply);
                }
                free_ChangePasswdDataMS(&chpw);
 
@@ -371,7 +386,7 @@ static bool kpasswd_process_request(struct kdc_server *kdc,
                        /* Admin password set */
                        status = samdb_set_password(samdb, mem_ctx,
                                                    set_password_on_dn, NULL,
-                                                   msg, password, NULL, NULL, 
+                                                   msg, &password, NULL, NULL, 
                                                    false, /* this is not a user password change */
                                                    &reject_reason, &dominfo);
                }
index 3af7e45002c6b5b1f32a88d1e7ea281f80ba78d4..6eb8767140b8eff6164e02304f11349afd2d7aeb 100644 (file)
@@ -536,9 +536,40 @@ bool decode_pw_buffer(uint8_t in_buffer[516], char *new_pwrd,
 #ifdef DEBUG_PASSWORD
        DEBUG(100,("decode_pw_buffer: new_pwrd: "));
        dump_data(100, (const uint8_t *)new_pwrd, converted_pw_len);
-       DEBUG(100,("multibyte len:%d\n", converted_pw_len));
+       DEBUG(100,("multibyte len:%d\n", (int)converted_pw_len));
        DEBUG(100,("original char len:%d\n", byte_len/2));
 #endif
        
        return true;
 }
+
+/***********************************************************
+ decode a password buffer
+ *new_pw_size is the length in bytes of the extracted unicode password
+************************************************************/
+bool extract_pw_from_buffer(TALLOC_CTX *mem_ctx, 
+                           uint8_t in_buffer[516], DATA_BLOB *new_pass)
+{
+       int byte_len=0;
+
+       /* The length of the new password is in the last 4 bytes of the data buffer. */
+
+       byte_len = IVAL(in_buffer, 512);
+
+#ifdef DEBUG_PASSWORD
+       dump_data(100, in_buffer, 516);
+#endif
+
+       /* Password cannot be longer than the size of the password buffer */
+       if ( (byte_len < 0) || (byte_len > 512)) {
+               return false;
+       }
+
+       *new_pass = data_blob_talloc(mem_ctx, &in_buffer[512 - byte_len], byte_len);
+
+       if (!*new_pass->data) {
+               return false;
+       }
+
+       return true;
+}
index 4274d2000a10f2d6ab6bc7bcfc3a728f310fb7a8..087f0c982ddf52595da9deeb0eb57bb7c199f730 100644 (file)
@@ -332,7 +332,7 @@ interface drsblobs {
                );
 
        typedef [public] struct {
-               [flag(STR_NOTERM|NDR_REMAINING)] string cleartext;
+               [flag(NDR_REMAINING)] DATA_BLOB cleartext;
        } package_PrimaryCLEARTEXTBlob;
 
        void decode_PrimaryCLEARTEXT(
index 9cda7d0d899ae0fe75fbfedc67ece1937153e379..4c596f1f0368899b554e7a873311aec009e54cc5 100644 (file)
@@ -968,19 +968,14 @@ static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dc
                                                           mem_ctx, msg_user, "unicodePwd", 
                                                           &auth_struct.incoming.current[i]->AuthInfo.nt4owf.password);
                                } else if (auth_struct.incoming.current[i]->AuthType == TRUST_AUTH_TYPE_CLEAR) {
-                                       struct samr_Password hash;
-/*
-                                      . We cannot do this, as windows chooses to send in random passwords here, that won't convert to UTF8 
-                                       samdb_msg_add_string(trusted_domain_state->policy->sam_ldb, 
-                                                            mem_ctx, msg_user, "userPassword", 
-                                                            auth_struct.incoming.current->array[i].AuthInfo.clear.password);
-*/
-                                       mdfour(hash.hash, auth_struct.incoming.current[i]->AuthInfo.clear.password,
-                                              auth_struct.incoming.current[i]->AuthInfo.clear.size);
-                                       samdb_msg_add_hash(trusted_domain_state->policy->sam_ldb, 
-                                                          mem_ctx, msg_user, "unicodePwd", 
-                                                          &hash);
-                               }
+                                       DATA_BLOB new_password = data_blob_const(auth_struct.incoming.current[i]->AuthInfo.clear.password,
+                                                                                auth_struct.incoming.current[i]->AuthInfo.clear.size);
+                                       ret = ldb_msg_add_value(msg_user, "clearTextPassword", &new_password, NULL);
+                                       if (ret != LDB_SUCCESS) {
+                                               ldb_transaction_cancel(policy_state->sam_ldb);
+                                               return NT_STATUS_NO_MEMORY;
+                                       }
+                               } 
                        }
                }
 
index 470c27a075687ba6df5da74e36d86d0a662cd563..9d4c897892fc97da4d821670ce395212b0ce4e0b 100644 (file)
@@ -107,7 +107,7 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca
 
        if (r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
                char *encoded_account = ldb_binary_encode_string(mem_ctx, r->in.account_name);
-               char *flatname;
+               const char *flatname;
                if (!encoded_account) {
                        return NT_STATUS_NO_MEMORY;
                }
@@ -370,7 +370,7 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call
                                           creds->sid,
                                           NULL, /* Don't have plaintext */
                                           NULL, &r->in.new_password,
-                                          false, /* This is not considered a password change */
+                                          true, /* Password change */
                                           NULL, NULL);
        return nt_status;
 }
@@ -385,15 +385,14 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
        struct creds_CredentialState *creds;
        struct ldb_context *sam_ctx;
        NTSTATUS nt_status;
-       char new_pass[512];
-       bool ret;
+       DATA_BLOB new_password;
 
        struct samr_CryptPassword password_buf;
 
        nt_status = dcesrv_netr_creds_server_step_check(dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
                                                        r->in.computer_name, mem_ctx, 
-                                                &r->in.credential, &r->out.return_authenticator,
-                                                &creds);
+                                                       &r->in.credential, &r->out.return_authenticator,
+                                                       &creds);
        NT_STATUS_NOT_OK_RETURN(nt_status);
 
        sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(mem_ctx, dce_call->conn->dce_ctx->lp_ctx));
@@ -402,22 +401,20 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
        }
 
        memcpy(password_buf.data, r->in.new_password.data, 512);
-       SIVAL(password_buf.data,512,r->in.new_password.length);
+       SIVAL(password_buf.data, 512, r->in.new_password.length);
        creds_arcfour_crypt(creds, password_buf.data, 516);
 
-       ret = decode_pw_buffer(password_buf.data, new_pass, sizeof(new_pass),
-                              STR_UNICODE);
-       if (!ret) {
-               DEBUG(3,("netr_ServerPasswordSet2: failed to decode password buffer\n"));
-               return NT_STATUS_ACCESS_DENIED;
+       if (!extract_pw_from_buffer(mem_ctx, password_buf.data, &new_password)) {
+               DEBUG(3,("samr: failed to decode password buffer\n"));
+               return NT_STATUS_WRONG_PASSWORD;
        }
-
+               
        /* Using the sid for the account as the key, set the password */
        nt_status = samdb_set_password_sid(sam_ctx, mem_ctx,
                                           creds->sid,
-                                          new_pass, /* we have plaintext */
+                                          &new_password, /* we have plaintext */
                                           NULL, NULL,
-                                          false, /* This is not considered a password change */
+                                          true, /* Password change */
                                           NULL, NULL);
        return nt_status;
 }
index 8a855a7bdba6957b98720cd913cafce9e353f125..336720ecc768004b85a972a9ba238814fb955a1b 100644 (file)
@@ -86,7 +86,8 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
        }
        msg = res[0];
 
-       status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
+       status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
+                                       msg, &lm_pwd, &nt_pwd);
        if (!NT_STATUS_IS_OK(status) || !lm_pwd || !nt_pwd) {
                ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
@@ -183,8 +184,8 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                                     struct samr_OemChangePasswordUser2 *r)
 {
        NTSTATUS status;
-       char new_pass[512];
-       uint32_t new_pass_len;
+       DATA_BLOB new_password;
+       char *new_pass;
        struct samr_CryptPassword *pwbuf = r->in.password;
        struct ldb_context *sam_ctx;
        struct ldb_dn *user_dn;
@@ -231,7 +232,8 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
 
        user_dn = res[0]->dn;
 
-       status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, NULL);
+       status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx,
+                                       res[0], &lm_pwd, NULL);
        if (!NT_STATUS_IS_OK(status) || !lm_pwd) {
                ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
@@ -242,15 +244,18 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
        arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
        data_blob_free(&lm_pwd_blob);
        
-       if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
-                             STR_ASCII)) {
+       if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
                ldb_transaction_cancel(sam_ctx);
                DEBUG(3,("samr: failed to decode password buffer\n"));
                return NT_STATUS_WRONG_PASSWORD;
        }
-
-       /* check LM verifier */
-       if (lm_pwd == NULL) {
+               
+       if (convert_string_talloc(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
+                                 CH_DOS, CH_UNIX, 
+                                 (const char *)new_password.data, 
+                                 new_password.length,
+                                 (void **)&new_pass) == -1) {
+               DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
                ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
        }
@@ -278,7 +283,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
         * due to password policies */
        status = samdb_set_password(sam_ctx, mem_ctx,
                                    user_dn, NULL, 
-                                   mod, new_pass
+                                   mod, &new_password
                                    NULL, NULL,
                                    true, /* this is a user password change */
                                    NULL, 
@@ -320,7 +325,7 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
                                  struct samr_ChangePasswordUser3 *r)
 {      
        NTSTATUS status;
-       char new_pass[512];
+       DATA_BLOB new_password;
        struct ldb_context *sam_ctx = NULL;
        struct ldb_dn *user_dn;
        int ret;
@@ -369,7 +374,8 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
 
        user_dn = res[0]->dn;
 
-       status = samdb_result_passwords(mem_ctx, res[0], &lm_pwd, &nt_pwd);
+       status = samdb_result_passwords(mem_ctx, dce_call->conn->dce_ctx->lp_ctx, 
+                                       res[0], &lm_pwd, &nt_pwd);
        if (!NT_STATUS_IS_OK(status) ) {
                goto failed;
        }
@@ -384,40 +390,49 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
        arcfour_crypt_blob(r->in.nt_password->data, 516, &nt_pwd_blob);
        data_blob_free(&nt_pwd_blob);
 
-       if (!decode_pw_buffer(r->in.nt_password->data, new_pass, sizeof(new_pass),
-                             STR_UNICODE)) {
+       if (!extract_pw_from_buffer(mem_ctx, r->in.nt_password->data, &new_password)) {
+               ldb_transaction_cancel(sam_ctx);
                DEBUG(3,("samr: failed to decode password buffer\n"));
-               status = NT_STATUS_WRONG_PASSWORD;
-               goto failed;
+               return NT_STATUS_WRONG_PASSWORD;
        }
-
+               
        if (r->in.nt_verifier == NULL) {
                status = NT_STATUS_WRONG_PASSWORD;
                goto failed;
        }
 
        /* check NT verifier */
-       E_md4hash(new_pass, new_nt_hash);
+       mdfour(new_nt_hash, new_password.data, new_password.length);
+
        E_old_pw_hash(new_nt_hash, nt_pwd->hash, nt_verifier.hash);
        if (memcmp(nt_verifier.hash, r->in.nt_verifier->hash, 16) != 0) {
                status = NT_STATUS_WRONG_PASSWORD;
                goto failed;
        }
 
-       /* check LM verifier */
+       /* check LM verifier (really not needed as we just checked the
+        * much stronger NT hash, but the RPC-SAMR test checks for
+        * this) */
        if (lm_pwd && r->in.lm_verifier != NULL) {
-               E_deshash(new_pass, new_lm_hash);
-               E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
-               if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
-                       status = NT_STATUS_WRONG_PASSWORD;
-                       goto failed;
+               char *new_pass;
+               if (convert_string_talloc(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
+                                         CH_UTF16, CH_UNIX, 
+                                         (const char *)new_password.data, 
+                                         new_password.length,
+                                         (void **)&new_pass) != -1) {
+                       E_deshash(new_pass, new_lm_hash);
+                       E_old_pw_hash(new_nt_hash, lm_pwd->hash, lm_verifier.hash);
+                       if (memcmp(lm_verifier.hash, r->in.lm_verifier->hash, 16) != 0) {
+                               status = NT_STATUS_WRONG_PASSWORD;
+                               goto failed;
+                       }
                }
        }
 
-
        mod = ldb_msg_new(mem_ctx);
        if (mod == NULL) {
-               return NT_STATUS_NO_MEMORY;
+               status = NT_STATUS_NO_MEMORY;
+               goto failed;
        }
 
        mod->dn = ldb_dn_copy(mod, user_dn);
@@ -430,7 +445,7 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
         * due to password policies */
        status = samdb_set_password(sam_ctx, mem_ctx,
                                    user_dn, NULL, 
-                                   mod, new_pass
+                                   mod, &new_password
                                    NULL, NULL,
                                    true, /* this is a user password change */
                                    &reason, 
@@ -517,7 +532,7 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
                           struct samr_CryptPassword *pwbuf)
 {
        NTSTATUS nt_status;
-       char new_pass[512];
+       DATA_BLOB new_password;
        DATA_BLOB session_key = data_blob(NULL, 0);
 
        nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
@@ -527,17 +542,16 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
 
        arcfour_crypt_blob(pwbuf->data, 516, &session_key);
 
-       if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
-                             STR_UNICODE)) {
+       if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
                DEBUG(3,("samr: failed to decode password buffer\n"));
                return NT_STATUS_WRONG_PASSWORD;
        }
-
+               
        /* set the password - samdb needs to know both the domain and user DNs,
           so the domain password policy can be used */
        return samdb_set_password(sam_ctx, mem_ctx,
                                  account_dn, domain_dn, 
-                                 msg, new_pass
+                                 msg, &new_password
                                  NULL, NULL,
                                  false, /* This is a password set, not change */
                                  NULL, NULL);
@@ -557,8 +571,7 @@ NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
                              struct samr_CryptPasswordEx *pwbuf)
 {
        NTSTATUS nt_status;
-       char new_pass[512];
-       uint32_t new_pass_len;
+       DATA_BLOB new_password;
        DATA_BLOB co_session_key;
        DATA_BLOB session_key = data_blob(NULL, 0);
        struct MD5Context ctx;
@@ -580,17 +593,16 @@ NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
        
        arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
 
-       if (!decode_pw_buffer(pwbuf->data, new_pass, sizeof(new_pass),
-                             STR_UNICODE)) {
+       if (!extract_pw_from_buffer(mem_ctx, pwbuf->data, &new_password)) {
                DEBUG(3,("samr: failed to decode password buffer\n"));
                return NT_STATUS_WRONG_PASSWORD;
        }
-
+               
        /* set the password - samdb needs to know both the domain and user DNs,
           so the domain password policy can be used */
        return samdb_set_password(sam_ctx, mem_ctx,
                                  account_dn, domain_dn, 
-                                 msg, new_pass
+                                 msg, &new_password
                                  NULL, NULL,
                                  false, /* This is a password set, not change */
                                  NULL, NULL);