Ensure convert_string_XXX is always called with a valid converted_size pointer.
[samba.git] / source4 / rpc_server / samr / samr_password.c
index f7aa376501644dd897dd9d46f361ca2dafa63615..8963b0436eb484bb4edeb00838a9c6dce897163d 100644 (file)
@@ -1,21 +1,21 @@
-/* 
+/*
    Unix SMB/CIFS implementation.
 
    samr server password set/change handling
 
    Copyright (C) Andrew Tridgell 2004
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
-   
+
    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 3 of the License, or
    (at your option) any later version.
-   
+
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    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, see <http://www.gnu.org/licenses/>.
 */
 #include "auth/auth.h"
 #include "libcli/auth/libcli_auth.h"
 #include "../lib/util/util_ldb.h"
+#include "rpc_server/samr/proto.h"
 
-/* 
-  samr_ChangePasswordUser 
+/*
+  samr_ChangePasswordUser
 */
-NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call, 
+NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
                                        TALLOC_CTX *mem_ctx,
                                        struct samr_ChangePasswordUser *r)
 {
@@ -60,23 +61,19 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
                return NT_STATUS_INVALID_PARAMETER_MIX;
        }
 
-       /* To change a password we need to open as system */
-       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));
+       /* Connect to a SAMDB with system privileges for fetching the old pw
+        * hashes. */
+       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), 0);
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
 
-       ret = ldb_transaction_start(sam_ctx);
-       if (ret != LDB_SUCCESS) {
-               DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
-               return NT_STATUS_TRANSACTION_ABORTED;
-       }
-
        /* fetch the old hashes */
        ret = gendb_search_dn(sam_ctx, mem_ctx,
                              a_state->account_dn, &res, attrs);
        if (ret != 1) {
-               ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
        }
 
@@ -84,7 +81,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
                                        dce_call->conn->dce_ctx->lp_ctx,
                                        res[0], &lm_pwd, &nt_pwd);
        if (!NT_STATUS_IS_OK(status) || !nt_pwd) {
-               ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
        }
 
@@ -93,7 +89,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
                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;
                }
        }
@@ -102,16 +97,14 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
        D_P16(nt_pwd->hash, r->in.new_nt_crypted->hash, new_ntPwdHash.hash);
        D_P16(new_ntPwdHash.hash, r->in.old_nt_crypted->hash, checkHash.hash);
        if (memcmp(checkHash.hash, nt_pwd, 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;
                }
        }
@@ -121,18 +114,33 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
        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;
                }
        }
 
-       /* setup password modify mods on the user DN specified.  This may fail
-        * due to password policies.  */
+       /* Start a SAM with user privileges for the password change */
+       sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+                               dce_call->conn->dce_ctx->lp_ctx,
+                               dce_call->conn->auth_state.session_info, 0);
+       if (sam_ctx == NULL) {
+               return NT_STATUS_INVALID_SYSTEM_SERVICE;
+       }
+
+       /* Start transaction */
+       ret = ldb_transaction_start(sam_ctx);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+               return NT_STATUS_TRANSACTION_ABORTED;
+       }
+
+       /* Performs the password modification. We pass the old hashes read out
+        * from the database since they were already checked against the user-
+        * provided ones. */
        status = samdb_set_password(sam_ctx, mem_ctx,
                                    a_state->account_dn,
                                    a_state->domain_state->domain_dn,
                                    NULL, &new_lmPwdHash, &new_ntPwdHash,
-                                   true, /* this is a user password change */
+                                   lm_pwd, nt_pwd, /* this is a user password change */
                                    NULL,
                                    NULL);
        if (!NT_STATUS_IS_OK(status)) {
@@ -152,8 +160,8 @@ NTSTATUS dcesrv_samr_ChangePasswordUser(struct dcesrv_call_state *dce_call,
        return NT_STATUS_OK;
 }
 
-/* 
-  samr_OemChangePasswordUser2 
+/*
+  samr_OemChangePasswordUser2
 */
 NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                                            TALLOC_CTX *mem_ctx,
@@ -173,6 +181,7 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
        uint8_t new_lm_hash[16];
        struct samr_Password lm_verifier;
        size_t unicode_pw_len;
+       size_t converted_size = 0;
 
        if (pwbuf == NULL) {
                return NT_STATUS_INVALID_PARAMETER;
@@ -187,27 +196,23 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
                return NT_STATUS_WRONG_PASSWORD;
        }
 
-       /* To change a password we need to open as system */
-       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));
+       /* Connect to a SAMDB with system privileges for fetching the old pw
+        * hashes. */
+       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), 0);
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
 
-       ret = ldb_transaction_start(sam_ctx);
-       if (ret != LDB_SUCCESS) {
-               DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
-               return NT_STATUS_TRANSACTION_ABORTED;
-       }
-
        /* we need the users dn and the domain dn (derived from the
           user SID). We also need the current lm password hash in
           order to decrypt the incoming password */
-       ret = gendb_search(sam_ctx, 
+       ret = gendb_search(sam_ctx,
                           mem_ctx, NULL, &res, attrs,
                           "(&(sAMAccountName=%s)(objectclass=user))",
                           r->in.account->string);
        if (ret != 1) {
-               ldb_transaction_cancel(sam_ctx);
                /* Don't give the game away:  (don't allow anonymous users to prove the existance of usernames) */
                return NT_STATUS_WRONG_PASSWORD;
        }
@@ -217,38 +222,34 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
        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;
        }
 
        /* decrypt the password we have been given */
-       lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash)); 
+       lm_pwd_blob = data_blob(lm_pwd->hash, sizeof(lm_pwd->hash));
        arcfour_crypt_blob(pwbuf->data, 516, &lm_pwd_blob);
        data_blob_free(&lm_pwd_blob);
-       
+
        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, lpcfg_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
-                                 CH_DOS, CH_UNIX, 
-                                 (const char *)new_password.data, 
+
+       if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
+                                 CH_DOS, CH_UNIX,
+                                 (const char *)new_password.data,
                                  new_password.length,
-                                 (void **)&new_pass, NULL, false)) {
+                                 (void **)&new_pass, &converted_size)) {
                DEBUG(3,("samr: failed to convert incoming password buffer to unix charset\n"));
-               ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
        }
 
-       if (!convert_string_talloc_convenience(mem_ctx, lpcfg_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
-                                              CH_DOS, CH_UTF16, 
-                                              (const char *)new_password.data, 
+       if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(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)) {
+                                              (void **)&new_unicode_password.data, &unicode_pw_len)) {
                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;
@@ -256,18 +257,33 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
        E_deshash(new_pass, new_lm_hash);
        E_old_pw_hash(new_lm_hash, lm_pwd->hash, lm_verifier.hash);
        if (memcmp(lm_verifier.hash, r->in.hash->hash, 16) != 0) {
-               ldb_transaction_cancel(sam_ctx);
                return NT_STATUS_WRONG_PASSWORD;
        }
 
-       /* set the password on the user DN specified.  This may fail
-        * due to password policies */
+       /* Connect to a SAMDB with user privileges for the password change */
+       sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+                               dce_call->conn->dce_ctx->lp_ctx,
+                               dce_call->conn->auth_state.session_info, 0);
+       if (sam_ctx == NULL) {
+               return NT_STATUS_INVALID_SYSTEM_SERVICE;
+       }
+
+       /* Start transaction */
+       ret = ldb_transaction_start(sam_ctx);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+               return NT_STATUS_TRANSACTION_ABORTED;
+       }
+
+       /* Performs the password modification. We pass the old hashes read out
+        * from the database since they were already checked against the user-
+        * provided ones. */
        status = samdb_set_password(sam_ctx, mem_ctx,
-                                   user_dn, NULL, 
+                                   user_dn, NULL,
                                    &new_unicode_password,
                                    NULL, NULL,
-                                   true, /* this is a user password change */
-                                   NULL, 
+                                   lm_pwd, NULL, /* this is a user password change */
+                                   NULL,
                                    NULL);
        if (!NT_STATUS_IS_OK(status)) {
                ldb_transaction_cancel(sam_ctx);
@@ -287,13 +303,13 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
 }
 
 
-/* 
-  samr_ChangePasswordUser3 
+/*
+  samr_ChangePasswordUser3
 */
-NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call, 
+NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
                                         TALLOC_CTX *mem_ctx,
                                         struct samr_ChangePasswordUser3 *r)
-{      
+{
        NTSTATUS status;
        DATA_BLOB new_password;
        struct ldb_context *sam_ctx = NULL;
@@ -317,23 +333,19 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
-       /* To change a password we need to open as system */
-       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));
+       /* Connect to a SAMDB with system privileges for fetching the old pw
+        * hashes. */
+       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), 0);
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
 
-       ret = ldb_transaction_start(sam_ctx);
-       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;
-       }
-
        /* we need the users dn and the domain dn (derived from the
           user SID). We also need the current lm and nt password hashes
           in order to decrypt the incoming passwords */
-       ret = gendb_search(sam_ctx, 
+       ret = gendb_search(sam_ctx,
                           mem_ctx, NULL, &res, attrs,
                           "(&(sAMAccountName=%s)(objectclass=user))",
                           r->in.account->string);
@@ -362,11 +374,11 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
        data_blob_free(&nt_pwd_blob);
 
        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"));
-               return NT_STATUS_WRONG_PASSWORD;
+               status =  NT_STATUS_WRONG_PASSWORD;
+               goto failed;
        }
-               
+
        if (r->in.nt_verifier == NULL) {
                status = NT_STATUS_WRONG_PASSWORD;
                goto failed;
@@ -386,11 +398,13 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
         * this) */
        if (lm_pwd && r->in.lm_verifier != NULL) {
                char *new_pass;
-               if (!convert_string_talloc_convenience(mem_ctx, lpcfg_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx),
-                                         CH_UTF16, CH_UNIX, 
-                                         (const char *)new_password.data, 
+               size_t converted_size = 0;
+
+               if (!convert_string_talloc_handle(mem_ctx, lpcfg_iconv_handle(dce_call->conn->dce_ctx->lp_ctx),
+                                         CH_UTF16, CH_UNIX,
+                                         (const char *)new_password.data,
                                          new_password.length,
-                                         (void **)&new_pass, NULL, false)) {
+                                         (void **)&new_pass, &converted_size)) {
                        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) {
@@ -400,17 +414,33 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
                }
        }
 
-       /* set the password on the user DN specified.  This may fail
-        * due to password policies */
+       /* Connect to a SAMDB with user privileges for the password change */
+       sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+                               dce_call->conn->dce_ctx->lp_ctx,
+                               dce_call->conn->auth_state.session_info, 0);
+       if (sam_ctx == NULL) {
+               return NT_STATUS_INVALID_SYSTEM_SERVICE;
+       }
+
+       ret = ldb_transaction_start(sam_ctx);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(1, ("Failed to start transaction: %s\n", ldb_errstring(sam_ctx)));
+               return NT_STATUS_TRANSACTION_ABORTED;
+       }
+
+       /* Performs the password modification. We pass the old hashes read out
+        * from the database since they were already checked against the user-
+        * provided ones. */
        status = samdb_set_password(sam_ctx, mem_ctx,
-                                   user_dn, NULL, 
+                                   user_dn, NULL,
                                    &new_password,
                                    NULL, NULL,
-                                   true, /* this is a user password change */
-                                   &reason, 
+                                   lm_pwd, nt_pwd, /* this is a user password change */
+                                   &reason,
                                    &dominfo);
 
        if (!NT_STATUS_IS_OK(status)) {
+               ldb_transaction_cancel(sam_ctx);
                goto failed;
        }
 
@@ -427,8 +457,6 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
        return NT_STATUS_OK;
 
 failed:
-       ldb_transaction_cancel(sam_ctx);
-
        reject = talloc_zero(mem_ctx, struct userPwdChangeFailureInformation);
        if (reject != NULL) {
                reject->extendedFailureReason = reason;
@@ -442,8 +470,8 @@ failed:
 }
 
 
-/* 
-  samr_ChangePasswordUser2 
+/*
+  samr_ChangePasswordUser2
 
   easy - just a subset of samr_ChangePasswordUser3
 */
@@ -494,14 +522,14 @@ NTSTATUS samr_set_password(struct dcesrv_call_state *dce_call,
                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, 
+                                 account_dn, domain_dn,
                                  &new_password,
                                  NULL, NULL,
-                                 false, /* This is a password set, not change */
+                                 NULL, NULL, /* This is a password set, not change */
                                  NULL, NULL);
 }
 
@@ -536,21 +564,21 @@ NTSTATUS samr_set_password_ex(struct dcesrv_call_state *dce_call,
        MD5Update(&ctx, &pwbuf->data[516], 16);
        MD5Update(&ctx, session_key.data, session_key.length);
        MD5Final(co_session_key.data, &ctx);
-       
+
        arcfour_crypt_blob(pwbuf->data, 516, &co_session_key);
 
        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, 
+                                 account_dn, domain_dn,
                                  &new_password,
                                  NULL, NULL,
-                                 false, /* This is a password set, not change */
+                                 NULL, NULL, /* This is a password set, not change */
                                  NULL, NULL);
 }
 
@@ -596,7 +624,7 @@ NTSTATUS samr_set_password_buffers(struct dcesrv_call_state *dce_call,
                nt_status = samdb_set_password(sam_ctx, mem_ctx, account_dn,
                                               domain_dn, NULL,
                                               d_lm_pwd_hash, d_nt_pwd_hash,
-                                              false, /* this is a password set */
+                                              NULL, NULL, /* this is a password set */
                                               NULL, NULL);
        }