]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
CVE-2021-20251 lib:crypto: Add Python functions for AES SAMR password change
authorJoseph Sutton <josephsutton@catalyst.net.nz>
Tue, 2 Aug 2022 02:35:19 +0000 (14:35 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 12 Sep 2022 23:07:37 +0000 (23:07 +0000)
These functions allow us to perform key derivation and AES256 encryption
in Python. They will be used in a following commit.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14611

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andreas Schneider <asn@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
lib/crypto/py_crypto.c

index 40b0cb9e9c03beda6482587e11ee358f52172ece..116595568840bddd4446726dd294c7f549cedc99 100644 (file)
 #include "lib/crypto/gnutls_helpers.h"
 #include "lib/crypto/md4.h"
 #include "libcli/auth/libcli_auth.h"
+#include "libcli/util/pyerrors.h"
+
+#ifdef HAVE_GNUTLS_PBKDF2
+static bool samba_gnutls_datum_from_PyObject(PyObject *py_obj,
+                                            gnutls_datum_t *datum)
+{
+       uint8_t *data = NULL;
+       Py_ssize_t size;
+
+       int ret;
+
+       ret = PyBytes_AsStringAndSize(py_obj,
+                                     (char **)&data,
+                                     &size);
+       if (ret != 0) {
+               return false;
+       }
+
+       datum->data = data;
+       datum->size = size;
+
+       return true;
+}
+#endif /* HAVE_GNUTLS_PBKDF2 */
+
+static bool samba_DATA_BLOB_from_PyObject(PyObject *py_obj,
+                                         DATA_BLOB *blob)
+{
+       uint8_t *data = NULL;
+       Py_ssize_t size;
+
+       int ret;
+
+       ret = PyBytes_AsStringAndSize(py_obj,
+                                     (char **)&data,
+                                     &size);
+       if (ret != 0) {
+               return false;
+       }
+
+       blob->data = data;
+       blob->length = size;
+
+       return true;
+}
 
 static PyObject *py_crypto_arcfour_crypt_blob(PyObject *module, PyObject *args)
 {
@@ -191,6 +236,165 @@ static PyObject *py_crypto_md4_hash_blob(PyObject *self, PyObject *args)
                                         sizeof(result));
 }
 
+static PyObject *py_crypto_sha512_pbkdf2(PyObject *self, PyObject *args)
+{
+#ifdef HAVE_GNUTLS_PBKDF2
+       PyObject *py_key = NULL;
+       uint8_t *key = NULL;
+       gnutls_datum_t key_datum = {0};
+
+       PyObject *py_salt = NULL;
+       gnutls_datum_t salt_datum = {0};
+
+       uint8_t result[16];
+
+       unsigned iterations = 0;
+
+       bool ok;
+       int ret;
+       NTSTATUS status;
+
+       ok = PyArg_ParseTuple(args, "SSI",
+                             &py_key, &py_salt, &iterations);
+       if (!ok) {
+               return NULL;
+       }
+
+       ok = samba_gnutls_datum_from_PyObject(py_key, &key_datum);
+       if (!ok) {
+               return NULL;
+       }
+
+       ok = samba_gnutls_datum_from_PyObject(py_salt, &salt_datum);
+       if (!ok) {
+               return NULL;
+       }
+
+       ret = gnutls_pbkdf2(GNUTLS_MAC_SHA512,
+                           &key_datum,
+                           &salt_datum,
+                           iterations,
+                           result,
+                           sizeof(result));
+       BURN_DATA(key);
+       if (ret < 0) {
+               status = gnutls_error_to_ntstatus(ret, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+               PyErr_SetNTSTATUS(status);
+               return NULL;
+       }
+
+       return PyBytes_FromStringAndSize((const char *)result,
+                                        sizeof(result));
+#else /* HAVE_GNUTLS_PBKDF2 */
+       PyErr_SetString(PyExc_NotImplementedError, "gnutls_pbkdf2() is not available");
+       return NULL;
+#endif /* HAVE_GNUTLS_PBKDF2 */
+}
+
+static PyObject *py_crypto_aead_aes_256_cbc_hmac_sha512_blob(PyObject *self, PyObject *args)
+{
+       TALLOC_CTX *ctx = NULL;
+
+       PyObject *py_ciphertext = NULL;
+       DATA_BLOB ciphertext_blob = {0};
+
+       PyObject *py_auth_data = NULL;
+       PyObject *py_result = NULL;
+
+       PyObject *py_plaintext = NULL;
+       DATA_BLOB plaintext_blob = {0};
+       PyObject *py_cek = NULL;
+       DATA_BLOB cek_blob = {0};
+       PyObject *py_key_salt = NULL;
+       DATA_BLOB key_salt_blob = {0};
+       PyObject *py_mac_salt = NULL;
+       DATA_BLOB mac_salt_blob = {0};
+       PyObject *py_iv = NULL;
+       DATA_BLOB iv_blob = {0};
+
+       uint8_t auth_data[64];
+
+       bool ok;
+       NTSTATUS status;
+
+       ok = PyArg_ParseTuple(args, "SSSSS",
+                             &py_plaintext,
+                             &py_cek,
+                             &py_key_salt,
+                             &py_mac_salt,
+                             &py_iv);
+       if (!ok) {
+               return NULL;
+       }
+
+       /* Create data blobs from the contents of the function parameters. */
+
+       ok = samba_DATA_BLOB_from_PyObject(py_plaintext, &plaintext_blob);
+       if (!ok) {
+               return NULL;
+       }
+
+       ok = samba_DATA_BLOB_from_PyObject(py_cek, &cek_blob);
+       if (!ok) {
+               return NULL;
+       }
+
+       ok = samba_DATA_BLOB_from_PyObject(py_key_salt, &key_salt_blob);
+       if (!ok) {
+               return NULL;
+       }
+
+       ok = samba_DATA_BLOB_from_PyObject(py_mac_salt, &mac_salt_blob);
+       if (!ok) {
+               return NULL;
+       }
+
+       ok = samba_DATA_BLOB_from_PyObject(py_iv, &iv_blob);
+       if (!ok) {
+               return NULL;
+       }
+
+       ctx = talloc_new(NULL);
+       if (ctx == NULL) {
+               return PyErr_NoMemory();
+       }
+
+       /* Encrypt the plaintext. */
+       status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt(ctx,
+                                                                  &plaintext_blob,
+                                                                  &cek_blob,
+                                                                  &key_salt_blob,
+                                                                  &mac_salt_blob,
+                                                                  &iv_blob,
+                                                                  &ciphertext_blob,
+                                                                  auth_data);
+       if (!NT_STATUS_IS_OK(status)) {
+               PyErr_SetNTSTATUS(status);
+               talloc_free(ctx);
+               return NULL;
+       }
+
+       /* Convert the output into Python 'bytes' objects. */
+       py_ciphertext = PyBytes_FromStringAndSize((const char *)ciphertext_blob.data,
+                                                 ciphertext_blob.length);
+       talloc_free(ctx);
+       if (py_ciphertext == NULL) {
+               return NULL;
+       }
+       py_auth_data = PyBytes_FromStringAndSize((const char *)auth_data,
+                                                sizeof(auth_data));
+       if (py_auth_data == NULL) {
+               return NULL;
+       }
+
+       /* Steal ciphertext and auth_data into a new tuple. */
+       py_result = Py_BuildValue("(NN)", py_ciphertext, py_auth_data);
+
+       return py_result;
+}
+
+
+
 static const char py_crypto_arcfour_crypt_blob_doc[] = "arcfour_crypt_blob(data, key)\n"
                                         "Encrypt the data with RC4 algorithm using the key";
 
@@ -201,12 +405,29 @@ static const char py_crypto_des_crypt_blob_16_doc[] = "des_crypt_blob_16(data, k
 static const char py_crypto_md4_hash_blob_doc[] = "md4_hash_blob(data) -> bytes\n"
                                                  "Hash the data with MD4 algorithm";
 
+static const char py_crypto_sha512_pbkdf2_doc[] = "sha512_pbkdf2(key, salt, iterations) -> bytes\n"
+                                                 "Derive a key from an existing one with SHA512 "
+                                                 "algorithm";
+
+static const char py_crypto_aead_aes_256_cbc_hmac_sha512_blob_doc[] =
+       "aead_aes_256_cbc_hmac_sha512_blob(plaintext, cek, key_salt, "
+       "mac_salt, iv) -> ciphertext, auth_data\n"
+       "Encrypt the plaintext with AES256 as specified in "
+       "[MS-SAMR] 3.2.2.4 AES Cipher Usage";
+
 static PyMethodDef py_crypto_methods[] = {
        { "arcfour_crypt_blob", (PyCFunction)py_crypto_arcfour_crypt_blob, METH_VARARGS, py_crypto_arcfour_crypt_blob_doc },
        { "set_relax_mode", (PyCFunction)py_crypto_set_relax_mode, METH_NOARGS, "Set fips to relax mode" },
        { "set_strict_mode", (PyCFunction)py_crypto_set_strict_mode, METH_NOARGS, "Set fips to strict mode" },
        { "des_crypt_blob_16", (PyCFunction)py_crypto_des_crypt_blob_16, METH_VARARGS, py_crypto_des_crypt_blob_16_doc },
        { "md4_hash_blob", (PyCFunction)py_crypto_md4_hash_blob, METH_VARARGS, py_crypto_md4_hash_blob_doc },
+       { "sha512_pbkdf2", (PyCFunction)py_crypto_sha512_pbkdf2, METH_VARARGS, py_crypto_sha512_pbkdf2_doc },
+       {
+               "aead_aes_256_cbc_hmac_sha512_blob",
+               (PyCFunction)py_crypto_aead_aes_256_cbc_hmac_sha512_blob,
+               METH_VARARGS,
+               py_crypto_aead_aes_256_cbc_hmac_sha512_blob_doc
+       },
        {0},
 };