summaryrefslogtreecommitdiffstats
path: root/libcli/drsuapi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /libcli/drsuapi
parentInitial commit. (diff)
downloadsamba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz
samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libcli/drsuapi')
-rw-r--r--libcli/drsuapi/drsuapi.h32
-rw-r--r--libcli/drsuapi/repl_decrypt.c391
-rw-r--r--libcli/drsuapi/tests/test_repl_decrypt.c522
-rw-r--r--libcli/drsuapi/wscript_build19
4 files changed, 964 insertions, 0 deletions
diff --git a/libcli/drsuapi/drsuapi.h b/libcli/drsuapi/drsuapi.h
new file mode 100644
index 0000000..3e1e5ce
--- /dev/null
+++ b/libcli/drsuapi/drsuapi.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+ Helper functions for applying replicated objects
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
+
+ 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/>.
+
+*/
+
+WERROR drsuapi_decrypt_attribute(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *gensec_skey,
+ uint32_t rid,
+ uint32_t dsdb_repl_flags,
+ struct drsuapi_DsReplicaAttribute *attr);
+
+
+WERROR drsuapi_encrypt_attribute(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *gensec_skey,
+ uint32_t rid,
+ struct drsuapi_DsReplicaAttribute *attr);
diff --git a/libcli/drsuapi/repl_decrypt.c b/libcli/drsuapi/repl_decrypt.c
new file mode 100644
index 0000000..30b3c64
--- /dev/null
+++ b/libcli/drsuapi/repl_decrypt.c
@@ -0,0 +1,391 @@
+/*
+ Unix SMB/CIFS implementation.
+ Helper functions for applying replicated objects
+
+ Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
+
+ 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 "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/gen_ndr/ndr_drsuapi.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "zlib.h"
+#include "../libcli/drsuapi/drsuapi.h"
+#include "libcli/auth/libcli_auth.h"
+#include "dsdb/samdb/samdb.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+static WERROR drsuapi_decrypt_attribute_value(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *gensec_skey,
+ bool rid_crypt,
+ uint32_t rid,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ DATA_BLOB confounder;
+ DATA_BLOB enc_buffer;
+
+ DATA_BLOB dec_buffer;
+
+ uint32_t crc32_given;
+ uint32_t crc32_calc;
+ DATA_BLOB checked_buffer;
+
+ DATA_BLOB plain_buffer;
+ WERROR result;
+ int rc;
+
+ /*
+ * users with rid == 0 should not exist
+ */
+ if (rid_crypt && rid == 0) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ /*
+ * the first 16 bytes at the beginning are the confounder
+ * followed by the 4 byte crc32 checksum
+ */
+ if (in->length < 20) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+ confounder = data_blob_const(in->data, 16);
+ enc_buffer = data_blob_const(in->data + 16, in->length - 16);
+
+ /*
+ * decrypt with the encryption key, being md5 over the session
+ * key followed by the confounder. The parameter order to
+ * samba_gnutls_arcfour_confounded_md5() matters for this!
+ *
+ * here the gensec session key is used and
+ * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
+ */
+
+ /*
+ * reference the encrypted buffer part and
+ * decrypt it using the created encryption key using arcfour
+ */
+ dec_buffer = data_blob_const(enc_buffer.data, enc_buffer.length);
+
+ rc = samba_gnutls_arcfour_confounded_md5(gensec_skey,
+ &confounder,
+ &dec_buffer,
+ SAMBA_GNUTLS_DECRYPT);
+ if (rc < 0) {
+ result = gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ goto out;
+ }
+
+ /*
+ * the first 4 byte are the crc32 checksum
+ * of the remaining bytes
+ */
+ crc32_given = IVAL(dec_buffer.data, 0);
+ crc32_calc = crc32(0, Z_NULL, 0);
+ crc32_calc = crc32(crc32_calc,
+ dec_buffer.data + 4 ,
+ dec_buffer.length - 4);
+ checked_buffer = data_blob_const(dec_buffer.data + 4, dec_buffer.length - 4);
+
+ plain_buffer = data_blob_talloc(mem_ctx, checked_buffer.data, checked_buffer.length);
+ W_ERROR_HAVE_NO_MEMORY(plain_buffer.data);
+
+ if (crc32_given != crc32_calc) {
+ result = W_ERROR(HRES_ERROR_V(HRES_SEC_E_DECRYPT_FAILURE));
+ goto out;
+ }
+ /*
+ * The following rid_crypt obfuscation isn't session specific
+ * and not really needed here, because we allways know the rid of the
+ * user account.
+ *
+ * some attributes with this 'additional encryption' include
+ * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
+ *
+ * But for the rest of samba it's easier when we remove this static
+ * obfuscation here
+ */
+ if (rid_crypt) {
+ uint32_t i, num_hashes;
+
+ if ((checked_buffer.length % 16) != 0) {
+ result = WERR_DS_DRA_INVALID_PARAMETER;
+ goto out;
+ }
+
+ num_hashes = plain_buffer.length / 16;
+ for (i = 0; i < num_hashes; i++) {
+ uint32_t offset = i * 16;
+ rc = sam_rid_crypt(rid, checked_buffer.data + offset,
+ plain_buffer.data + offset,
+ SAMBA_GNUTLS_DECRYPT);
+ if (rc != 0) {
+ result = gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ goto out;
+ }
+ }
+ }
+
+ *out = plain_buffer;
+ result = WERR_OK;
+out:
+ return result;
+}
+
+WERROR drsuapi_decrypt_attribute(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *gensec_skey,
+ uint32_t rid,
+ uint32_t dsdb_repl_flags,
+ struct drsuapi_DsReplicaAttribute *attr)
+{
+ WERROR status;
+ DATA_BLOB *enc_data;
+ DATA_BLOB plain_data;
+ bool rid_crypt = false;
+
+ if (attr->value_ctr.num_values == 0) {
+ return WERR_OK;
+ }
+
+ switch (attr->attid) {
+ case DRSUAPI_ATTID_dBCSPwd:
+ case DRSUAPI_ATTID_unicodePwd:
+ case DRSUAPI_ATTID_ntPwdHistory:
+ case DRSUAPI_ATTID_lmPwdHistory:
+ rid_crypt = true;
+ break;
+ case DRSUAPI_ATTID_supplementalCredentials:
+ case DRSUAPI_ATTID_priorValue:
+ case DRSUAPI_ATTID_currentValue:
+ case DRSUAPI_ATTID_trustAuthOutgoing:
+ case DRSUAPI_ATTID_trustAuthIncoming:
+ case DRSUAPI_ATTID_initialAuthOutgoing:
+ case DRSUAPI_ATTID_initialAuthIncoming:
+ break;
+ default:
+ return WERR_OK;
+ }
+
+ if (dsdb_repl_flags & DSDB_REPL_FLAG_EXPECT_NO_SECRETS) {
+ return WERR_TOO_MANY_SECRETS;
+ }
+
+ if (attr->value_ctr.num_values > 1) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ if (!attr->value_ctr.values[0].blob) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ enc_data = attr->value_ctr.values[0].blob;
+
+ status = drsuapi_decrypt_attribute_value(mem_ctx,
+ gensec_skey,
+ rid_crypt,
+ rid,
+ enc_data,
+ &plain_data);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ talloc_free(attr->value_ctr.values[0].blob->data);
+ *attr->value_ctr.values[0].blob = plain_data;
+
+ return WERR_OK;
+}
+
+static WERROR drsuapi_encrypt_attribute_value(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *gensec_skey,
+ bool rid_crypt,
+ uint32_t rid,
+ const DATA_BLOB *in,
+ DATA_BLOB *out)
+{
+ DATA_BLOB rid_crypt_out = data_blob(NULL, 0);
+ DATA_BLOB confounder;
+
+ DATA_BLOB enc_buffer;
+
+ DATA_BLOB to_encrypt;
+
+ uint32_t crc32_calc;
+ WERROR result;
+ int rc;
+
+ /*
+ * users with rid == 0 should not exist
+ */
+ if (rid_crypt && rid == 0) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ /*
+ * The following rid_crypt obfuscation isn't session specific
+ * and not really needed here, because we allways know the rid of the
+ * user account.
+ *
+ * some attributes with this 'additional encryption' include
+ * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
+ *
+ * But for the rest of samba it's easier when we remove this static
+ * obfuscation here
+ */
+ if (rid_crypt) {
+ uint32_t i, num_hashes;
+ rid_crypt_out = data_blob_talloc(mem_ctx, in->data, in->length);
+ W_ERROR_HAVE_NO_MEMORY(rid_crypt_out.data);
+
+ if ((rid_crypt_out.length % 16) != 0) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ num_hashes = rid_crypt_out.length / 16;
+ for (i = 0; i < num_hashes; i++) {
+ uint32_t offset = i * 16;
+ rc = sam_rid_crypt(rid, in->data + offset,
+ rid_crypt_out.data + offset,
+ SAMBA_GNUTLS_ENCRYPT);
+ if (rc != 0) {
+ result = gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ goto out;
+ }
+ }
+ in = &rid_crypt_out;
+ }
+
+ /*
+ * the first 16 bytes at the beginning are the confounder
+ * followed by the 4 byte crc32 checksum
+ */
+
+ enc_buffer = data_blob_talloc(mem_ctx, NULL, in->length+20);
+ if (!enc_buffer.data) {
+ talloc_free(rid_crypt_out.data);
+ return WERR_NOT_ENOUGH_MEMORY;
+ };
+
+ confounder = data_blob_const(enc_buffer.data, 16);
+ generate_random_buffer(confounder.data, confounder.length);
+
+ /*
+ * the first 4 byte are the crc32 checksum
+ * of the remaining bytes
+ */
+ crc32_calc = crc32(0, Z_NULL, 0);
+ crc32_calc = crc32(crc32_calc, in->data, in->length);
+ SIVAL(enc_buffer.data, 16, crc32_calc);
+
+ /*
+ * copy the plain buffer part and
+ * encrypt it using the created encryption key using arcfour
+ */
+ memcpy(enc_buffer.data+20, in->data, in->length);
+ talloc_free(rid_crypt_out.data);
+
+ to_encrypt = data_blob_const(enc_buffer.data+16,
+ enc_buffer.length-16);
+
+ /*
+ * encrypt with the encryption key, being md5 over the session
+ * key followed by the confounder. The parameter order to
+ * samba_gnutls_arcfour_confounded_md5() matters for this!
+ *
+ * here the gensec session key is used and
+ * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
+ */
+
+ rc = samba_gnutls_arcfour_confounded_md5(gensec_skey,
+ &confounder,
+ &to_encrypt,
+ SAMBA_GNUTLS_ENCRYPT);
+ if (rc < 0) {
+ result = gnutls_error_to_werror(rc, WERR_INTERNAL_ERROR);
+ goto out;
+ }
+
+ *out = enc_buffer;
+ result = WERR_OK;
+out:
+ return result;
+}
+
+/*
+ encrypt a DRSUAPI attribute ready for sending over the wire
+ Only some attribute types are encrypted
+ */
+WERROR drsuapi_encrypt_attribute(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *gensec_skey,
+ uint32_t rid,
+ struct drsuapi_DsReplicaAttribute *attr)
+{
+ WERROR status;
+ DATA_BLOB *plain_data;
+ DATA_BLOB enc_data;
+ bool rid_crypt = false;
+
+ if (attr->value_ctr.num_values == 0) {
+ return WERR_OK;
+ }
+
+ switch (attr->attid) {
+ case DRSUAPI_ATTID_dBCSPwd:
+ case DRSUAPI_ATTID_unicodePwd:
+ case DRSUAPI_ATTID_ntPwdHistory:
+ case DRSUAPI_ATTID_lmPwdHistory:
+ rid_crypt = true;
+ break;
+ case DRSUAPI_ATTID_supplementalCredentials:
+ case DRSUAPI_ATTID_priorValue:
+ case DRSUAPI_ATTID_currentValue:
+ case DRSUAPI_ATTID_trustAuthOutgoing:
+ case DRSUAPI_ATTID_trustAuthIncoming:
+ case DRSUAPI_ATTID_initialAuthOutgoing:
+ case DRSUAPI_ATTID_initialAuthIncoming:
+ break;
+ default:
+ return WERR_OK;
+ }
+
+ if (attr->value_ctr.num_values > 1) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ if (!attr->value_ctr.values[0].blob) {
+ return WERR_DS_DRA_INVALID_PARAMETER;
+ }
+
+ plain_data = attr->value_ctr.values[0].blob;
+
+ status = drsuapi_encrypt_attribute_value(mem_ctx,
+ gensec_skey,
+ rid_crypt,
+ rid,
+ plain_data,
+ &enc_data);
+ W_ERROR_NOT_OK_RETURN(status);
+
+ talloc_free(attr->value_ctr.values[0].blob->data);
+ *attr->value_ctr.values[0].blob = enc_data;
+
+ return WERR_OK;
+}
+
diff --git a/libcli/drsuapi/tests/test_repl_decrypt.c b/libcli/drsuapi/tests/test_repl_decrypt.c
new file mode 100644
index 0000000..996c6e6
--- /dev/null
+++ b/libcli/drsuapi/tests/test_repl_decrypt.c
@@ -0,0 +1,522 @@
+/*
+ * Unit tests for source4/rpc_server/dnsserver/dnsutils.c
+ *
+ * Copyright (C) Catalyst.NET Ltd 2018
+ * Copyright (C) Andrew Bartlett 2019
+ *
+ * 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/>.
+ *
+ */
+
+/*
+ * from cmocka.c:
+ * These headers or their equivalents should be included prior to
+ * including
+ * this header file.
+ *
+ * #include <stdarg.h>
+ * #include <stddef.h>
+ * #include <setjmp.h>
+ *
+ * This allows test applications to use custom definitions of C standard
+ * library functions and types.
+ *
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+
+#include "../repl_decrypt.c"
+
+
+/*
+ * test encryption and decryption including RID obfustincation
+ */
+static void test_drsuapi_rid_encrypt_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ uint8_t test_data[] = { 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04 };
+ const uint32_t rid = 514;
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB plaintext = data_blob_const(test_data,
+ sizeof(test_data));
+ DATA_BLOB encrypted;
+ DATA_BLOB decrypted = data_blob_null;
+
+ werr = drsuapi_encrypt_attribute_value(mem_ctx,
+ &key_blob,
+ true,
+ rid,
+ &plaintext,
+ &encrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_OK));
+ assert_int_not_equal(encrypted.length, plaintext.length);
+
+ werr = drsuapi_decrypt_attribute_value(mem_ctx,
+ &key_blob,
+ true,
+ rid,
+ &encrypted,
+ &decrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_OK));
+
+ assert_int_equal(decrypted.length, plaintext.length);
+
+ assert_memory_equal(decrypted.data, plaintext.data, plaintext.length);
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * test encryption and decryption failing RID obfustincation (data length)
+ */
+static void test_drsuapi_bad_len_rid_encrypt_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ uint8_t test_data[] = { 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x05 };
+ const uint32_t rid = 514;
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB plaintext = data_blob_const(test_data,
+ sizeof(test_data));
+ DATA_BLOB encrypted;
+
+ werr = drsuapi_encrypt_attribute_value(mem_ctx,
+ &key_blob,
+ true,
+ rid,
+ &plaintext,
+ &encrypted);
+
+ assert_int_equal(W_ERROR_V(werr),
+ W_ERROR_V(WERR_DS_DRA_INVALID_PARAMETER));
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * test encryption and decryption failing RID obfustincation (zero rid)
+ */
+static void test_drsuapi_zero_rid_encrypt_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ uint8_t test_data[] = { 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04 };
+ const uint32_t rid = 0;
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB plaintext = data_blob_const(test_data,
+ sizeof(test_data));
+ DATA_BLOB encrypted;
+
+ werr = drsuapi_encrypt_attribute_value(mem_ctx,
+ &key_blob,
+ true,
+ rid,
+ &plaintext,
+ &encrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_DS_DRA_INVALID_PARAMETER));
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * test encryption and decryption without RID obfustication
+ */
+static void test_drsuapi_encrypt_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ /* Ensures we can cope with odd lengths */
+ uint8_t test_data[] = { 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x05 };
+
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB plaintext = data_blob_const(test_data,
+ sizeof(test_data));
+ DATA_BLOB encrypted;
+ DATA_BLOB decrypted = data_blob_null;
+
+ werr = drsuapi_encrypt_attribute_value(mem_ctx,
+ &key_blob,
+ false,
+ 0,
+ &plaintext,
+ &encrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_OK));
+ assert_int_not_equal(encrypted.length, plaintext.length);
+
+ werr = drsuapi_decrypt_attribute_value(mem_ctx,
+ &key_blob,
+ false,
+ 0,
+ &encrypted,
+ &decrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_OK));
+
+ assert_int_equal(decrypted.length, plaintext.length);
+
+ assert_memory_equal(decrypted.data, plaintext.data, plaintext.length);
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * test decryption of fixed buffer
+ */
+static void test_drsuapi_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ /* Ensures we can cope with odd lengths */
+ uint8_t test_data[] = { 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x05 };
+
+ uint8_t encrypted_test_data[] = { 0xFF, 0x5C, 0x58, 0x3F,
+ 0xD4, 0x41, 0xCA, 0xB0,
+ 0x14, 0xFE, 0xFB, 0xA6,
+ 0xB0, 0x32, 0x45, 0x45,
+ 0x9D, 0x76, 0x75, 0xD2,
+ 0xFB, 0x34, 0x77, 0xBD,
+ 0x8C, 0x1E, 0x09, 0x1A,
+ 0xF1, 0xAB, 0xD3, 0x0E,
+ 0xBE, 0x80, 0xAB, 0x19, 0xFC };
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB plaintext = data_blob_const(test_data,
+ sizeof(test_data));
+ const DATA_BLOB encrypted
+ = data_blob_const(encrypted_test_data,
+ sizeof(encrypted_test_data));
+ DATA_BLOB decrypted = data_blob_null;
+
+ werr = drsuapi_decrypt_attribute_value(mem_ctx,
+ &key_blob,
+ false,
+ 0,
+ &encrypted,
+ &decrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_OK));
+
+ assert_int_equal(decrypted.length, plaintext.length);
+
+ assert_memory_equal(decrypted.data, plaintext.data, plaintext.length);
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * test decryption of fixed buffer (rid decrypt)
+ */
+static void test_drsuapi_rid_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ /* Ensures we can cope with odd lengths */
+ uint8_t test_data[] = { 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04 };
+
+ uint8_t encrypted_test_data[] = {0x95, 0xB2, 0xE8, 0x02,
+ 0x05, 0x5E, 0xFD, 0x3D,
+ 0x7D, 0x17, 0xB9, 0x76,
+ 0x4D, 0x91, 0xED, 0x59,
+ 0x98, 0x79, 0x7A, 0xFC,
+ 0x38, 0x73, 0x28, 0x55,
+ 0x62, 0x27, 0x99, 0x3B,
+ 0xD0, 0x18, 0xBD, 0x23,
+ 0x5D, 0x98, 0xFE, 0xA8};
+
+ const uint32_t rid = 514;
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB plaintext = data_blob_const(test_data,
+ sizeof(test_data));
+ const DATA_BLOB encrypted
+ = data_blob_const(encrypted_test_data,
+ sizeof(encrypted_test_data));
+ DATA_BLOB decrypted = data_blob_null;
+
+ werr = drsuapi_decrypt_attribute_value(mem_ctx,
+ &key_blob,
+ true,
+ rid,
+ &encrypted,
+ &decrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_OK));
+
+ assert_int_equal(decrypted.length, plaintext.length);
+
+ assert_memory_equal(decrypted.data, plaintext.data, plaintext.length);
+
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * test decryption of fixed buffer (rid decrypt)
+ */
+static void test_drsuapi_bad_len_rid_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ uint8_t encrypted_test_data[] = { 0xFF, 0x5C, 0x58, 0x3F,
+ 0xD4, 0x41, 0xCA, 0xB0,
+ 0x14, 0xFE, 0xFB, 0xA6,
+ 0xB0, 0x32, 0x45, 0x45,
+ 0x9D, 0x76, 0x75, 0xD2,
+ 0xFB, 0x34, 0x77, 0xBD,
+ 0x8C, 0x1E, 0x09, 0x1A,
+ 0xF1, 0xAB, 0xD3, 0x0E,
+ 0xBE, 0x80, 0xAB, 0x19, 0xFC };
+
+ const uint32_t rid = 514;
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB encrypted
+ = data_blob_const(encrypted_test_data,
+ sizeof(encrypted_test_data));
+ DATA_BLOB decrypted;
+
+ werr = drsuapi_decrypt_attribute_value(mem_ctx,
+ &key_blob,
+ true,
+ rid,
+ &encrypted,
+ &decrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_DS_DRA_INVALID_PARAMETER));
+
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * test decryption of fixed buffer (rid decrypt)
+ */
+static void test_drsuapi_zero_rid_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ uint8_t encrypted_test_data[] = { 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x05 };
+ const uint32_t rid = 0;
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB encrypted
+ = data_blob_const(encrypted_test_data,
+ sizeof(encrypted_test_data));
+ DATA_BLOB decrypted;
+
+ werr = drsuapi_decrypt_attribute_value(mem_ctx,
+ &key_blob,
+ true,
+ rid,
+ &encrypted,
+ &decrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_DS_DRA_INVALID_PARAMETER));
+
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * test decryption of fixed buffer (bad crc)
+ */
+static void test_drsuapi_bad_crc_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ uint8_t encrypted_test_data[] = { 0xFF, 0x5C, 0x58, 0x3F,
+ 0xD4, 0x41, 0xCA, 0xB0,
+ 0x14, 0xFE, 0xFB, 0xA6,
+ 0xB0, 0x32, 0x45, 0x45,
+ 0x9D, 0x76, 0x75, 0xD2,
+ 0xFB, 0x34, 0x77, 0xBD,
+ 0x8C, 0x1E, 0x09, 0x1A,
+ 0xF1, 0xAB, 0xD3, 0x0E,
+ 0xBE, 0x80, 0xAB, 0x19, 0xFF };
+
+ const uint32_t rid = 514;
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB encrypted
+ = data_blob_const(encrypted_test_data,
+ sizeof(encrypted_test_data));
+ DATA_BLOB decrypted;
+
+ werr = drsuapi_decrypt_attribute_value(mem_ctx,
+ &key_blob,
+ true,
+ rid,
+ &encrypted,
+ &decrypted);
+
+ assert_int_equal(W_ERROR_V(werr), HRES_ERROR_V(HRES_SEC_E_DECRYPT_FAILURE));
+
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * test decryption of short buffer
+ */
+static void test_drsuapi_short_decrypt_attribute_value(void **state)
+{
+ uint8_t key[] = { 0xa1, 0xb2, 0xc3, 0xd4,
+ 0xe1, 0xf2, 0x03, 0x14,
+ 0x21, 0x32, 0x43, 0x54,
+ 0x61, 0x72, 0x83, 0x94 };
+
+ uint8_t encrypted_test_data[] = { 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x01, 0x02, 0x03, 0x04, 0x05 };
+ const uint32_t rid = 514;
+
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+
+ WERROR werr;
+
+ const DATA_BLOB key_blob = data_blob_const(key, sizeof(key));
+ const DATA_BLOB encrypted
+ = data_blob_const(encrypted_test_data,
+ sizeof(encrypted_test_data));
+ DATA_BLOB decrypted;
+
+ werr = drsuapi_decrypt_attribute_value(mem_ctx,
+ &key_blob,
+ true,
+ rid,
+ &encrypted,
+ &decrypted);
+
+ assert_int_equal(W_ERROR_V(werr), W_ERROR_V(WERR_DS_DRA_INVALID_PARAMETER));
+
+ TALLOC_FREE(mem_ctx);
+}
+
+int main(int argc, const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(
+ test_drsuapi_rid_encrypt_decrypt_attribute_value),
+ cmocka_unit_test(
+ test_drsuapi_bad_len_rid_encrypt_decrypt_attribute_value),
+ cmocka_unit_test(
+ test_drsuapi_zero_rid_encrypt_decrypt_attribute_value),
+ cmocka_unit_test(
+ test_drsuapi_encrypt_decrypt_attribute_value),
+ cmocka_unit_test(
+ test_drsuapi_decrypt_attribute_value),
+ cmocka_unit_test(
+ test_drsuapi_bad_crc_decrypt_attribute_value),
+ cmocka_unit_test(
+ test_drsuapi_rid_decrypt_attribute_value),
+ cmocka_unit_test(
+ test_drsuapi_zero_rid_decrypt_attribute_value),
+ cmocka_unit_test(
+ test_drsuapi_bad_len_rid_decrypt_attribute_value),
+ cmocka_unit_test(
+ test_drsuapi_short_decrypt_attribute_value),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/libcli/drsuapi/wscript_build b/libcli/drsuapi/wscript_build
new file mode 100644
index 0000000..1aee095
--- /dev/null
+++ b/libcli/drsuapi/wscript_build
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+
+
+bld.SAMBA_SUBSYSTEM('LIBCLI_DRSUAPI',
+ source='repl_decrypt.c',
+ public_deps='LIBCLI_AUTH samdb z'
+ )
+
+bld.SAMBA_BINARY(
+ 'test_repl_decrypt',
+ source='tests/test_repl_decrypt.c',
+ deps='''
+ LIBCLI_DRSUAPI
+ cmocka
+ talloc
+ ''',
+ for_selftest=True,
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+)