s4-dsdb: change samdb_replace() to dsdb_replace() and allow for dsdb_flags
[ira/wip.git] / source4 / rpc_server / samr / samr_password.c
index 6341dda6812bec4cbc4bcb06e61e98132ae2eed8..1a09283ea6b3a62beaf8cb41fe02033f3e66cc56 100644 (file)
@@ -8,7 +8,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #include "rpc_server/dcerpc_server.h"
-#include "rpc_server/common/common.h"
 #include "rpc_server/samr/dcesrv_samr.h"
 #include "system/time.h"
-#include "lib/crypto/crypto.h"
-#include "ads.h"
-#include "libcli/ldap/ldap.h"
+#include "../lib/crypto/crypto.h"
 #include "dsdb/samdb/samdb.h"
 #include "auth/auth.h"
-#include "rpc_server/samr/proto.h"
 #include "libcli/auth/libcli_auth.h"
-#include "db_wrap.h"
+#include "../lib/util/util_ldb.h"
 
 /* 
   samr_ChangePasswordUser 
 */
-NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
-                                struct samr_ChangePasswordUser *r)
+NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, 
+                                       TALLOC_CTX *mem_ctx,
+                                       struct samr_ChangePasswordUser *r)
 {
        struct dcesrv_handle *h;
        struct samr_account_state *a_state;
@@ -49,7 +45,7 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
        struct samr_Password new_lmPwdHash, new_ntPwdHash, checkHash;
        struct samr_Password *lm_pwd, *nt_pwd;
        NTSTATUS status = NT_STATUS_OK;
-       const char * const attrs[] = { "lmPwdHash", "ntPwdHash" , NULL };
+       const char * const attrs[] = { "dBCSPwd", "unicodePwd" , NULL };
 
        DCESRV_PULL_HANDLE(h, r->in.user_handle, SAMR_HANDLE_USER);
 
@@ -63,21 +59,15 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
                   present */
                return NT_STATUS_INVALID_PARAMETER_MIX;
        }
-       if (!r->in.cross1_present || !r->in.nt_cross) {
-               return NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED;
-       }
-       if (!r->in.cross2_present || !r->in.lm_cross) {
-               return NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED;
-       }
 
        /* To change a password we need to open as system */
-       sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
+       sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx));
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
 
        ret = ldb_transaction_start(sam_ctx);
-       if (ret) {
+       if (ret != LDB_SUCCESS) {
                DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
                return NT_STATUS_TRANSACTION_ABORTED;
        }
@@ -91,18 +81,21 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
        }
        msg = res[0];
 
-       status = samdb_result_passwords(mem_ctx, msg, &lm_pwd, &nt_pwd);
-       if (!NT_STATUS_IS_OK(status) || !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) || !nt_pwd) {
                ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
        }
 
        /* decrypt and check the new lm hash */
-       D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
-       D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
-       if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
-               ldb_transaction_cancel(sam_ctx);
-               return NT_STATUS_WRONG_PASSWORD;
+       if (lm_pwd) {
+               D_P16(lm_pwd->hash, r->in.new_lm_crypted->hash, new_lmPwdHash.hash);
+               D_P16(new_lmPwdHash.hash, r->in.old_lm_crypted->hash, checkHash.hash);
+               if (memcmp(checkHash.hash, lm_pwd, 16) != 0) {
+                       ldb_transaction_cancel(sam_ctx);
+                       return NT_STATUS_WRONG_PASSWORD;
+               }
        }
 
        /* decrypt and check the new nt hash */
@@ -113,18 +106,24 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
                return NT_STATUS_WRONG_PASSWORD;
        }
        
-       /* check the nt cross hash */
-       D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
-       if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
-               ldb_transaction_cancel(sam_ctx);
-               return NT_STATUS_WRONG_PASSWORD;
+       /* The NT Cross is not required by Win2k3 R2, but if present
+          check the nt cross hash */
+       if (r->in.cross1_present && r->in.nt_cross && lm_pwd) {
+               D_P16(lm_pwd->hash, r->in.nt_cross->hash, checkHash.hash);
+               if (memcmp(checkHash.hash, new_ntPwdHash.hash, 16) != 0) {
+                       ldb_transaction_cancel(sam_ctx);
+                       return NT_STATUS_WRONG_PASSWORD;
+               }
        }
 
-       /* check the lm cross hash */
-       D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
-       if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
-               ldb_transaction_cancel(sam_ctx);
-               return NT_STATUS_WRONG_PASSWORD;
+       /* The LM Cross is not required by Win2k3 R2, but if present
+          check the lm cross hash */
+       if (r->in.cross2_present && r->in.lm_cross && lm_pwd) {
+               D_P16(nt_pwd->hash, r->in.lm_cross->hash, checkHash.hash);
+               if (memcmp(checkHash.hash, new_lmPwdHash.hash, 16) != 0) {
+                       ldb_transaction_cancel(sam_ctx);
+                       return NT_STATUS_WRONG_PASSWORD;
+               }
        }
 
        msg = ldb_msg_new(mem_ctx);
@@ -139,13 +138,12 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
                return NT_STATUS_NO_MEMORY;
        }
 
-       /* set the password on the user DN specified.  This may fail
-        * due to password policies */
+       /* setup password modify mods on the user DN specified.  This may fail
+        * due to password policies */
        status = samdb_set_password(sam_ctx, mem_ctx,
                                    a_state->account_dn, a_state->domain_state->domain_dn,
                                    msg, NULL, &new_lmPwdHash, &new_ntPwdHash, 
-                                   True, /* this is a user password change */
-                                   True, /* run restriction tests */
+                                   true, /* this is a user password change */
                                    NULL,
                                    NULL);
        if (!NT_STATUS_IS_OK(status)) {
@@ -155,10 +153,10 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
 
        /* The above call only setup the modifications, this actually
         * makes the write to the database. */
-       ret = samdb_replace(sam_ctx, mem_ctx, msg);
-       if (ret != 0) {
+       ret = dsdb_replace(sam_ctx, msg, 0);
+       if (ret != LDB_SUCCESS) {
                DEBUG(2,("Failed to modify record to change password on %s: %s\n",
-                        ldb_dn_linearize(mem_ctx, a_state->account_dn),
+                        ldb_dn_get_linearized(a_state->account_dn),
                         ldb_errstring(sam_ctx)));
                ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
@@ -166,9 +164,9 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
 
        /* And this confirms it in a transaction commit */
        ret = ldb_transaction_commit(sam_ctx);
-       if (ret != 0) {
+       if (ret != LDB_SUCCESS) {
                DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
-                        ldb_dn_linearize(mem_ctx, a_state->account_dn),
+                        ldb_dn_get_linearized(a_state->account_dn),
                         ldb_errstring(sam_ctx)));
                return NT_STATUS_TRANSACTION_ABORTED;
        }
@@ -179,35 +177,46 @@ NTSTATUS samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, TALLOC_CTX
 /* 
   samr_OemChangePasswordUser2 
 */
-NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
-                                    struct samr_OemChangePasswordUser2 *r)
+NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
+                                           TALLOC_CTX *mem_ctx,
+                                           struct samr_OemChangePasswordUser2 *r)
 {
        NTSTATUS status;
-       char new_pass[512];
-       uint32_t new_pass_len;
+       DATA_BLOB new_password, new_unicode_password;
+       char *new_pass;
        struct samr_CryptPassword *pwbuf = r->in.password;
        struct ldb_context *sam_ctx;
-       const struct ldb_dn *user_dn;
+       struct ldb_dn *user_dn;
        int ret;
        struct ldb_message **res, *mod;
-       const char * const attrs[] = { "objectSid", "lmPwdHash", NULL };
+       const char * const attrs[] = { "objectSid", "dBCSPwd", NULL };
        struct samr_Password *lm_pwd;
        DATA_BLOB lm_pwd_blob;
        uint8_t new_lm_hash[16];
        struct samr_Password lm_verifier;
+       size_t unicode_pw_len;
 
        if (pwbuf == NULL) {
-               return NT_STATUS_WRONG_PASSWORD;
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (r->in.hash == NULL) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       /* this call can only work with lanman auth */
+       if (!lp_lanman_auth(dce_call->conn->dce_ctx->lp_ctx)) {
+               return NT_STATUS_NOT_SUPPORTED;
        }
 
        /* To change a password we need to open as system */
-       sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
+       sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx));
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
 
        ret = ldb_transaction_start(sam_ctx);
-       if (ret) {
+       if (ret != LDB_SUCCESS) {
                DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
                return NT_STATUS_TRANSACTION_ABORTED;
        }
@@ -227,7 +236,8 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_
 
        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;
@@ -238,18 +248,32 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_
        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),
-                             &new_pass_len, 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;
        }
+               
+       if (!convert_string_talloc_convenience(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, NULL, false)) {
+               DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
+               ldb_transaction_cancel(sam_ctx);
+               return NT_STATUS_WRONG_PASSWORD;
+       }
 
-       /* check LM verifier */
-       if (lm_pwd == NULL || r->in.hash == NULL) {
+       if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), 
+                                              CH_DOS, CH_UTF16, 
+                                              (const char *)new_password.data, 
+                                              new_password.length,
+                                              (void **)&new_unicode_password.data, &unicode_pw_len, false)) {
+               DEBUG(3,("samr: failed to convert incoming password buffer to UTF16 charset\n"));
                ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
        }
+       new_unicode_password.length = unicode_pw_len;
 
        E_deshash(new_pass, new_lm_hash);
        E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
@@ -274,10 +298,9 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_
         * due to password policies */
        status = samdb_set_password(sam_ctx, mem_ctx,
                                    user_dn, NULL, 
-                                   mod, new_pass
+                                   mod, &new_unicode_password
                                    NULL, NULL,
-                                   True, /* this is a user password change */
-                                   True, /* run restriction tests */
+                                   true, /* this is a user password change */
                                    NULL, 
                                    NULL);
        if (!NT_STATUS_IS_OK(status)) {
@@ -287,10 +310,10 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_
 
        /* The above call only setup the modifications, this actually
         * makes the write to the database. */
-       ret = samdb_replace(sam_ctx, mem_ctx, mod);
-       if (ret != 0) {
+       ret = dsdb_replace(sam_ctx, mod, 0);
+       if (ret != LDB_SUCCESS) {
                DEBUG(2,("Failed to modify record to change password on %s: %s\n",
-                        ldb_dn_linearize(mem_ctx, user_dn),
+                        ldb_dn_get_linearized(user_dn),
                         ldb_errstring(sam_ctx)));
                ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_INTERNAL_DB_CORRUPTION;
@@ -298,9 +321,9 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_
 
        /* And this confirms it in a transaction commit */
        ret = ldb_transaction_commit(sam_ctx);
-       if (ret != 0) {
+       if (ret != LDB_SUCCESS) {
                DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
-                        ldb_dn_linearize(mem_ctx, user_dn),
+                        ldb_dn_get_linearized(user_dn),
                         ldb_errstring(sam_ctx)));
                return NT_STATUS_TRANSACTION_ABORTED;
        }
@@ -312,27 +335,27 @@ NTSTATUS samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_
 /* 
   samr_ChangePasswordUser3 
 */
-NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
-                                 TALLOC_CTX *mem_ctx,
-                                 struct samr_ChangePasswordUser3 *r)
+NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
+                                        TALLOC_CTX *mem_ctx,
+                                        struct samr_ChangePasswordUser3 *r)
 {      
        NTSTATUS status;
-       char new_pass[512];
-       uint32_t new_pass_len;
+       DATA_BLOB new_password;
        struct ldb_context *sam_ctx = NULL;
-       const struct ldb_dn *user_dn;
+       struct ldb_dn *user_dn;
        int ret;
        struct ldb_message **res, *mod;
-       const char * const attrs[] = { "ntPwdHash", "lmPwdHash", NULL };
+       const char * const attrs[] = { "unicodePwd", "dBCSPwd", NULL };
        struct samr_Password *nt_pwd, *lm_pwd;
        DATA_BLOB nt_pwd_blob;
        struct samr_DomInfo1 *dominfo = NULL;
-       struct samr_ChangeReject *reject = NULL;
-       enum samr_RejectReason reason = SAMR_REJECT_OTHER;
+       struct userPwdChangeFailureInformation *reject = NULL;
+       enum samPwdChangeReason reason = SAM_PWD_CHANGE_NO_ERROR;
        uint8_t new_nt_hash[16], new_lm_hash[16];
        struct samr_Password nt_verifier, lm_verifier;
 
-       ZERO_STRUCT(r->out);
+       *r->out.dominfo = NULL;
+       *r->out.reject = NULL;
 
        if (r->in.nt_password == NULL ||
            r->in.nt_verifier == NULL) {
@@ -340,13 +363,13 @@ NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
        }
 
        /* To change a password we need to open as system */
-       sam_ctx = samdb_connect(mem_ctx, system_session(mem_ctx));
+       sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx));
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
 
        ret = ldb_transaction_start(sam_ctx);
-       if (ret) {
+       if (ret != LDB_SUCCESS) {
                talloc_free(sam_ctx);
                DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
                return NT_STATUS_TRANSACTION_ABORTED;
@@ -367,7 +390,8 @@ NTSTATUS 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;
        }
@@ -382,40 +406,49 @@ NTSTATUS 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),
-                             &new_pass_len, 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_convenience(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, NULL, false)) {
+                       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);
@@ -428,22 +461,22 @@ NTSTATUS 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 */
-                                   True, /* run restriction tests */
+                                   true, /* this is a user password change */
                                    &reason, 
                                    &dominfo);
+
        if (!NT_STATUS_IS_OK(status)) {
                goto failed;
        }
 
        /* The above call only setup the modifications, this actually
         * makes the write to the database. */
-       ret = samdb_replace(sam_ctx, mem_ctx, mod);
-       if (ret != 0) {
-               DEBUG(2,("samdb_replace failed to change password for %s: %s\n",
-                        ldb_dn_linearize(mem_ctx, user_dn),
+       ret = dsdb_replace(sam_ctx, mod, 0);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(2,("dsdb_replace failed to change password for %s: %s\n",
+                        ldb_dn_get_linearized(user_dn),
                         ldb_errstring(sam_ctx)));
                status = NT_STATUS_UNSUCCESSFUL;
                goto failed;
@@ -451,9 +484,9 @@ NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
 
        /* And this confirms it in a transaction commit */
        ret = ldb_transaction_commit(sam_ctx);
-       if (ret != 0) {
+       if (ret != LDB_SUCCESS) {
                DEBUG(1,("Failed to commit transaction to change password on %s: %s\n",
-                        ldb_dn_linearize(mem_ctx, user_dn),
+                        ldb_dn_get_linearized(user_dn),
                         ldb_errstring(sam_ctx)));
                status = NT_STATUS_TRANSACTION_ABORTED;
                goto failed;
@@ -463,18 +496,16 @@ NTSTATUS samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
 
 failed:
        ldb_transaction_cancel(sam_ctx);
-       talloc_free(sam_ctx);
 
-       reject = talloc(mem_ctx, struct samr_ChangeReject);
-       r->out.dominfo = dominfo;
-       r->out.reject = reject;
+       reject = talloc(mem_ctx, struct userPwdChangeFailureInformation);
+       if (reject != NULL) {
+               ZERO_STRUCTP(reject);
+               reject->extendedFailureReason = reason;
 
-       if (reject == NULL) {
-               return status;
+               *r->out.reject = reject;
        }
-       ZERO_STRUCTP(reject);
 
-       reject->reason = reason;
+       *r->out.dominfo = dominfo;
 
        return status;
 }
@@ -485,10 +516,13 @@ failed:
 
   easy - just a subset of samr_ChangePasswordUser3
 */
-NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx,
-                                 struct samr_ChangePasswordUser2 *r)
+NTSTATUS dcesrv_samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call,
+                                        TALLOC_CTX *mem_ctx,
+                                        struct samr_ChangePasswordUser2 *r)
 {
        struct samr_ChangePasswordUser3 r2;
+       struct samr_DomInfo1 *dominfo = NULL;
+       struct userPwdChangeFailureInformation *reject = NULL;
 
        r2.in.server = r->in.server;
        r2.in.account = r->in.account;
@@ -498,242 +532,13 @@ NTSTATUS samr_ChangePasswordUser2(struct dcesrv_call_state *dce_call, TALLOC_CTX
        r2.in.lm_password = r->in.lm_password;
        r2.in.lm_verifier = r->in.lm_verifier;
        r2.in.password3 = NULL;
+       r2.out.dominfo = &dominfo;
+       r2.out.reject = &reject;
 
-       return samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
+       return dcesrv_samr_ChangePasswordUser3(dce_call, mem_ctx, &r2);
 }
 
 
-/*
-  check that a password is sufficiently complex
-*/
-static BOOL samdb_password_complexity_ok(const char *pass)
-{
-       return check_password_quality(pass);
-}
-
-/*
-  set the user password using plaintext, obeying any user or domain
-  password restrictions
-
-  note that this function doesn't actually store the result in the
-  database, it just fills in the "mod" structure with ldb modify
-  elements to setup the correct change when samdb_replace() is
-  called. This allows the caller to combine the change with other
-  changes (as is needed by some of the set user info levels)
-
-  The caller should probably have a transaction wrapping this
-*/
-NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
-                           const struct ldb_dn *user_dn,
-                           const struct ldb_dn *domain_dn,
-                           struct ldb_message *mod,
-                           const char *new_pass,
-                           struct samr_Password *lmNewHash, 
-                           struct samr_Password *ntNewHash,
-                           BOOL user_change,
-                           BOOL restrictions,
-                           enum samr_RejectReason *reject_reason,
-                           struct samr_DomInfo1 **_dominfo)
-{
-       const char * const user_attrs[] = { "userAccountControl", "sambaLMPwdHistory", 
-                                           "sambaNTPwdHistory", 
-                                           "lmPwdHash", "ntPwdHash", 
-                                           "objectSid", 
-                                           "pwdLastSet", NULL };
-       const char * const domain_attrs[] = { "pwdProperties", "pwdHistoryLength", 
-                                             "maxPwdAge", "minPwdAge", 
-                                             "minPwdLength", NULL };
-       NTTIME pwdLastSet;
-       int64_t minPwdAge;
-       uint_t minPwdLength, pwdProperties, pwdHistoryLength;
-       uint_t userAccountControl;
-       struct samr_Password *sambaLMPwdHistory, *sambaNTPwdHistory, *lmPwdHash, *ntPwdHash;
-       struct samr_Password local_lmNewHash, local_ntNewHash;
-       int sambaLMPwdHistory_len, sambaNTPwdHistory_len;
-       struct dom_sid *domain_sid;
-       struct ldb_message **res;
-       int count;
-       time_t now = time(NULL);
-       NTTIME now_nt;
-       int i;
-
-       /* we need to know the time to compute password age */
-       unix_to_nt_time(&now_nt, now);
-
-       /* pull all the user parameters */
-       count = gendb_search_dn(ctx, mem_ctx, user_dn, &res, user_attrs);
-       if (count != 1) {
-               return NT_STATUS_INTERNAL_DB_CORRUPTION;
-       }
-       userAccountControl = samdb_result_uint(res[0],   "userAccountControl", 0);
-       sambaLMPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
-                                                "sambaLMPwdHistory", &sambaLMPwdHistory);
-       sambaNTPwdHistory_len =   samdb_result_hashes(mem_ctx, res[0], 
-                                                "sambaNTPwdHistory", &sambaNTPwdHistory);
-       lmPwdHash =          samdb_result_hash(mem_ctx, res[0],   "lmPwdHash");
-       ntPwdHash =          samdb_result_hash(mem_ctx, res[0],   "ntPwdHash");
-       pwdLastSet =         samdb_result_uint64(res[0], "pwdLastSet", 0);
-
-       if (domain_dn) {
-               /* pull the domain parameters */
-               count = gendb_search_dn(ctx, mem_ctx, domain_dn, &res, domain_attrs);
-               if (count != 1) {
-                       return NT_STATUS_NO_SUCH_DOMAIN;
-               }
-       } else {
-               /* work out the domain sid, and pull the domain from there */
-               domain_sid =         samdb_result_sid_prefix(mem_ctx, res[0], "objectSid");
-               if (domain_sid == NULL) {
-                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
-               }
-
-               count = gendb_search(ctx, mem_ctx, NULL, &res, domain_attrs, 
-                                    "(objectSid=%s)", 
-                                    ldap_encode_ndr_dom_sid(mem_ctx, domain_sid));
-               if (count != 1) {
-                       return NT_STATUS_NO_SUCH_DOMAIN;
-               }
-       }
-
-       pwdProperties =    samdb_result_uint(res[0],   "pwdProperties", 0);
-       pwdHistoryLength = samdb_result_uint(res[0],   "pwdHistoryLength", 0);
-       minPwdLength =     samdb_result_uint(res[0],   "minPwdLength", 0);
-       minPwdAge =        samdb_result_int64(res[0],  "minPwdAge", 0);
-
-       if (_dominfo) {
-               struct samr_DomInfo1 *dominfo;
-               /* on failure we need to fill in the reject reasons */
-               dominfo = talloc(mem_ctx, struct samr_DomInfo1);
-               if (dominfo == NULL) {
-                       return NT_STATUS_NO_MEMORY;
-               }
-               dominfo->min_password_length     = minPwdLength;
-               dominfo->password_properties     = pwdProperties;
-               dominfo->password_history_length = pwdHistoryLength;
-               dominfo->max_password_age        = minPwdAge;
-               dominfo->min_password_age        = minPwdAge;
-               *_dominfo = dominfo;
-       }
-
-       if (new_pass) {
-               /* check the various password restrictions */
-               if (restrictions && minPwdLength > strlen_m(new_pass)) {
-                       if (reject_reason) {
-                               *reject_reason = SAMR_REJECT_TOO_SHORT;
-                       }
-                       return NT_STATUS_PASSWORD_RESTRICTION;
-               }
-               
-               /* 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;
-               }
-               
-               /* compute the new nt and lm hashes */
-               if (E_deshash(new_pass, local_lmNewHash.hash)) {
-                       lmNewHash = &local_lmNewHash;
-               }
-               E_md4hash(new_pass, local_ntNewHash.hash);
-               ntNewHash = &local_ntNewHash;
-       }
-
-       if (restrictions && user_change) {
-               /* are all password changes disallowed? */
-               if (pwdProperties & DOMAIN_REFUSE_PASSWORD_CHANGE) {
-                       if (reject_reason) {
-                               *reject_reason = SAMR_REJECT_OTHER;
-                       }
-                       return NT_STATUS_PASSWORD_RESTRICTION;
-               }
-               
-               /* can this user change password? */
-               if (userAccountControl & UF_PASSWD_CANT_CHANGE) {
-                       if (reject_reason) {
-                               *reject_reason = SAMR_REJECT_OTHER;
-                       }
-                       return NT_STATUS_PASSWORD_RESTRICTION;
-               }
-               
-               /* yes, this is a minus. The ages are in negative 100nsec units! */
-               if (pwdLastSet - minPwdAge > now_nt) {
-                       if (reject_reason) {
-                               *reject_reason = SAMR_REJECT_OTHER;
-                       }
-                       return NT_STATUS_PASSWORD_RESTRICTION;
-               }
-
-               /* check the immediately past password */
-               if (pwdHistoryLength > 0) {
-                       if (lmNewHash && lmPwdHash && memcmp(lmNewHash->hash, lmPwdHash->hash, 16) == 0) {
-                               if (reject_reason) {
-                                       *reject_reason = SAMR_REJECT_COMPLEXITY;
-                               }
-                               return NT_STATUS_PASSWORD_RESTRICTION;
-                       }
-                       if (ntNewHash && ntPwdHash && memcmp(ntNewHash->hash, ntPwdHash->hash, 16) == 0) {
-                               if (reject_reason) {
-                                       *reject_reason = SAMR_REJECT_COMPLEXITY;
-                               }
-                               return NT_STATUS_PASSWORD_RESTRICTION;
-                       }
-               }
-               
-               /* check the password history */
-               sambaLMPwdHistory_len = MIN(sambaLMPwdHistory_len, pwdHistoryLength);
-               sambaNTPwdHistory_len = MIN(sambaNTPwdHistory_len, pwdHistoryLength);
-               
-               for (i=0; lmNewHash && i<sambaLMPwdHistory_len;i++) {
-                       if (memcmp(lmNewHash->hash, sambaLMPwdHistory[i].hash, 16) == 0) {
-                               if (reject_reason) {
-                                       *reject_reason = SAMR_REJECT_COMPLEXITY;
-                               }
-                               return NT_STATUS_PASSWORD_RESTRICTION;
-                       }
-               }
-               for (i=0; ntNewHash && i<sambaNTPwdHistory_len;i++) {
-                       if (memcmp(ntNewHash->hash, sambaNTPwdHistory[i].hash, 16) == 0) {
-                               if (reject_reason) {
-                                       *reject_reason = SAMR_REJECT_COMPLEXITY;
-                               }
-                               return NT_STATUS_PASSWORD_RESTRICTION;
-                       }
-               }
-       }
-
-#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.
-                * Modules in ldb will set all the appropriate
-                * hashes */
-               CHECK_RET(samdb_msg_add_string(ctx, mem_ctx, mod, 
-                                              "sambaPassword", new_pass));
-       } 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, "sambaPassword"));
-
-               if (lmNewHash) {
-                       CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "lmPwdHash", lmNewHash));
-               } else {
-                       CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "lmPwdHash"));
-               }
-               
-               if (ntNewHash) {
-                       CHECK_RET(samdb_msg_add_hash(ctx, mem_ctx, mod, "ntPwdHash", ntNewHash));
-               } else {
-                       CHECK_RET(samdb_msg_add_delete(ctx, mem_ctx, mod, "ntPwdHash"));
-               }
-       }
-
-       return NT_STATUS_OK;
-}
-
 /*
   set password via a samr_CryptPassword buffer
   this will in the 'msg' with modify operations that will update the user
@@ -741,14 +546,13 @@ NTSTATUS samdb_set_password(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
 */
 NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
                           void *sam_ctx,
-                          const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
+                          struct ldb_dn *account_dn, struct ldb_dn *domain_dn,
                           TALLOC_CTX *mem_ctx,
                           struct ldb_message *msg, 
                           struct samr_CryptPassword *pwbuf)
 {
        NTSTATUS nt_status;
-       char new_pass[512];
-       uint32_t new_pass_len;
+       DATA_BLOB new_password;
        DATA_BLOB session_key = data_blob(NULL, 0);
 
        nt_status = dcesrv_fetch_session_key(dce_call->conn, &session_key);
@@ -758,20 +562,18 @@ 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),
-                             &new_pass_len, 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 */
-                                 True, /* run restriction tests */
+                                 false, /* This is a password set, not change */
                                  NULL, NULL);
 }
 
@@ -783,14 +585,14 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
 */
 NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
                              struct ldb_context *sam_ctx,
-                             const struct ldb_dn *account_dn, const struct ldb_dn *domain_dn,
+                             struct ldb_dn *account_dn,
+                             struct ldb_dn *domain_dn,
                              TALLOC_CTX *mem_ctx,
                              struct ldb_message *msg, 
                              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;
@@ -812,99 +614,18 @@ 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),
-                             &new_pass_len, 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 */
-                                 True, /* run restriction tests */
+                                 false, /* This is a password set, not change */
                                  NULL, NULL);
 }
 
-/*
-  set the user password using plaintext, obeying any user or domain
-  password restrictions
-
-  This wrapper function takes a SID as input, rather than a user DN,
-  and actually performs the password change
-
-*/
-NTSTATUS samdb_set_password_sid(struct ldb_context *ctx, TALLOC_CTX *mem_ctx,
-                               const struct dom_sid *user_sid,
-                               const char *new_pass,
-                               struct samr_Password *lmNewHash, 
-                               struct samr_Password *ntNewHash,
-                               BOOL user_change,
-                               BOOL restrictions,
-                               enum samr_RejectReason *reject_reason,
-                               struct samr_DomInfo1 **_dominfo) 
-{
-       NTSTATUS nt_status;
-       struct ldb_dn *user_dn;
-       struct ldb_message *msg;
-       int ret;
-
-       ret = ldb_transaction_start(ctx);
-       if (ret) {
-               DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(ctx)));
-               return NT_STATUS_TRANSACTION_ABORTED;
-       }
-
-       user_dn = samdb_search_dn(ctx, mem_ctx, NULL, 
-                                 "(&(objectSid=%s)(objectClass=user))", 
-                                 ldap_encode_ndr_dom_sid(mem_ctx, user_sid));
-       if (!user_dn) {
-               ldb_transaction_cancel(ctx);
-               DEBUG(3, ("samdb_set_password_sid: SID %s not found in samdb, returning NO_SUCH_USER\n",
-                         dom_sid_string(mem_ctx, user_sid)));
-               return NT_STATUS_NO_SUCH_USER;
-       }
-
-       msg = ldb_msg_new(mem_ctx);
-       if (msg == NULL) {
-               ldb_transaction_cancel(ctx);
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       msg->dn = ldb_dn_copy(msg, user_dn);
-       if (!msg->dn) {
-               ldb_transaction_cancel(ctx);
-               return NT_STATUS_NO_MEMORY;
-       }
-
-       nt_status = samdb_set_password(ctx, mem_ctx,
-                                      user_dn, NULL,
-                                      msg, new_pass, 
-                                      lmNewHash, ntNewHash,
-                                      user_change, /* This is a password set, not change */
-                                      restrictions, /* run restriction tests */
-                                      reject_reason, _dominfo);
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               ldb_transaction_cancel(ctx);
-               return nt_status;
-       }
-       
-       /* modify the samdb record */
-       ret = samdb_replace(ctx, mem_ctx, msg);
-       if (ret != 0) {
-               ldb_transaction_cancel(ctx);
-               return NT_STATUS_ACCESS_DENIED;
-       }
-
-       ret = ldb_transaction_commit(ctx);
-       if (ret != 0) {
-               DEBUG(0,("Failed to commit transaction to change password on %s: %s\n",
-                        ldb_dn_linearize(mem_ctx, msg->dn),
-                        ldb_errstring(ctx)));
-               return NT_STATUS_TRANSACTION_ABORTED;
-       }
-       return NT_STATUS_OK;
-}