pycredentials: Add support for netr_crypt_password
authorGary Lockyer <gary@catalyst.net.nz>
Tue, 20 Jun 2017 20:10:30 +0000 (08:10 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Thu, 22 Jun 2017 06:56:22 +0000 (08:56 +0200)
Add code to encrypt a netr_CryptPassword structure with the current
session key.  This allows the making of Netr_ServerPasswordSet2 calls
from python.

Signed-off-by: Gary Lockyer <gary@catalyst.net.nz>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
auth/credentials/credentials.c
auth/credentials/credentials.h
auth/credentials/pycredentials.c
python/samba/tests/py_credentials.py

index ff444e35413ae08a0b8283b8f86c3de2ccd607ad..2342d7253cc456107a764fbe038a5a3da55d31b4 100644 (file)
@@ -1283,3 +1283,43 @@ _PUBLIC_ bool cli_credentials_parse_password_fd(struct cli_credentials *credenti
 }
 
 
+/**
+ * Encrypt a data blob using the session key and the negotiated encryption
+ * algorithm
+ *
+ * @param state Credential state, contains the session key and algorithm
+ * @param data Data blob containing the data to be encrypted.
+ *
+ */
+_PUBLIC_ NTSTATUS netlogon_creds_session_encrypt(
+       struct netlogon_creds_CredentialState *state,
+       DATA_BLOB data)
+{
+       if (data.data == NULL || data.length == 0) {
+               DBG_ERR("Nothing to encrypt "
+                       "data.data == NULL or data.length == 0");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       /*
+        * Don't crypt an all-zero password it will give away the
+        * NETLOGON pipe session key .
+        */
+       if (all_zero(data.data, data.length)) {
+               DBG_ERR("Supplied data all zeros, could leak session key");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+       if (state->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+               netlogon_creds_aes_encrypt(state,
+                                          data.data,
+                                          data.length);
+       } else if (state->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+               netlogon_creds_arcfour_crypt(state,
+                                            data.data,
+                                            data.length);
+       } else {
+               DBG_ERR("Unsupported encryption option negotiated");
+               return NT_STATUS_NOT_SUPPORTED;
+       }
+       return NT_STATUS_OK;
+}
+
index 50f69940138dd852922c1e1604d0931e4a62ae05..e75694a4b16966b74e1687c8dbfe1390cf5bf209 100644 (file)
@@ -293,4 +293,8 @@ void *_cli_credentials_callback_data(struct cli_credentials *cred);
  */
 struct netlogon_creds_CredentialState *cli_credentials_get_netlogon_creds(struct cli_credentials *cred);
 
+NTSTATUS netlogon_creds_session_encrypt(
+       struct netlogon_creds_CredentialState *state,
+       DATA_BLOB data);
+
 #endif /* __CREDENTIALS_H__ */
index 30698d49dc5e7d0b01f5bb661401cb49762ede7a..caf30bff4925e74e47f8750ca78757598af2efff 100644 (file)
 #include "pycredentials.h"
 #include "param/param.h"
 #include "lib/cmdline/credentials.h"
+#include "auth/credentials/credentials_internal.h"
 #include "librpc/gen_ndr/samr.h" /* for struct samr_Password */
+#include "librpc/gen_ndr/netlogon.h"
 #include "libcli/util/pyerrors.h"
+#include "libcli/auth/libcli_auth.h"
 #include "param/pyparam.h"
 #include <tevent.h>
 #include "libcli/auth/libcli_auth.h"
@@ -633,6 +636,29 @@ static PyObject *py_creds_set_secure_channel_type(PyObject *self, PyObject *args
        Py_RETURN_NONE;
 }
 
+static PyObject *py_creds_encrypt_netr_crypt_password(PyObject *self,
+                                                     PyObject *args)
+{
+       DATA_BLOB data = data_blob_null;
+       struct cli_credentials    *creds  = NULL;
+       struct netr_CryptPassword *pwd    = NULL;
+       NTSTATUS status;
+       PyObject *py_cp = Py_None;
+
+       creds = PyCredentials_AsCliCredentials(self);
+
+       if (!PyArg_ParseTuple(args, "|O", &py_cp)) {
+               return NULL;
+       }
+       pwd = pytalloc_get_type(py_cp, struct netr_CryptPassword);
+       data.length = sizeof(struct netr_CryptPassword);
+       data.data   = (uint8_t *)pwd;
+       status = netlogon_creds_session_encrypt(creds->netlogon_creds, data);
+
+       PyErr_NTSTATUS_IS_ERR_RAISE(status);
+
+       Py_RETURN_NONE;
+}
 
 static PyMethodDef py_creds_methods[] = {
        { "get_username", py_creds_get_username, METH_NOARGS,
@@ -742,6 +768,13 @@ static PyMethodDef py_creds_methods[] = {
                "Get a new client NETLOGON_AUTHENTICATOR"},
        { "set_secure_channel_type", py_creds_set_secure_channel_type,
          METH_VARARGS, NULL },
+       { "encrypt_netr_crypt_password",
+               py_creds_encrypt_netr_crypt_password,
+               METH_VARARGS,
+               "S.encrypt_netr_crypt_password(password) -> NTSTATUS\n"
+               "Encrypt the supplied password using the session key and\n"
+               "the negotiated encryption algorithm in place\n"
+               "i.e. it overwrites the original data"},
        { NULL }
 };
 
index fd9853ae7e1c053940df23812dcb5800be6042e6..b47cf9ebc45c4dda47a72289307056200b465ddc 100644 (file)
@@ -92,10 +92,52 @@ class PyCredentialsTests(TestCase):
         (authenticator, subsequent) = self.get_authenticator(c)
         self.do_NetrLogonGetDomainInfo(c, authenticator, subsequent)
 
+    # Test Credentials.encrypt_netr_crypt_password
+    # By performing a NetrServerPasswordSet2
+    # And the logging on using the new password.
+    def test_encrypt_netr_password(self):
+        # Change the password
+        self.do_Netr_ServerPasswordSet2()
+        # Now use the new password to perform an operation
+        self.do_DsrEnumerateDomainTrusts()
 
 
+   # Change the current machine account pazssword with a
+   # netr_ServerPasswordSet2 call.
+
+    def do_Netr_ServerPasswordSet2(self):
+        c = self.get_netlogon_connection()
+        (authenticator, subsequent) = self.get_authenticator(c)
+        PWD_LEN  = 32
+        DATA_LEN = 512
+        newpass = samba.generate_random_password(PWD_LEN, PWD_LEN)
+        filler  = [ord(x) for x in os.urandom(DATA_LEN-PWD_LEN)]
+        pwd = netlogon.netr_CryptPassword()
+        pwd.length = PWD_LEN
+        pwd.data = filler + [ord(x) for x in newpass]
+        self.machine_creds.encrypt_netr_crypt_password(pwd)
+        c.netr_ServerPasswordSet2(self.server,
+                                  self.machine_creds.get_workstation(),
+                                  SEC_CHAN_WKSTA,
+                                  self.machine_name,
+                                  authenticator,
+                                  pwd)
+
+        self.machine_pass = newpass
+        self.machine_creds.set_password(newpass)
+
+    # Perform a DsrEnumerateDomainTrusts, this provides confirmation that
+    # a netlogon connection has been correctly established
+    def do_DsrEnumerateDomainTrusts(self):
+        c = self.get_netlogon_connection()
+        trusts = c.netr_DsrEnumerateDomainTrusts(
+            self.server,
+            netlogon.NETR_TRUST_FLAG_IN_FOREST |
+            netlogon.NETR_TRUST_FLAG_OUTBOUND  |
+            netlogon.NETR_TRUST_FLAG_INBOUND)
+
+    # Establish sealed schannel netlogon connection over TCP/IP
     #
-    # Establish aealed schannel netlogon connection over TCP/IP
     def get_netlogon_connection(self):
         return netlogon.netlogon("ncacn_ip_tcp:%s[schannel,seal]" % self.server,
                                  self.lp,
@@ -128,6 +170,7 @@ class PyCredentialsTests(TestCase):
         self.machine_creds.set_secure_channel_type(SEC_CHAN_WKSTA)
         self.machine_creds.set_password(self.machine_pass)
         self.machine_creds.set_username(self.machine_name + "$")
+        self.machine_creds.set_workstation(self.machine_name)
 
     #
     # Create a test user account
@@ -154,6 +197,7 @@ class PyCredentialsTests(TestCase):
         self.user_creds.guess(self.get_loadparm())
         self.user_creds.set_password(self.user_pass)
         self.user_creds.set_username(self.user_name)
+        self.user_creds.set_workstation(self.machine_name)
         pass
 
     #