Implement samba.crypto.arcfour_crypt_blob for Python access

Alexander Bokovoy ab at samba.org
Fri Mar 10 14:43:34 UTC 2017


Hi,

attached patch improves availability of Samba AD in FIPS 140-2 environment.

To establish trust relationship, we call CreateTrustedDomainEx2 LSA
call. This call requires to encrypt AuthenticationInformation blob with
RC4 cipher. While Samba C code does use lib/crypto/arcfour.c to have
independent RC4 implementation, Python code relies on system Python
libraries to get access to RC4 cipher.

In FIPS 140-2 compliant environment all non-compliant ciphers are
disabled and calling them causes an error. Thus, encrypting
AuthenticationInformation blob with RC4 is not possible in this
environment.

Use of RC4 is part of the MS-LSAD 5.1.1:
----------
Implementations of this protocol protect the LSAPR_TRUSTED_DOMAIN_AUTH_BLOB
structure by encrypting the data referenced by that structure's AuthBlob field.
The RC4 algorithm is used to encrypt the data on request (and reply) and
decrypt the data on receipt. The key, required during runtime by the RC4
algorithm, is the 16-byte key specified by the method that uses this
structure (for example, see section 3.1.4.7.10). The size of data (the
AuthSize field of LSAPR_TRUSTED_DOMAIN_AUTH_BLOB) must remain unencrypted.
----------

I asked Microsoft dochelp team on the matter and got an answer:

----------
FIPS mode does not change Windows Server product behavior with regards
to MS-LSAD 5.1.1.

LSAD goes over RPCE, which in turn goes over SMB/SMB2 transport. The
protocol requires packet integrity or encryption at the RPCE level.
MS-SMB/CIFS and MS-SMB2 (and its related authentication protocols)
define what cryptographic algorithms are used respectively by each
dialect of the protocol. As specified in the specs, each negotiated
protocol parameters indicates what crypto is used. This does not depend
on any FIPS mode configuration.

On Windows, SMB1 can be disabled by configuration if desired, but this
is purely driven by known security limitations with SMB1 protocol,
rather than FIPS enforcing any policy.

The encrypted blob (LSAPR_TRUSTED_DOMAIN_AUTH_BLOB structure you are
referring to) is RC4-encrypted at the application level using the key
from that RPC binding session.

The encryption key is the session key from the RPC binding policy
handle.
-----------

As we have RC4 implementation on application level already, exposing it
to Python code allows us to solve the availability problem.

-- 
/ Alexander Bokovoy
-------------- next part --------------
>From 581d83850354f38189016f680f101a134151d377 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <ab at samba.org>
Date: Fri, 10 Mar 2017 16:20:06 +0200
Subject: [PATCH] lib/crypto: implement samba.crypto Python module for RC4

Implement a small Python module that exposes arcfour_crypt_blob()
function widely used in Samba C code.

When Samba Python bindings are used to call LSA CreateTrustedDomainEx2,
there is a need to encrypt trusted credentials with RC4 cipher.

Current Samba Python code relies on Python runtime to provide RC4
cipher. However, in FIPS 140-2 mode system crypto libraries do not
provide access RC4 cipher at all. According to Microsoft dochelp team,
Windows is treating AuthenticationInformation blob encryption as 'plain
text' in terms of FIPS 140-2, thus doing application-level encryption.

Replace samba.arcfour_encrypt() implementation with a call to
samba.crypto.arcfour_crypt_blob().

Signed-off-by: Alexander Bokovoy <ab at samba.org>
---
 lib/crypto/py_crypto.c   | 90 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/crypto/wscript_build |  7 ++++
 python/samba/__init__.py | 16 ++-------
 3 files changed, 99 insertions(+), 14 deletions(-)
 create mode 100644 lib/crypto/py_crypto.c

diff --git a/lib/crypto/py_crypto.c b/lib/crypto/py_crypto.c
new file mode 100644
index 0000000..bf7f9f4
--- /dev/null
+++ b/lib/crypto/py_crypto.c
@@ -0,0 +1,90 @@
+/*
+   Unix SMB/CIFS implementation.
+   Samba crypto functions
+
+   Copyright (C) Alexander Bokovoy <ab at samba.org> 2017
+
+   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 <Python.h>
+#include "includes.h"
+#include "python/py3compat.h"
+#include "lib/crypto/arcfour.h"
+
+static PyObject *py_crypto_arcfour_crypt_blob(PyObject *module, PyObject *args, PyObject *kwargs)
+{
+	DATA_BLOB data, key;
+	PyObject *py_data, *py_key, *result;
+	TALLOC_CTX *ctx;
+
+	if (!PyArg_ParseTuple(args, "OO", &py_data, &py_key))
+		return NULL;
+
+	if (!PyBytes_Check(py_data)) {
+		PyErr_Format(PyExc_TypeError, "bytes expected");
+		return NULL;
+	}
+
+	if (!PyBytes_Check(py_key)) {
+		PyErr_Format(PyExc_TypeError, "bytes expected");
+		return NULL;
+	}
+
+	ctx = talloc_new(NULL);
+
+	data.length = PyBytes_Size(py_data);
+	data.data = talloc_memdup(ctx, PyBytes_AsString(py_data), data.length);
+	if (!data.data) {
+		talloc_free(ctx);
+		return PyErr_NoMemory();
+	}
+
+	key.data = (uint8_t *)PyBytes_AsString(py_key);
+	key.length = PyBytes_Size(py_key);
+
+	arcfour_crypt_blob(data.data, data.length, &key);
+
+	result = PyBytes_FromStringAndSize((const char*) data.data, data.length);
+	talloc_free(ctx);
+	return 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";
+
+static PyMethodDef py_crypto_methods[] = {
+	{ "arcfour_crypt_blob", (PyCFunction)py_crypto_arcfour_crypt_blob, METH_VARARGS, py_crypto_arcfour_crypt_blob_doc },
+	{ NULL },
+};
+
+static struct PyModuleDef moduledef = {
+	PyModuleDef_HEAD_INIT,
+	.m_name = "crypto",
+	.m_doc = "Crypto functions required for SMB",
+	.m_size = -1,
+	.m_methods = py_crypto_methods,
+};
+
+MODULE_INIT_FUNC(crypto)
+{
+	PyObject *m;
+
+	m = PyModule_Create(&moduledef);
+	if (m == NULL)
+		return NULL;
+
+	return m;
+}
diff --git a/lib/crypto/wscript_build b/lib/crypto/wscript_build
index 7f94532..d1f152e 100644
--- a/lib/crypto/wscript_build
+++ b/lib/crypto/wscript_build
@@ -25,3 +25,10 @@ bld.SAMBA_SUBSYSTEM('TORTURE_LIBCRYPTO',
         autoproto='test_proto.h',
         deps='LIBCRYPTO'
         )
+
+for env in bld.gen_python_environments():
+	bld.SAMBA_PYTHON('python_crypto',
+		source='py_crypto.c',
+		deps='LIBCRYPTO',
+		realname='samba/crypto.so'
+		)
diff --git a/python/samba/__init__.py b/python/samba/__init__.py
index 3535704..67aa823 100644
--- a/python/samba/__init__.py
+++ b/python/samba/__init__.py
@@ -375,20 +375,8 @@ def string_to_byte_array(string):
     return blob
 
 def arcfour_encrypt(key, data):
-    try:
-        from Crypto.Cipher import ARC4
-        c = ARC4.new(key)
-        return c.encrypt(data)
-    except ImportError as e:
-        pass
-    try:
-        from M2Crypto.RC4 import RC4
-        c = RC4(key)
-        return c.update(data)
-    except ImportError as e:
-        pass
-    raise Exception("arcfour_encrypt() requires " +
-                    "python*-crypto or python*-m2crypto or m2crypto")
+    from samba.crypto import arcfour_crypt_blob
+    return arcfour_crypt_blob(data, key)
 
 version = _glue.version
 interface_ips = _glue.interface_ips
-- 
2.9.3



More information about the samba-technical mailing list