]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s3:rpc_server: Implement dcesrv_samr_ChangePasswordUser4()
authorAndreas Schneider <asn@samba.org>
Mon, 28 Feb 2022 12:51:40 +0000 (13:51 +0100)
committerAndreas Schneider <asn@cryptomilk.org>
Thu, 28 Jul 2022 11:51:29 +0000 (11:51 +0000)
Signed-off-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
source3/rpc_server/samr/srv_samr_chgpasswd.c
source3/rpc_server/samr/srv_samr_nt.c
source3/rpc_server/samr/srv_samr_util.h

index 56dc7397b9dc57b5e7bd2aa6524a10d477ce23af..f1d833a630cf3f43ae136603ce9608f6f8eb5649 100644 (file)
@@ -54,6 +54,7 @@
 #include "passdb.h"
 #include "auth.h"
 #include "lib/util/sys_rw.h"
+#include "librpc/rpc/dcerpc_samr.h"
 
 #include "lib/crypto/gnutls_helpers.h"
 #include <gnutls/gnutls.h>
@@ -1297,3 +1298,63 @@ NTSTATUS pass_oem_change(char *user, const char *rhost,
 
        return nt_status;
 }
+
+NTSTATUS samr_set_password_aes(TALLOC_CTX *mem_ctx,
+                              struct samu *sampass,
+                              const char *rhost,
+                              const DATA_BLOB *cdk,
+                              struct samr_EncryptedPasswordAES *pwbuf,
+                              enum samPwdChangeReason *reject_reason)
+{
+       DATA_BLOB pw_data = data_blob_null;
+       DATA_BLOB new_password = data_blob_null;
+       const DATA_BLOB ciphertext =
+               data_blob_const(pwbuf->cipher, pwbuf->cipher_len);
+       DATA_BLOB iv = data_blob_const(pwbuf->salt, sizeof(pwbuf->salt));
+       char *new_password_str = NULL;
+       NTSTATUS status;
+       bool ok;
+
+       status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_decrypt(
+               mem_ctx,
+               &ciphertext,
+               cdk,
+               &samr_aes256_enc_key_salt,
+               &samr_aes256_mac_key_salt,
+               &iv,
+               pwbuf->auth_data,
+               &pw_data);
+       if (!NT_STATUS_IS_OK(status)) {
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+
+       ok = decode_pwd_string_from_buffer514(mem_ctx,
+                                             pw_data.data,
+                                             CH_UTF16,
+                                             &new_password);
+       TALLOC_FREE(pw_data.data);
+       if (!ok) {
+               DBG_NOTICE("samr: failed to decode password buffer\n");
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+
+       new_password_str = talloc_strndup(mem_ctx,
+                                         (char *)new_password.data,
+                                         new_password.length);
+       TALLOC_FREE(new_password.data);
+       if (new_password_str == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       become_root();
+       status = change_oem_password(sampass,
+                                    rhost,
+                                    NULL,
+                                    new_password_str,
+                                    true,
+                                    reject_reason);
+       unbecome_root();
+       TALLOC_FREE(new_password_str);
+
+       return status;
+}
index ee92014f7621e6a8cfc2e7164a040f7311cb0142..4f8605eb576aa3f384d6a35bc51f72c32d136fcd 100644 (file)
@@ -7677,8 +7677,115 @@ void _samr_Opnum72NotUsedOnWire(struct pipes_struct *p,
 NTSTATUS _samr_ChangePasswordUser4(struct pipes_struct *p,
                                   struct samr_ChangePasswordUser4 *r)
 {
+#ifdef HAVE_GNUTLS_PBKDF2
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct dcesrv_call_state *dce_call = p->dce_call;
+       struct dcesrv_connection *dcesrv_conn = dce_call->conn;
+       const struct tsocket_address *remote_address =
+               dcesrv_connection_get_remote_address(dcesrv_conn);
+       enum samPwdChangeReason reject_reason;
+       char *rhost = NULL;
+       struct samu *sampass = NULL;
+       char *username = NULL;
+       uint32_t acct_ctrl = 0;
+       const uint8_t *nt_pw = NULL;
+       gnutls_datum_t nt_key;
+       gnutls_datum_t salt = {
+               .data = r->in.password->salt,
+               .size = sizeof(r->in.password->salt),
+       };
+       uint8_t cdk_data[16] = {0};
+       DATA_BLOB cdk = {
+               .data = cdk_data,
+               .length = sizeof(cdk_data),
+       };
+       NTSTATUS status = NT_STATUS_WRONG_PASSWORD;
+       bool ok;
+       int rc;
+
+       r->out.result = NT_STATUS_WRONG_PASSWORD;
+
+       DBG_NOTICE("_samr_ChangePasswordUser4\n");
+
+       if (r->in.account->string == NULL) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       if (r->in.password == NULL) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       if (r->in.password->PBKDF2Iterations < 5000 ||
+           r->in.password->PBKDF2Iterations > 1000000) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       (void)map_username(frame, r->in.account->string, &username);
+       if (username == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       rhost = tsocket_address_inet_addr_string(remote_address, frame);
+       if (rhost == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+       sampass = samu_new(frame);
+       if (sampass == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       become_root();
+       ok = pdb_getsampwnam(sampass, username);
+       unbecome_root();
+       if (!ok) {
+               status = NT_STATUS_NO_SUCH_USER;
+               goto done;
+       }
+
+       acct_ctrl = pdb_get_acct_ctrl(sampass);
+       if (acct_ctrl & ACB_AUTOLOCK) {
+               status = NT_STATUS_ACCOUNT_LOCKED_OUT;
+               goto done;
+       }
+
+       nt_pw = pdb_get_nt_passwd(sampass);
+       nt_key = (gnutls_datum_t){
+               .data = discard_const_p(uint8_t, nt_pw),
+               .size = NT_HASH_LEN,
+       };
+
+       rc = gnutls_pbkdf2(GNUTLS_MAC_SHA512,
+                          &nt_key,
+                          &salt,
+                          r->in.password->PBKDF2Iterations,
+                          cdk.data,
+                          cdk.length);
+       if (rc < 0) {
+               BURN_DATA(cdk_data);
+               status = NT_STATUS_WRONG_PASSWORD;
+               goto done;
+       }
+
+       status = samr_set_password_aes(frame,
+                                      sampass,
+                                      rhost,
+                                      &cdk,
+                                      r->in.password,
+                                      &reject_reason);
+       BURN_DATA(cdk_data);
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+               return NT_STATUS_WRONG_PASSWORD;
+       }
+
+done:
+       TALLOC_FREE(frame);
+
+       return status;
+#else  /* HAVE_GNUTLS_PBKDF2 */
        p->fault_state = DCERPC_FAULT_OP_RNG_ERROR;
        return NT_STATUS_NOT_IMPLEMENTED;
+#endif /* HAVE_GNUTLS_PBKDF2 */
 }
 
 /* include the generated boilerplate */
index 475e2c3728dd7186c01d3b84e0363251080b9626..a702d5d449c752038438117459cd3813a9c5feec 100644 (file)
@@ -79,3 +79,9 @@ NTSTATUS check_password_complexity(const char *username,
                                   const char *fullname,
                                   const char *password,
                                   enum samPwdChangeReason *samr_reject_reason);
+NTSTATUS samr_set_password_aes(TALLOC_CTX *mem_ctx,
+                              struct samu *sampass,
+                              const char *rhost,
+                              const DATA_BLOB *cdk,
+                              struct samr_EncryptedPasswordAES *pwbuf,
+                              enum samPwdChangeReason *reject_reason);