[PATCH] Use the LogonId in NETLOGON_LOGON_IDENTITY_INFO to link winbind authentication and SamLogon

Gary Lockyer gary at catalyst.net.nz
Thu Feb 7 20:16:12 UTC 2019


This change:
  Folds the two 32 bit values logon_id_high and logon_id_low into a
  single 64 bit logon_id in netr_identity_info. Samba currently sets
  this to 0xdeadbeef.

  Populates this with a random value in winbind and passes it in the
  SamLogon calls.

  Adds Authentication logging in winbind for PAM_AUTH and PAM_AUTH_CRAP
  authentication.

  Logs the logonId in JSON Authentication messages.


Merge Request: https://gitlab.com/samba-team/samba/merge_requests/242
CI: https://gitlab.com/samba-team/devel/samba/pipelines/46606500

Review appreciated

Ngā mihi
Gary




Log message for a winbind PAM_AUTH request.
{
  "timestamp": "2019-02-08T08:45:20.677535+1300",
  "type": "Authentication",
  "Authentication": {
    "version": {
      "major": 1,
      "minor": 2
    },
    "eventId": 4624,
    "logonId": "7f0fb6cb17b1b2eb",
    "logonType": 8,
    "status": "NT_STATUS_OK",
    "localAddress": "unix:",
    "remoteAddress": "unix:",
    "serviceDescription": "winbind",
    "authDescription": "PAM_AUTH, ntlm_auth, 14269",
    "clientDomain": "SAMBADOMAIN",
    "clientAccount": "Administrator",
    "workstation": null,
    "becameAccount": "",
    "becameDomain": "",
    "becameSid": null,
    "mappedAccount": null,
    "mappedDomain": null,
    "netlogonComputer": null,
    "netlogonTrustAccount": null,
    "netlogonNegotiateFlags": "0x00000000",
    "netlogonSecureChannelType": 0,
    "netlogonTrustAccountSid": null,
    "passwordType": "Plaintext",
    "duration": 122573
  }
}

Log message for the corresponding SamLogon.
{
  "timestamp": "2019-02-08T08:45:20.560851+1300",
  "type": "Authentication",
  "Authentication": {
    "version": {
      "major": 1,
      "minor": 2
    },
    "eventId": 4624,
    "logonId": "7f0fb6cb17b1b2eb",
    "logonType": 2,
    "status": "NT_STATUS_OK",
    "localAddress": "ipv4:127.0.0.21:1026",
    "remoteAddress": "ipv4:127.0.0.23:24297",
    "serviceDescription": "SamLogon",
    "authDescription": "interactive",
    "clientDomain": "SAMBADOMAIN",
    "clientAccount": "Administrator",
    "workstation": "\\S4MEMBER",
    "becameAccount": "Administrator",
    "becameDomain": "SAMBADOMAIN",
    "becameSid": "S-1-5-21-1560481212-3295640397-1679667293-500",
    "mappedAccount": "Administrator",
    "mappedDomain": "SAMBADOMAIN",
    "netlogonComputer": "S4MEMBER",
    "netlogonTrustAccount": "S4MEMBER$",
    "netlogonNegotiateFlags": "0x610FFFFF",
    "netlogonSecureChannelType": 2,
    "netlogonTrustAccountSid":
       "S-1-5-21-1560481212-3295640397-1679667293-1109",
    "passwordType": "Supplied-NT-Hash",
    "duration": 5528
  }
}
-------------- next part --------------
From c8e69cccd25a7f70b7a81306e7a81105ff3c890a Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 28 Jan 2019 15:27:29 +1300
Subject: [PATCH 01/12] auth_log tests: Allow the remote address to be None

Allow self.remoteAddress to be None, remote address filtering is not
required for the winbind auth logging tests.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 python/samba/tests/auth_log_base.py | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/python/samba/tests/auth_log_base.py b/python/samba/tests/auth_log_base.py
index 38f5eb5175d..c1391080566 100644
--- a/python/samba/tests/auth_log_base.py
+++ b/python/samba/tests/auth_log_base.py
@@ -77,6 +77,9 @@ class AuthLogTestBase(samba.tests.TestCase):
             return False
 
         def isRemote(message):
+            if self.remoteAddress is None:
+                return True
+
             remote = None
             if message["type"] == "Authorization":
                 remote = message["Authorization"]["remoteAddress"]
-- 
2.18.1


From 0307dc616c874986e83b147295da852aec1d21df Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Thu, 31 Jan 2019 10:52:07 +1300
Subject: [PATCH 02/12] wbinfo: fix --ntlmv1 option

Currently using the --ntlmv1 option fails with an unknown option error.
This patch ensures that the option is correctly supported.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 nsswitch/wbinfo.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/nsswitch/wbinfo.c b/nsswitch/wbinfo.c
index b6f0ff8ccbf..6a1dbd9b56b 100644
--- a/nsswitch/wbinfo.c
+++ b/nsswitch/wbinfo.c
@@ -3243,6 +3243,7 @@ int main(int argc, const char **argv, char **envp)
 		/* generic configuration options */
 		case OPT_DOMAIN_NAME:
 		case OPT_VERBOSE:
+		case OPT_NTLMV1:
 		case OPT_NTLMV2:
 		case OPT_LANMAN:
 		case OPT_LOGOFF_USER:
-- 
2.18.1


From 831a8540fa45428d9f2f1709104749a487b20ddd Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Tue, 22 Jan 2019 09:16:05 +1300
Subject: [PATCH 03/12] s3 winbind auth_log: Tests for logon id logging.

Tests to validate that winbind generates a random logon_id and passes it
in the netlogon call.

This will allow the linking of the windbind authentication requests and
the SamLogon request on the DC.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 python/samba/tests/auth_log_winbind.py | 453 +++++++++++++++++++++++++
 selftest/knownfail.d/winbind_auth      |   4 +
 selftest/target/Samba4.pm              |   1 +
 source4/selftest/tests.py              |   4 +
 4 files changed, 462 insertions(+)
 create mode 100644 python/samba/tests/auth_log_winbind.py
 create mode 100644 selftest/knownfail.d/winbind_auth

diff --git a/python/samba/tests/auth_log_winbind.py b/python/samba/tests/auth_log_winbind.py
new file mode 100644
index 00000000000..bf3b6fd880a
--- /dev/null
+++ b/python/samba/tests/auth_log_winbind.py
@@ -0,0 +1,453 @@
+# Unix SMB/CIFS implementation.
+# Copyright (C) Andrew Bartlett <abartlet at samba.org> 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/>.
+#
+
+"""
+    auth logging tests that exercise winbind
+"""
+
+import json
+import os
+from random import SystemRandom
+import string
+import time
+
+from samba.auth import system_session
+from samba.credentials import Credentials
+from samba.compat import get_string, get_bytes
+from samba.dcerpc.messaging import AUTH_EVENT_NAME, MSG_AUTH_LOG
+from samba.dsdb import UF_NORMAL_ACCOUNT
+from samba.messaging import Messaging
+from samba.param import LoadParm
+from samba.samdb import SamDB
+from samba.tests import delete_force, BlackboxProcessError, BlackboxTestCase
+from samba.tests.auth_log_base import AuthLogTestBase
+
+USER_NAME = "WBALU"
+
+
+class AuthLogTestsWinbind(AuthLogTestBase, BlackboxTestCase):
+
+    # Generate a random password that can be safely  passed on the command line
+    # i.e. it does not contain any shell meta characters.
+    def random_password(self, count=32):
+        password = SystemRandom().choice(string.ascii_uppercase)
+        password += SystemRandom().choice(string.digits)
+        password += SystemRandom().choice(string.ascii_lowercase)
+        password += ''.join(SystemRandom().choice(string.ascii_uppercase +
+                    string.ascii_lowercase +
+                    string.digits) for x in range(count - 3))
+        return password
+
+    #
+    # Helper function to watch for authentication messages on the
+    # Domain Controller.
+    #
+    def dc_watcher(self):
+
+        (r1, w1) = os.pipe()
+        pid = os.fork()
+        if pid != 0:
+            # Parent process return the result socket to the caller.
+            return r1
+
+        # Load the lp context for the Domain Controller, rather than the
+        # member server.
+        prefix = os.environ["PREFIX"]
+        dc_env = os.environ["DC_ENV"]
+        config_file = "{0}/{1}/etc/smb.conf".format(prefix, dc_env)
+        lp_ctx = LoadParm()
+        lp_ctx.load(config_file)
+
+        #
+        # Is the message a SamLogon authentication?
+        def is_sam_logon(m):
+            if m is None:
+                return False
+            msg = json.loads(m)
+            return (
+                msg["type"] == "Authentication" and
+                msg["Authentication"]["serviceDescription"] == "SamLogon" and
+                msg["Authentication"]["authDescription"] == "interactive")
+
+        #
+        # Handler function for received authentication messages.
+        def message_handler(context, msgType, src, message):
+            # Print the message to help debugging the tests.
+            # as it's a JSON message it does not look like a sub-unit message.
+            print(message)
+            self.dc_msg = message
+
+        # Set up a messaging context to listen for authentication events on
+        # the domain controller.
+        msg_ctx = Messaging((1,), lp_ctx=lp_ctx)
+        msg_ctx.irpc_add_name(AUTH_EVENT_NAME)
+        msg_handler_and_context = (message_handler, None)
+        msg_ctx.register(msg_handler_and_context, msg_type=MSG_AUTH_LOG)
+
+        # Wait for the SamLogon message.
+        # As the SamLogon is the only message we're interested ignore any other
+        # messages.
+        self.dc_msg = None
+        start_time = time.time()
+        while not is_sam_logon(self.dc_msg) and (time.time() - start_time < 1):
+            msg_ctx.loop_once(0.1)
+
+        if self.dc_msg is None:
+            os.write(w1, get_bytes("None"))
+        else:
+            os.write(w1, get_bytes(self.dc_msg))
+
+        msg_ctx.deregister(msg_handler_and_context, msg_type=MSG_AUTH_LOG)
+        msg_ctx.irpc_remove_name(AUTH_EVENT_NAME)
+
+        os._exit(0)
+
+    # Remove any DCE/RPC ncacn_np messages
+    # these only get triggered once per session, and stripping them out
+    # avoids ordering dependencies in the tests
+    #
+    def filter_messages(self, messages):
+        def keep(msg):
+            if (msg["type"] == "Authorization" and
+                msg["Authorization"]["serviceDescription"] == "DCE/RPC" and
+                msg["Authorization"]["authType"] == "ncacn_np"):
+                    return False
+            else:
+                return True
+
+        return list(filter(keep, messages))
+
+    def setUp(self):
+        super(AuthLogTestsWinbind, self).setUp()
+        self.domain = os.environ["DOMAIN"]
+        self.host = os.environ["SERVER"]
+        self.lp = self.get_loadparm()
+        self.credentials = self.get_credentials()
+        self.session = system_session()
+
+        self.ldb = SamDB(
+            session_info=self.session,
+            credentials=self.credentials,
+            lp=self.lp)
+        self.create_user_account()
+
+    def tearDown(self):
+        super(AuthLogTestsWinbind, self).tearDown()
+        delete_force(self.ldb, self.user_dn)
+
+    #
+    # Create a test user account
+    def create_user_account(self):
+        self.user_pass = self.random_password()
+        self.user_name = USER_NAME
+        self.user_dn = "cn=%s,%s" % (self.user_name, self.ldb.domain_dn())
+
+        # remove the account if it exists, this will happen if a previous test
+        # run failed
+        delete_force(self.ldb, self.user_dn)
+
+        utf16pw = ('"%s"' % get_string(self.user_pass)).encode('utf-16-le')
+        self.ldb.add({
+           "dn": self.user_dn,
+           "objectclass": "user",
+           "sAMAccountName": "%s" % self.user_name,
+           "userAccountControl": str(UF_NORMAL_ACCOUNT),
+           "unicodePwd": utf16pw})
+
+        self.user_creds = Credentials()
+        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.server)
+
+    def test_ntlm_auth(self):
+
+        def isLastExpectedMessage(msg):
+            DESC = "PAM_AUTH, ntlm_auth"
+            return (
+                msg["type"] == "Authentication" and
+                msg["Authentication"]["serviceDescription"] == "winbind" and
+                msg["Authentication"]["authDescription"] is not None and
+                msg["Authentication"]["authDescription"].startswith(DESC))
+
+        pipe = self.dc_watcher()
+        COMMAND = "bin/ntlm_auth"
+        self.check_run("{0} --username={1} --password={2}".format(
+            COMMAND,
+            self.credentials.get_username(),
+            self.credentials.get_password()),
+            msg="ntlm_auth failed")
+
+        messages = self.waitForMessages(isLastExpectedMessage)
+        messages = self.filter_messages(messages)
+        expected_messages = 1
+        self.assertEquals(expected_messages,
+                          len(messages),
+                          "Did not receive the expected number of messages")
+
+        # Check the first message it should be an Authentication
+        msg = messages[0]
+        self.assertEquals("Authentication", msg["type"])
+        self.assertTrue(
+            msg["Authentication"]["authDescription"].startswith(
+                "PAM_AUTH, ntlm_auth,"))
+        self.assertEquals("winbind",
+                          msg["Authentication"]["serviceDescription"])
+        self.assertEquals("Plaintext", msg["Authentication"]["passwordType"])
+        # Logon type should be NetworkCleartext
+        self.assertEquals(8, msg["Authentication"]["logonType"])
+        # Event code should be Successful logon
+        self.assertEquals(4624, msg["Authentication"]["eventId"])
+        self.assertEquals("unix:", msg["Authentication"]["remoteAddress"])
+        self.assertEquals("unix:", msg["Authentication"]["localAddress"])
+        self.assertEquals(self.domain, msg["Authentication"]["clientDomain"])
+        self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
+        self.assertEquals(self.credentials.get_username(),
+                          msg["Authentication"]["clientAccount"])
+        self.assertEquals(self.credentials.get_domain(),
+                          msg["Authentication"]["clientDomain"])
+        self.assertTrue(msg["Authentication"]["workstation"] is None)
+
+        logon_id = msg["Authentication"]["logonId"]
+
+        message = os.read(pipe, 4096)
+        msg = json.loads(get_string(message))
+        self.assertEquals("Authentication", msg["type"])
+        self.assertEquals(logon_id, msg["Authentication"]["logonId"])
+        self.assertEquals("SamLogon",
+                          msg["Authentication"]["serviceDescription"])
+        self.assertEquals("interactive",
+                          msg["Authentication"]["authDescription"])
+
+    def test_wbinfo(self):
+        def isLastExpectedMessage(msg):
+            DESC = "NTLM_AUTH, wbinfo"
+            return (
+                msg["type"] == "Authentication" and
+                msg["Authentication"]["serviceDescription"] == "winbind" and
+                msg["Authentication"]["authDescription"] is not None and
+                msg["Authentication"]["authDescription"].startswith(DESC))
+
+        pipe = self.dc_watcher()
+        COMMAND = "bin/wbinfo"
+        try:
+            self.check_run("{0} -a {1}%{2}".format(
+                COMMAND,
+                self.credentials.get_username(),
+                self.credentials.get_password()),
+                msg="ntlm_auth failed")
+        except BlackboxProcessError:
+            pass
+
+        messages = self.waitForMessages(isLastExpectedMessage)
+        messages = self.filter_messages(messages)
+        expected_messages = 3
+        self.assertEquals(expected_messages,
+                          len(messages),
+                          "Did not receive the expected number of messages")
+
+        # The 1st message should be an Authentication against the local
+        # password database
+        msg = messages[0]
+        self.assertEquals("Authentication", msg["type"])
+        self.assertTrue(msg["Authentication"]["authDescription"].startswith(
+            "PASSDB, wbinfo,"))
+        self.assertEquals("winbind",
+                          msg["Authentication"]["serviceDescription"])
+        # Logon type should be Interactive
+        self.assertEquals(2, msg["Authentication"]["logonType"])
+        # Event code should be Unsuccessful logon
+        self.assertEquals(4625, msg["Authentication"]["eventId"])
+        self.assertEquals("unix:", msg["Authentication"]["remoteAddress"])
+        self.assertEquals("unix:", msg["Authentication"]["localAddress"])
+        self.assertEquals('', msg["Authentication"]["clientDomain"])
+        # This is what the existing winbind implementation returns.
+        self.assertEquals("NT_STATUS_NO_SUCH_USER",
+                          msg["Authentication"]["status"])
+        self.assertEquals("NTLMv2", msg["Authentication"]["passwordType"])
+        self.assertEquals(self.credentials.get_username(),
+                          msg["Authentication"]["clientAccount"])
+        self.assertEquals("", msg["Authentication"]["clientDomain"])
+
+        logon_id = msg["Authentication"]["logonId"]
+
+        # The 2nd message should be a PAM_AUTH with the same logon id as the
+        # 1st message
+        msg = messages[1]
+        self.assertEquals("Authentication", msg["type"])
+        self.assertTrue(msg["Authentication"]["authDescription"].startswith(
+            "PAM_AUTH"))
+        self.assertEquals("winbind",
+                          msg["Authentication"]["serviceDescription"])
+        self.assertEquals(logon_id, msg["Authentication"]["logonId"])
+        # Logon type should be NetworkCleartext
+        self.assertEquals(8, msg["Authentication"]["logonType"])
+        # Event code should be Unsuccessful logon
+        self.assertEquals(4625, msg["Authentication"]["eventId"])
+        self.assertEquals("unix:", msg["Authentication"]["remoteAddress"])
+        self.assertEquals("unix:", msg["Authentication"]["localAddress"])
+        self.assertEquals('', msg["Authentication"]["clientDomain"])
+        # This is what the existing winbind implementation returns.
+        self.assertEquals("NT_STATUS_INVALID_HANDLE",
+                          msg["Authentication"]["status"])
+        self.assertEquals(self.credentials.get_username(),
+                          msg["Authentication"]["clientAccount"])
+        self.assertEquals("", msg["Authentication"]["clientDomain"])
+
+        # The 3rd message should be an NTLM_AUTH
+        msg = messages[2]
+        self.assertEquals("Authentication", msg["type"])
+        self.assertTrue(msg["Authentication"]["authDescription"].startswith(
+            "NTLM_AUTH, wbinfo,"))
+        self.assertEquals("winbind",
+                          msg["Authentication"]["serviceDescription"])
+        # Logon type should be Network
+        self.assertEquals(3, msg["Authentication"]["logonType"])
+        self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
+        # Event code should be successful logon
+        self.assertEquals(4624, msg["Authentication"]["eventId"])
+        self.assertEquals("NTLMv2", msg["Authentication"]["passwordType"])
+        self.assertEquals("unix:", msg["Authentication"]["remoteAddress"])
+        self.assertEquals("unix:", msg["Authentication"]["localAddress"])
+        self.assertEquals(self.credentials.get_username(),
+                          msg["Authentication"]["clientAccount"])
+        self.assertEquals(self.credentials.get_domain(),
+                          msg["Authentication"]["clientDomain"])
+
+        logon_id = msg["Authentication"]["logonId"]
+
+        #
+        # Now check the Domain server authentication message
+        #
+        message = os.read(pipe, 4096)
+        msg = json.loads(get_string(message))
+        self.assertEquals("Authentication", msg["type"])
+        self.assertEquals(logon_id, msg["Authentication"]["logonId"])
+        self.assertEquals("SamLogon",
+                          msg["Authentication"]["serviceDescription"])
+        self.assertEquals("network",
+                          msg["Authentication"]["authDescription"])
+
+    def test_wbinfo_ntlmv1(self):
+        def isLastExpectedMessage(msg):
+            DESC = "NTLM_AUTH, wbinfo"
+            return (
+                msg["type"] == "Authentication" and
+                msg["Authentication"]["serviceDescription"] == "winbind" and
+                msg["Authentication"]["authDescription"] is not None and
+                msg["Authentication"]["authDescription"].startswith(DESC))
+
+        pipe = self.dc_watcher()
+        COMMAND = "bin/wbinfo"
+        try:
+            self.check_run("{0} --ntlmv1 -a {1}%{2}".format(
+                COMMAND,
+                self.credentials.get_username(),
+                self.credentials.get_password()),
+                msg="ntlm_auth failed")
+        except BlackboxProcessError:
+            pass
+
+        messages = self.waitForMessages(isLastExpectedMessage)
+        messages = self.filter_messages(messages)
+        expected_messages = 3
+        self.assertEquals(expected_messages,
+                          len(messages),
+                          "Did not receive the expected number of messages")
+
+        # The 1st message should be an Authentication against the local
+        # password database
+        msg = messages[0]
+        self.assertEquals("Authentication", msg["type"])
+        self.assertTrue(msg["Authentication"]["authDescription"].startswith(
+            "PASSDB, wbinfo,"))
+        self.assertEquals("winbind",
+                          msg["Authentication"]["serviceDescription"])
+        # Logon type should be Interactive
+        self.assertEquals(2, msg["Authentication"]["logonType"])
+        # Event code should be Unsuccessful logon
+        self.assertEquals(4625, msg["Authentication"]["eventId"])
+        self.assertEquals("unix:", msg["Authentication"]["remoteAddress"])
+        self.assertEquals("unix:", msg["Authentication"]["localAddress"])
+        self.assertEquals('', msg["Authentication"]["clientDomain"])
+        # This is what the existing winbind implementation returns.
+        self.assertEquals("NT_STATUS_NO_SUCH_USER",
+                          msg["Authentication"]["status"])
+        self.assertEquals("NTLMv2", msg["Authentication"]["passwordType"])
+        self.assertEquals(self.credentials.get_username(),
+                          msg["Authentication"]["clientAccount"])
+        self.assertEquals("", msg["Authentication"]["clientDomain"])
+
+        logon_id = msg["Authentication"]["logonId"]
+
+        # The 2nd message should be a PAM_AUTH with the same logon id as the
+        # 1st message
+        msg = messages[1]
+        self.assertEquals("Authentication", msg["type"])
+        self.assertTrue(msg["Authentication"]["authDescription"].startswith(
+            "PAM_AUTH"))
+        self.assertEquals("winbind",
+                          msg["Authentication"]["serviceDescription"])
+        self.assertEquals(logon_id, msg["Authentication"]["logonId"])
+        self.assertEquals("Plaintext", msg["Authentication"]["passwordType"])
+        # Logon type should be NetworkCleartext
+        self.assertEquals(8, msg["Authentication"]["logonType"])
+        # Event code should be Unsuccessful logon
+        self.assertEquals(4625, msg["Authentication"]["eventId"])
+        self.assertEquals("unix:", msg["Authentication"]["remoteAddress"])
+        self.assertEquals("unix:", msg["Authentication"]["localAddress"])
+        self.assertEquals('', msg["Authentication"]["clientDomain"])
+        # This is what the existing winbind implementation returns.
+        self.assertEquals("NT_STATUS_INVALID_HANDLE",
+                          msg["Authentication"]["status"])
+        self.assertEquals(self.credentials.get_username(),
+                          msg["Authentication"]["clientAccount"])
+        self.assertEquals("", msg["Authentication"]["clientDomain"])
+
+        # The 3rd message should be an NTLM_AUTH
+        msg = messages[2]
+        self.assertEquals("Authentication", msg["type"])
+        self.assertTrue(msg["Authentication"]["authDescription"].startswith(
+            "NTLM_AUTH, wbinfo,"))
+        self.assertEquals("winbind",
+                          msg["Authentication"]["serviceDescription"])
+        self.assertEquals("NTLMv1",
+                          msg["Authentication"]["passwordType"])
+        # Logon type should be Network
+        self.assertEquals(3, msg["Authentication"]["logonType"])
+        self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
+        # Event code should be successful logon
+        self.assertEquals(4624, msg["Authentication"]["eventId"])
+        self.assertEquals("unix:", msg["Authentication"]["remoteAddress"])
+        self.assertEquals("unix:", msg["Authentication"]["localAddress"])
+        self.assertEquals(self.credentials.get_username(),
+                          msg["Authentication"]["clientAccount"])
+        self.assertEquals(self.credentials.get_domain(),
+                          msg["Authentication"]["clientDomain"])
+
+        logon_id = msg["Authentication"]["logonId"]
+        #
+        # Now check the Domain server authentication message
+        #
+        message = os.read(pipe, 4096)
+        msg = json.loads(get_string(message))
+        self.assertEquals("Authentication", msg["type"])
+        self.assertEquals(logon_id, msg["Authentication"]["logonId"])
+        self.assertEquals("SamLogon",
+                          msg["Authentication"]["serviceDescription"])
+        self.assertEquals("network",
+                          msg["Authentication"]["authDescription"])
diff --git a/selftest/knownfail.d/winbind_auth b/selftest/knownfail.d/winbind_auth
new file mode 100644
index 00000000000..e0e0dd08906
--- /dev/null
+++ b/selftest/knownfail.d/winbind_auth
@@ -0,0 +1,4 @@
+^samba.tests.auth_log_winbind.samba.tests.auth_log_winbind.AuthLogTestsWinbind.test_ntlm_auth
+^samba.tests.auth_log_winbind.samba.tests.auth_log_winbind.AuthLogTestsWinbind.test_wbinfo
+^samba.tests.auth_log_winbind.samba.tests.auth_log_winbind.AuthLogTestsWinbind.test_wbinfo_ntlmv1
+
diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 33c66848c56..9a703129921 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -1241,6 +1241,7 @@ sub provision_s4member($$$$$)
 	print "PROVISIONING MEMBER...\n";
 	my $extra_smb_conf = "
         passdb backend = samba_dsdb
+	auth event notification = true
 winbindd:use external pipes = true
 
 # the source4 smb server doesn't allow signing by default
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index 8689cbba3a7..12f33f4bb2d 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -748,6 +748,10 @@ if have_heimdal_support:
                            extra_args=['-U"$USERNAME%$PASSWORD"'],
                            environ={'CLIENT_IP': '127.0.0.11',
                                     'SOCKET_WRAPPER_DEFAULT_IFACE': 11})
+    planoldpythontestsuite("s4member:local",
+                           "samba.tests.auth_log_winbind",
+                           extra_args=['-U"$DC_USERNAME%$DC_PASSWORD"'],
+                           environ={'DC_ENV': 'ad_dc_ntvfs'})
     planoldpythontestsuite("ad_dc:local", "samba.tests.audit_log_pass_change",
                            extra_args=['-U"$USERNAME%$PASSWORD"'],
                            environ={'CLIENT_IP': '127.0.0.11',
-- 
2.18.1


From d29db87abfca752a279c66189779bf6209f81f9f Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 28 Jan 2019 15:30:23 +1300
Subject: [PATCH 04/12] s3 auth: Create messaging and lp contexts.

If 'auth event notifications' are enabled create an imessaging_context
and a loadparm_context that can be passed to log_authentication_event.

This will allow the generated authentication messages to be tested.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 source3/auth/auth.c        | 18 +++++++++++++++---
 source3/auth/wscript_build |  2 +-
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/source3/auth/auth.c b/source3/auth/auth.c
index 0a96d591808..7215218413f 100644
--- a/source3/auth/auth.c
+++ b/source3/auth/auth.c
@@ -21,6 +21,9 @@
 #include "auth.h"
 #include "../lib/tsocket/tsocket.h"
 
+#include "param/param.h"
+#include "../lib/messaging/messaging.h"
+
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_AUTH
 
@@ -176,6 +179,8 @@ NTSTATUS auth_check_ntlm_password(TALLOC_CTX *mem_ctx,
 	auth_methods *auth_method;
 	struct auth_serversupplied_info *server_info = NULL;
 	struct dom_sid sid = {0};
+	struct imessaging_context *msg_ctx = NULL;
+	struct loadparm_context *lp_ctx = NULL;
 
 	if (user_info == NULL || auth_context == NULL || pserver_info == NULL) {
 		return NT_STATUS_LOGON_FAILURE;
@@ -183,6 +188,12 @@ NTSTATUS auth_check_ntlm_password(TALLOC_CTX *mem_ctx,
 
 	frame = talloc_stackframe();
 
+	if (lp_auth_event_notification()) {
+		lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+		msg_ctx = imessaging_client_init(
+		    frame, lp_ctx, global_event_context());
+	}
+
 	*pauthoritative = 1;
 
 	DEBUG(3, ("check_ntlm_password:  Checking password for unmapped user [%s]\\[%s]@[%s] with the new password interface\n", 
@@ -299,7 +310,8 @@ NTSTATUS auth_check_ntlm_password(TALLOC_CTX *mem_ctx,
 		sid = (struct dom_sid) {0};
 	}
 
-	log_authentication_event(NULL, NULL,
+	log_authentication_event(msg_ctx,
+				 lp_ctx,
 				 &auth_context->start_time,
 				 user_info,
 				 nt_status,
@@ -333,8 +345,8 @@ fail:
 		  user_info->client.account_name, user_info->mapped.account_name,
 		  nt_errstr(nt_status), *pauthoritative));
 
-	log_authentication_event(NULL,
-				 NULL,
+	log_authentication_event(msg_ctx,
+				 lp_ctx,
 				 &auth_context->start_time,
 				 user_info,
 				 nt_status,
diff --git a/source3/auth/wscript_build b/source3/auth/wscript_build
index d27c231caa7..c1062199aa8 100644
--- a/source3/auth/wscript_build
+++ b/source3/auth/wscript_build
@@ -21,7 +21,7 @@ bld.SAMBA3_LIBRARY('auth',
                    user_krb5.c
                    auth_ntlmssp.c
                    auth_generic.c''',
-                   deps='''PLAINTEXT_AUTH SLCACHE DCUTIL TOKEN_UTIL AUTH_COMMON libcli_netlogon3 samba-hostconfig''',
+                   deps='''PLAINTEXT_AUTH SLCACHE DCUTIL TOKEN_UTIL AUTH_COMMON libcli_netlogon3 samba-hostconfig MESSAGING''',
                    private_library=True)
 
 bld.SAMBA3_MODULE('auth_sam',
-- 
2.18.1


From a82bc4d9f74d7eed61a482cca29f9d1082b9c9c9 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Fri, 1 Feb 2019 09:33:53 +1300
Subject: [PATCH 05/12] lib util: Add function to generate random uint64_t

Generate a random uint64_t , which will be used for the netlogon
logon_id.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 lib/util/genrand_util.c | 11 +++++++++++
 lib/util/samba_util.h   |  5 +++++
 2 files changed, 16 insertions(+)

diff --git a/lib/util/genrand_util.c b/lib/util/genrand_util.c
index 76b7cd91890..d7b74c6cf1a 100644
--- a/lib/util/genrand_util.c
+++ b/lib/util/genrand_util.c
@@ -37,6 +37,17 @@ _PUBLIC_ uint32_t generate_random(void)
 	return IVAL(v, 0);
 }
 
+/**
+  @brief generate a random uint64
+**/
+_PUBLIC_ uint64_t generate_random_u64(void)
+{
+	uint8_t v[8];
+	generate_random_buffer(v, 8);
+	return BVAL(v, 0);
+}
+
+
 
 /**
   Microsoft composed the following rules (among others) for quality
diff --git a/lib/util/samba_util.h b/lib/util/samba_util.h
index 7b96a595d43..43da4200032 100644
--- a/lib/util/samba_util.h
+++ b/lib/util/samba_util.h
@@ -91,6 +91,11 @@ _PUBLIC_ int sys_getnameinfo(const struct sockaddr *psa,
 **/
 _PUBLIC_ uint32_t generate_random(void);
 
+/**
+  generate a single random uint64_t
+**/
+_PUBLIC_ uint64_t generate_random_u64(void);
+
 /**
   very basic password quality checker
 **/
-- 
2.18.1


From d08b9ec2ae8aa392af45075aa6d495ab63d3585e Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Thu, 20 Dec 2018 15:02:30 +1300
Subject: [PATCH 06/12] librpc idl: netlogon netr_identity_info logon_id to 64
 bit
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fold the two 32 bit values logon_id_high and logon_id_low into a single
64 bit logon_id in netr_identity_info.  This will be used to tie
together winbind and SamLogon requests in audit logging.

Summary of the of the Query and Response from Microsoft on it's usage.

[REG:119013019612095] [MS-NRPC]: NETLOGON_LOGON_IDENTITY_INFO: Does
the Reserved field have LogonId meaning?

In NetrLogonSamLogonEx:

Does the Reserved field (of NETLOGON_LOGON_IDENTITY_INFO) have LogonId
meaning?

What is a valid LogonID, and does have any audit usage?

Samba is sending a constant "deadbeef" in hex and would like to
understand any usage of this field.

The NRPC spec is accurate in defining the field as Reserved, and without
protocol significance. In the header file in our source code, it is
defined as LogonId and commented as such, but it’s effectively not used.
This is probably why the API structure has that field name. It may have
been intended as such but it’s not used.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 librpc/idl/netlogon.idl           |  3 +--
 source3/rpc_client/cli_netlogon.c | 12 ++++--------
 source4/auth/ntlm/auth_winbind.c  |  3 +--
 source4/kdc/hdb-samba4.c          |  3 +--
 source4/torture/ndr/netlogon.c    |  3 +--
 source4/torture/rpc/netlogon.c    |  3 +--
 source4/torture/rpc/remote_pac.c  |  6 ++----
 source4/torture/rpc/samba3rpc.c   |  3 +--
 source4/torture/rpc/samlogon.c    |  6 ++----
 source4/torture/rpc/samr.c        |  3 +--
 source4/torture/rpc/samsync.c     |  3 +--
 source4/torture/rpc/schannel.c    |  6 ++----
 12 files changed, 18 insertions(+), 36 deletions(-)

diff --git a/librpc/idl/netlogon.idl b/librpc/idl/netlogon.idl
index 22f86b92076..3f100679381 100644
--- a/librpc/idl/netlogon.idl
+++ b/librpc/idl/netlogon.idl
@@ -136,8 +136,7 @@ interface netlogon
 	typedef struct {
 		lsa_String  domain_name;
 		netr_LogonParameterControl parameter_control; /* see MSV1_0_* */
-		uint32      logon_id_low;
-		uint32      logon_id_high;
+		udlong logon_id;
 		lsa_String  account_name;
 		lsa_String  workstation;
 	} netr_IdentityInfo;
diff --git a/source3/rpc_client/cli_netlogon.c b/source3/rpc_client/cli_netlogon.c
index 2aa0f5e7b1c..c7cc183dd16 100644
--- a/source3/rpc_client/cli_netlogon.c
+++ b/source3/rpc_client/cli_netlogon.c
@@ -508,8 +508,7 @@ NTSTATUS rpccli_netlogon_password_logon(
 
 		password_info->identity_info.domain_name.string		= domain;
 		password_info->identity_info.parameter_control		= logon_parameters;
-		password_info->identity_info.logon_id_low		= 0xdead;
-		password_info->identity_info.logon_id_high		= 0xbeef;
+		password_info->identity_info.logon_id			= 0xbeef0000dead;
 		password_info->identity_info.account_name.string	= username;
 		password_info->identity_info.workstation.string		= workstation_slash;
 
@@ -551,8 +550,7 @@ NTSTATUS rpccli_netlogon_password_logon(
 
 		network_info->identity_info.domain_name.string		= domain;
 		network_info->identity_info.parameter_control		= logon_parameters;
-		network_info->identity_info.logon_id_low		= 0xdead;
-		network_info->identity_info.logon_id_high		= 0xbeef;
+		network_info->identity_info.logon_id			= 0xbeef0000dead;
 		network_info->identity_info.account_name.string		= username;
 		network_info->identity_info.workstation.string		= workstation_slash;
 
@@ -670,8 +668,7 @@ NTSTATUS rpccli_netlogon_network_logon(
 
 	network_info->identity_info.domain_name.string		= domain;
 	network_info->identity_info.parameter_control		= logon_parameters;
-	network_info->identity_info.logon_id_low		= 0xdead;
-	network_info->identity_info.logon_id_high		= 0xbeef;
+	network_info->identity_info.logon_id			= 0xbeef0000dead;
 	network_info->identity_info.account_name.string		= username;
 	network_info->identity_info.workstation.string		= workstation_name_slash;
 
@@ -771,8 +768,7 @@ NTSTATUS rpccli_netlogon_interactive_logon(
 
 	password_info->identity_info.domain_name.string		= domain;
 	password_info->identity_info.parameter_control		= logon_parameters;
-	password_info->identity_info.logon_id_low		= 0xdead;
-	password_info->identity_info.logon_id_high		= 0xbeef;
+	password_info->identity_info.logon_id			= 0xbeef0000dead;
 	password_info->identity_info.account_name.string	= username;
 	password_info->identity_info.workstation.string		= workstation_name_slash;
 
diff --git a/source4/auth/ntlm/auth_winbind.c b/source4/auth/ntlm/auth_winbind.c
index 318675fa59b..d7c26e7a1a7 100644
--- a/source4/auth/ntlm/auth_winbind.c
+++ b/source4/auth/ntlm/auth_winbind.c
@@ -174,8 +174,7 @@ static struct tevent_req *winbind_check_password_send(TALLOC_CTX *mem_ctx,
 
 	identity_info->domain_name.string	= user_info->client.domain_name;
 	identity_info->parameter_control	= user_info->logon_parameters; /* see MSV1_0_* */
-	identity_info->logon_id_low		= 0;
-	identity_info->logon_id_high		= 0;
+	identity_info->logon_id			= 0;
 	identity_info->account_name.string	= user_info->client.account_name;
 	identity_info->workstation.string	= user_info->workstation_name;
 
diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c
index cff472574d4..638179e8806 100644
--- a/source4/kdc/hdb-samba4.c
+++ b/source4/kdc/hdb-samba4.c
@@ -361,8 +361,7 @@ static void send_bad_password_netlogon(TALLOC_CTX *mem_ctx,
 
 	identity_info->domain_name.string = user_info->mapped.domain_name;
 	identity_info->parameter_control = user_info->logon_parameters; /* TODO */
-	identity_info->logon_id_low = 0;
-	identity_info->logon_id_high = 0;
+	identity_info->logon_id = 0;
 	identity_info->account_name.string = user_info->mapped.account_name;
 	identity_info->workstation.string
 		= talloc_asprintf(identity_info, "krb5-bad-pw on RODC from %s",
diff --git a/source4/torture/ndr/netlogon.c b/source4/torture/ndr/netlogon.c
index 2e20ff748b9..05a68eb79d2 100644
--- a/source4/torture/ndr/netlogon.c
+++ b/source4/torture/ndr/netlogon.c
@@ -154,8 +154,7 @@ static bool netrlogonsamlogon_w2k_in_check(struct torture_context *tctx,
 	torture_assert_int_equal(tctx, r->in.logon->password->identity_info.domain_name.size, 12, "domain_name.size");
 	torture_assert_str_equal(tctx, r->in.logon->password->identity_info.domain_name.string, "W2KDOM", "domain_name.string");
 	torture_assert_int_equal(tctx, r->in.logon->password->identity_info.parameter_control, 0, "parameter_control");
-	torture_assert_int_equal(tctx, r->in.logon->password->identity_info.logon_id_low, 0xdead, "logon_id_low");
-	torture_assert_int_equal(tctx, r->in.logon->password->identity_info.logon_id_high, 0xbeef, "logon_id_high");
+	torture_assert_u64_equal(tctx, r->in.logon->password->identity_info.logon_id, 0xbeef0000dead, "logon_id");
 	torture_assert_int_equal(tctx, r->in.logon->password->identity_info.account_name.length, 26, "account_name.length");
 	torture_assert_int_equal(tctx, r->in.logon->password->identity_info.account_name.size, 26, "account_name.size");
 	torture_assert_str_equal(tctx, r->in.logon->password->identity_info.account_name.string, "administrator", "account_name.string");
diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c
index 026d86d50e4..52c443e39f3 100644
--- a/source4/torture/rpc/netlogon.c
+++ b/source4/torture/rpc/netlogon.c
@@ -988,8 +988,7 @@ static bool test_netlogon_ops_args(struct dcerpc_pipe *p, struct torture_context
 	ninfo.nt.length = nt_resp.length;
 
 	ninfo.identity_info.parameter_control = 0;
-	ninfo.identity_info.logon_id_low = 0;
-	ninfo.identity_info.logon_id_high = 0;
+	ninfo.identity_info.logon_id = 0;
 	ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials);
 
 	logon.network = &ninfo;
diff --git a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c
index ab10013356b..d915427f1b4 100644
--- a/source4/torture/rpc/remote_pac.c
+++ b/source4/torture/rpc/remote_pac.c
@@ -362,8 +362,7 @@ static bool test_PACVerify(struct torture_context *tctx,
 	/* Validate it over the netlogon pipe */
 
 	generic.identity_info.parameter_control = 0;
-	generic.identity_info.logon_id_high = 0;
-	generic.identity_info.logon_id_low = 0;
+	generic.identity_info.logon_id = 0;
 	generic.identity_info.domain_name.string = session_info->info->domain_name;
 	generic.identity_info.account_name.string = session_info->info->account_name;
 	generic.identity_info.workstation.string = test_machine_name;
@@ -836,8 +835,7 @@ static bool test_S2U4Self(struct torture_context *tctx,
 	ninfo.nt.length = nt_resp.length;
 
 	ninfo.identity_info.parameter_control = 0;
-	ninfo.identity_info.logon_id_low = 0;
-	ninfo.identity_info.logon_id_high = 0;
+	ninfo.identity_info.logon_id = 0;
 	ninfo.identity_info.workstation.string = cli_credentials_get_workstation(server_creds);
 
 	logon.network = &ninfo;
diff --git a/source4/torture/rpc/samba3rpc.c b/source4/torture/rpc/samba3rpc.c
index 9cd479c9baf..9fee3ef68cd 100644
--- a/source4/torture/rpc/samba3rpc.c
+++ b/source4/torture/rpc/samba3rpc.c
@@ -1212,8 +1212,7 @@ static bool schan(struct torture_context *tctx,
 		ninfo.identity_info.domain_name.string =
 			cli_credentials_get_domain(user_creds);
 		ninfo.identity_info.parameter_control = 0;
-		ninfo.identity_info.logon_id_low = 0;
-		ninfo.identity_info.logon_id_high = 0;
+		ninfo.identity_info.logon_id = 0;
 		ninfo.identity_info.workstation.string =
 			cli_credentials_get_workstation(user_creds);
 		memcpy(ninfo.challenge, chal.data, sizeof(ninfo.challenge));
diff --git a/source4/torture/rpc/samlogon.c b/source4/torture/rpc/samlogon.c
index d5ef47ec1ee..7053fc11fa5 100644
--- a/source4/torture/rpc/samlogon.c
+++ b/source4/torture/rpc/samlogon.c
@@ -96,8 +96,7 @@ static NTSTATUS check_samlogon(struct samlogon_state *samlogon_state,
 
 	ninfo.identity_info.domain_name.string = samlogon_state->account_domain;
 	ninfo.identity_info.parameter_control = parameter_control;
-	ninfo.identity_info.logon_id_low = 0;
-	ninfo.identity_info.logon_id_high = 0;
+	ninfo.identity_info.logon_id = 0;
 	ninfo.identity_info.account_name.string = samlogon_state->account_name;
 	ninfo.identity_info.workstation.string = TEST_MACHINE_NAME;
 
@@ -1526,8 +1525,7 @@ bool test_InteractiveLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
 
 	pinfo.identity_info.domain_name.string = account_domain;
 	pinfo.identity_info.parameter_control = parameter_control;
-	pinfo.identity_info.logon_id_low = 0;
-	pinfo.identity_info.logon_id_high = 0;
+	pinfo.identity_info.logon_id = 0;
 	pinfo.identity_info.account_name.string = account_name;
 	pinfo.identity_info.workstation.string = workstation_name;
 
diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c
index 92861f4c8aa..cb4778ecd79 100644
--- a/source4/torture/rpc/samr.c
+++ b/source4/torture/rpc/samr.c
@@ -3058,8 +3058,7 @@ static bool test_SamLogon(struct torture_context *tctx,
 	identity.parameter_control =
 		MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
 		MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
-	identity.logon_id_low = 0;
-	identity.logon_id_high = 0;
+	identity.logon_id = 0;
 	identity.workstation.string = cli_credentials_get_workstation(test_credentials);
 
 	if (interactive) {
diff --git a/source4/torture/rpc/samsync.c b/source4/torture/rpc/samsync.c
index e71faf899a5..869d3ba96b6 100644
--- a/source4/torture/rpc/samsync.c
+++ b/source4/torture/rpc/samsync.c
@@ -64,8 +64,7 @@ static NTSTATUS test_SamLogon(struct torture_context *tctx,
 
 	ninfo.identity_info.domain_name.string = domain;
 	ninfo.identity_info.parameter_control = 0;
-	ninfo.identity_info.logon_id_low = 0;
-	ninfo.identity_info.logon_id_high = 0;
+	ninfo.identity_info.logon_id = 0;
 	ninfo.identity_info.account_name.string = account_name;
 	ninfo.identity_info.workstation.string = workstation;
 	generate_random_buffer(ninfo.challenge,
diff --git a/source4/torture/rpc/schannel.c b/source4/torture/rpc/schannel.c
index de3a36eaa4f..5b40af216a5 100644
--- a/source4/torture/rpc/schannel.c
+++ b/source4/torture/rpc/schannel.c
@@ -105,8 +105,7 @@ bool test_netlogon_ex_ops(struct dcerpc_pipe *p, struct torture_context *tctx,
 	ninfo.nt.length = nt_resp.length;
 
 	ninfo.identity_info.parameter_control = 0;
-	ninfo.identity_info.logon_id_low = 0;
-	ninfo.identity_info.logon_id_high = 0;
+	ninfo.identity_info.logon_id = 0;
 	ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials);
 
 	logon.network = &ninfo;
@@ -872,8 +871,7 @@ static bool torture_schannel_bench_start(struct torture_schannel_bench_conn *con
 	conn->ninfo.nt.length = nt_resp.length;
 
 	conn->ninfo.identity_info.parameter_control = 0;
-	conn->ninfo.identity_info.logon_id_low = 0;
-	conn->ninfo.identity_info.logon_id_high = 0;
+	conn->ninfo.identity_info.logon_id = 0;
 	conn->ninfo.identity_info.workstation.string = cli_credentials_get_workstation(conn->wks_creds);
 
 	conn->r.in.server_name = talloc_asprintf(conn->tmp, "\\\\%s", dcerpc_server_name(conn->pipe));
-- 
2.18.1


From 09cdcf8ec51eb796d0d5b266bf2125c456d54e6c Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Fri, 1 Feb 2019 09:40:10 +1300
Subject: [PATCH 07/12] auth log: Log the netlogon logon id.

Add code to log the logonId in the JSON Authentication messages.

The version number for Authentication messages changes from 1.1 to 1.2
to reflect this.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 auth/auth_log.c    | 11 ++++++++++-
 auth/common_auth.h |  1 +
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/auth/auth_log.c b/auth/auth_log.c
index 8f1ae61a99e..d9b83b0ea0d 100644
--- a/auth/auth_log.c
+++ b/auth/auth_log.c
@@ -41,7 +41,7 @@
  * increment the major version.
  */
 #define AUTH_MAJOR 1
-#define AUTH_MINOR 1
+#define AUTH_MINOR 2
 #define AUTHZ_MAJOR 1
 #define AUTHZ_MINOR 1
 
@@ -151,6 +151,7 @@ static void log_authentication_event_json(
 	struct json_object wrapper = json_empty_object;
 	struct json_object authentication = json_empty_object;
 	char negotiate_flags[11];
+	char logon_id[19];
 	int rc = 0;
 
 	authentication = json_new_object();
@@ -167,6 +168,14 @@ static void log_authentication_event_json(
 	if (rc != 0) {
 		goto failure;
 	}
+	snprintf(logon_id,
+		 sizeof( logon_id),
+		 "%"PRIx64"",
+		 ui->logon_id);
+	rc = json_add_string(&authentication, "logonId", logon_id);
+	if (rc != 0) {
+		goto failure;
+	}
 	rc = json_add_int(&authentication, "logonType", get_logon_type(ui));
 	if (rc != 0) {
 		goto failure;
diff --git a/auth/common_auth.h b/auth/common_auth.h
index d8377eb5347..0443c4e8044 100644
--- a/auth/common_auth.h
+++ b/auth/common_auth.h
@@ -51,6 +51,7 @@ struct auth_usersupplied_info
 
 	bool mapped_state;
 	bool was_mapped;
+	uint64_t logon_id;
 	/* the values the client gives us */
 	struct {
 		const char *account_name;
-- 
2.18.1


From d32d1dbf9f9c7eae0eb975f536b17b627be379e9 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Fri, 1 Feb 2019 09:41:18 +1300
Subject: [PATCH 08/12] s4 rpc netlogon: Pass logon_id to auth logging

Pass the logon_id passed in the netlogon identity information to
auth_logging.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 source4/rpc_server/netlogon/dcerpc_netlogon.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 530350d442a..0bb350aa9a8 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -1056,6 +1056,9 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
 		NT_STATUS_HAVE_NO_MEMORY(user_info->password.hash.nt);
 		*user_info->password.hash.nt = r->in.logon->password->ntpassword;
 
+		user_info->logon_id
+		    = r->in.logon->password->identity_info.logon_id;
+
 		break;
 	case NetlogonNetworkInformation:
 	case NetlogonNetworkTransitiveInformation:
@@ -1080,6 +1083,9 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
 		user_info->password.response.lanman = data_blob_talloc(mem_ctx, r->in.logon->network->lm.data, r->in.logon->network->lm.length);
 		user_info->password.response.nt = data_blob_talloc(mem_ctx, r->in.logon->network->nt.data, r->in.logon->network->nt.length);
 
+		user_info->logon_id
+		    = r->in.logon->network->identity_info.logon_id;
+
 		nt_status = NTLMv2_RESPONSE_verify_netlogon_creds(
 					user_info->client.account_name,
 					user_info->client.domain_name,
@@ -1108,6 +1114,9 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base_call(struct dcesrv_netr_LogonSamL
 
 			r->out.validation->generic = generic;
 
+			user_info->logon_id
+			    = r->in.logon->generic->identity_info.logon_id;
+
 			irpc_handle = irpc_binding_handle_by_name(mem_ctx,
 								  dce_call->msg_ctx,
 								  "kdc_server",
-- 
2.18.1


From 5500120ade052151176e0f5fb7be5ddee3afae36 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Fri, 1 Feb 2019 13:46:01 +1300
Subject: [PATCH 09/12] kdc hdb: Generate and pass logon ID

Generate and pass the logon_id in SamLogon calls

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 source4/kdc/hdb-samba4.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c
index 638179e8806..c42d742a50f 100644
--- a/source4/kdc/hdb-samba4.c
+++ b/source4/kdc/hdb-samba4.c
@@ -361,7 +361,7 @@ static void send_bad_password_netlogon(TALLOC_CTX *mem_ctx,
 
 	identity_info->domain_name.string = user_info->mapped.domain_name;
 	identity_info->parameter_control = user_info->logon_parameters; /* TODO */
-	identity_info->logon_id = 0;
+	identity_info->logon_id = user_info->logon_id;
 	identity_info->account_name.string = user_info->mapped.account_name;
 	identity_info->workstation.string
 		= talloc_asprintf(identity_info, "krb5-bad-pw on RODC from %s",
@@ -396,6 +396,7 @@ static krb5_error_code hdb_samba4_auth_status(krb5_context context, HDB *db,
 									struct samba_kdc_db_context);
 
 	struct ldb_dn *domain_dn = ldb_get_default_basedn(kdc_db_ctx->samdb);
+	uint64_t logon_id = generate_random_u64();
 
 	/*
 	 * Forcing this via the NTLM auth structure is not ideal, but
@@ -411,7 +412,8 @@ static krb5_error_code hdb_samba4_auth_status(krb5_context context, HDB *db,
 		},
 		.service_description = "Kerberos KDC",
 		.auth_description = "ENC-TS Pre-authentication",
-		.password_type = auth_type
+		.password_type = auth_type,
+		.logon_id = logon_id
 	};
 
 	size_t sa_socklen = 0;
-- 
2.18.1


From 5e200489a91970e1d0dd78007774d81da2bfe3bf Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Fri, 1 Feb 2019 13:49:49 +1300
Subject: [PATCH 10/12] winbind: Generate and pass logon ID

Generate a random logon_id and pass it in the SamLogon calls.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 source3/rpc_client/cli_netlogon.c    | 11 +++++++----
 source3/rpc_client/cli_netlogon.h    |  3 +++
 source3/rpcclient/cmd_netlogon.c     |  3 +++
 source3/winbindd/winbindd_dual_srv.c |  1 +
 source3/winbindd/winbindd_pam.c      | 24 +++++++++++++++++++++++-
 source3/winbindd/winbindd_proto.h    |  1 +
 source4/auth/ntlm/auth_winbind.c     |  2 +-
 7 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/source3/rpc_client/cli_netlogon.c b/source3/rpc_client/cli_netlogon.c
index c7cc183dd16..3ed49686206 100644
--- a/source3/rpc_client/cli_netlogon.c
+++ b/source3/rpc_client/cli_netlogon.c
@@ -458,6 +458,7 @@ NTSTATUS rpccli_netlogon_password_logon(
 	const char *username,
 	const char *password,
 	const char *workstation,
+	const uint64_t logon_id,
 	enum netr_LogonInfoClass logon_type,
 	uint8_t *authoritative,
 	uint32_t *flags,
@@ -508,7 +509,7 @@ NTSTATUS rpccli_netlogon_password_logon(
 
 		password_info->identity_info.domain_name.string		= domain;
 		password_info->identity_info.parameter_control		= logon_parameters;
-		password_info->identity_info.logon_id			= 0xbeef0000dead;
+		password_info->identity_info.logon_id			= logon_id;
 		password_info->identity_info.account_name.string	= username;
 		password_info->identity_info.workstation.string		= workstation_slash;
 
@@ -550,7 +551,7 @@ NTSTATUS rpccli_netlogon_password_logon(
 
 		network_info->identity_info.domain_name.string		= domain;
 		network_info->identity_info.parameter_control		= logon_parameters;
-		network_info->identity_info.logon_id			= 0xbeef0000dead;
+		network_info->identity_info.logon_id			= logon_id;
 		network_info->identity_info.account_name.string		= username;
 		network_info->identity_info.workstation.string		= workstation_slash;
 
@@ -605,6 +606,7 @@ NTSTATUS rpccli_netlogon_network_logon(
 	const char *username,
 	const char *domain,
 	const char *workstation,
+	const uint64_t logon_id,
 	const uint8_t chal[8],
 	DATA_BLOB lm_response,
 	DATA_BLOB nt_response,
@@ -668,7 +670,7 @@ NTSTATUS rpccli_netlogon_network_logon(
 
 	network_info->identity_info.domain_name.string		= domain;
 	network_info->identity_info.parameter_control		= logon_parameters;
-	network_info->identity_info.logon_id			= 0xbeef0000dead;
+	network_info->identity_info.logon_id			= logon_id;
 	network_info->identity_info.account_name.string		= username;
 	network_info->identity_info.workstation.string		= workstation_name_slash;
 
@@ -707,6 +709,7 @@ NTSTATUS rpccli_netlogon_interactive_logon(
 	const char *username,
 	const char *domain,
 	const char *workstation,
+	const uint64_t logon_id,
 	DATA_BLOB lm_hash,
 	DATA_BLOB nt_hash,
 	enum netr_LogonInfoClass logon_type,
@@ -768,7 +771,7 @@ NTSTATUS rpccli_netlogon_interactive_logon(
 
 	password_info->identity_info.domain_name.string		= domain;
 	password_info->identity_info.parameter_control		= logon_parameters;
-	password_info->identity_info.logon_id			= 0xbeef0000dead;
+	password_info->identity_info.logon_id			= logon_id;
 	password_info->identity_info.account_name.string	= username;
 	password_info->identity_info.workstation.string		= workstation_name_slash;
 
diff --git a/source3/rpc_client/cli_netlogon.h b/source3/rpc_client/cli_netlogon.h
index d0232b51d98..362321f312f 100644
--- a/source3/rpc_client/cli_netlogon.h
+++ b/source3/rpc_client/cli_netlogon.h
@@ -68,6 +68,7 @@ NTSTATUS rpccli_netlogon_password_logon(
 	const char *username,
 	const char *password,
 	const char *workstation,
+	const uint64_t logon_id,
 	enum netr_LogonInfoClass logon_type,
 	uint8_t *authoritative,
 	uint32_t *flags,
@@ -81,6 +82,7 @@ NTSTATUS rpccli_netlogon_network_logon(
 	const char *username,
 	const char *domain,
 	const char *workstation,
+	const uint64_t logon_id,
 	const uint8_t chal[8],
 	DATA_BLOB lm_response,
 	DATA_BLOB nt_response,
@@ -97,6 +99,7 @@ NTSTATUS rpccli_netlogon_interactive_logon(
 	const char *username,
 	const char *domain,
 	const char *workstation,
+	const uint64_t logon_id,
 	DATA_BLOB lm_hash,
 	DATA_BLOB nt_hash,
 	enum netr_LogonInfoClass logon_type,
diff --git a/source3/rpcclient/cmd_netlogon.c b/source3/rpcclient/cmd_netlogon.c
index 39bee441b73..4db23793c63 100644
--- a/source3/rpcclient/cmd_netlogon.c
+++ b/source3/rpcclient/cmd_netlogon.c
@@ -500,6 +500,7 @@ static NTSTATUS cmd_netlogon_sam_logon(struct rpc_pipe_client *cli,
 	uint32_t flags = 0;
 	uint16_t validation_level;
 	union netr_Validation *validation = NULL;
+	uint64_t logon_id = 0;
 
 	/* Check arguments */
 
@@ -525,6 +526,7 @@ static NTSTATUS cmd_netlogon_sam_logon(struct rpc_pipe_client *cli,
 		result = NT_STATUS_UNSUCCESSFUL;
 		goto done;
 	}
+	logon_id = generate_random_u64();
 
 	/* Perform the sam logon */
 
@@ -536,6 +538,7 @@ static NTSTATUS cmd_netlogon_sam_logon(struct rpc_pipe_client *cli,
 						username,
 						password,
 						workstation,
+						logon_id,
 						logon_type,
 						&authoritative,
 						&flags,
diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
index ab14f5d51a0..a34fce4bbe5 100644
--- a/source3/winbindd/winbindd_dual_srv.c
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -1001,6 +1001,7 @@ NTSTATUS _winbind_SamLogon(struct pipes_struct *p,
 				       identity_info->account_name.string,
 				       identity_info->domain_name.string,
 				       identity_info->workstation.string,
+				       identity_info->logon_id,
 				       challenge,
 				       lm_response, nt_response,
 				       &r->out.authoritative,
diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c
index b81f2722c42..4405205a5f2 100644
--- a/source3/winbindd/winbindd_pam.c
+++ b/source3/winbindd/winbindd_pam.c
@@ -1337,7 +1337,9 @@ done:
 
 static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
 					  uint32_t logon_parameters,
-					  const char *domain, const char *user,
+					  const char *domain,
+					  const char *user,
+					  const uint64_t logon_id,
 					  const DATA_BLOB *challenge,
 					  const DATA_BLOB *lm_resp,
 					  const DATA_BLOB *nt_resp,
@@ -1387,6 +1389,7 @@ static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
 	}
 
 	user_info->logon_parameters = logon_parameters;
+	user_info->logon_id = logon_id;
 
 	/* We don't want any more mapping of the username */
 	user_info->mapped_state = True;
@@ -1452,6 +1455,7 @@ static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
 					    const char *password,
 					    const char *domainname,
 					    const char *workstation,
+					    const uint64_t logon_id,
 					    bool plaintext_given,
 					    const uint8_t chal[8],
 					    DATA_BLOB lm_response,
@@ -1564,6 +1568,7 @@ static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
 				username,
 				password,
 				workstation,
+				logon_id,
 				logon_type_i,
 				authoritative,
 				flags,
@@ -1578,6 +1583,7 @@ static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
 				username,
 				domainname,
 				workstation,
+				logon_id,
 				lm_response,
 				nt_response,
 				logon_type_i,
@@ -1594,6 +1600,7 @@ static NTSTATUS winbind_samlogon_retry_loop(struct winbindd_domain *domain,
 				username,
 				domainname,
 				workstation,
+				logon_id,
 				chal,
 				lm_response,
 				nt_response,
@@ -1683,6 +1690,7 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(
 	struct winbindd_domain *domain,
 	const char *user,
 	const char *pass,
+	uint64_t logon_id,
 	uint32_t request_flags,
 	uint16_t *_validation_level,
 	union netr_Validation **_validation)
@@ -1760,6 +1768,7 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(
 
 		result = winbindd_dual_auth_passdb(
 			talloc_tos(), 0, name_domain, name_user,
+			logon_id,
 			&chal_blob, &lm_resp, &nt_resp,
 			true, /* interactive */
 			&authoritative,
@@ -1795,6 +1804,7 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(
 					     pass,
 					     name_domain,
 					     lp_netbios_name(),
+					     logon_id,
 					     true, /* plaintext_given */
 					     NULL,
 					     data_blob_null, data_blob_null,
@@ -1910,6 +1920,7 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
 	union netr_Validation *validation = NULL;
 	NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
 	bool ok;
+	uint64_t logon_id = 0;
 
 	/* Ensure null termination */
 	state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
@@ -1917,6 +1928,10 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
 	/* Ensure null termination */
 	state->request->data.auth.pass[sizeof(state->request->data.auth.pass)-1]='\0';
 
+	/*
+	 * Generate a logon_id for this session.
+	 */
+	logon_id = generate_random_u64();
 	DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
 		  state->request->data.auth.user));
 
@@ -2037,6 +2052,7 @@ sam_logon:
 			state->mem_ctx, domain,
 			state->request->data.auth.user,
 			state->request->data.auth.pass,
+			logon_id,
 			state->request->flags,
 			&validation_level,
 			&validation);
@@ -2246,6 +2262,7 @@ NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
 			       const char *name_user,
 			       const char *name_domain,
 			       const char *workstation,
+			       const uint64_t logon_id,
 			       const uint8_t chal[8],
 			       DATA_BLOB lm_response,
 			       DATA_BLOB nt_response,
@@ -2277,6 +2294,7 @@ NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
 			talloc_tos(),
 			logon_parameters,
 			name_domain, name_user,
+			logon_id,
 			&chal_blob, &lm_response, &nt_response,
 			interactive,
 			authoritative,
@@ -2310,6 +2328,7 @@ NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
 					     name_domain,
 					     /* Bug #3248 - found by Stefan Burkei. */
 					     workstation, /* We carefully set this above so use it... */
+					     logon_id,
 					     false, /* plaintext_given */
 					     chal,
 					     lm_response,
@@ -2411,6 +2430,7 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
 	const char *name_user = NULL;
 	const char *name_domain = NULL;
 	const char *workstation;
+	uint64_t logon_id = 0;
 	uint8_t authoritative = 0;
 	uint32_t flags = 0;
 	uint16_t validation_level;
@@ -2427,6 +2447,7 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
 	name_user = state->request->data.auth_crap.user;
 	name_domain = state->request->data.auth_crap.domain;
 	workstation = state->request->data.auth_crap.workstation;
+	logon_id = generate_random_u64();
 
 	DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
 		  name_domain, name_user));
@@ -2464,6 +2485,7 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
 				       name_domain,
 				       /* Bug #3248 - found by Stefan Burkei. */
 				       workstation, /* We carefully set this above so use it... */
+				       logon_id,
 				       state->request->data.auth_crap.chal,
 				       lm_resp,
 				       nt_resp,
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index be3626dc477..85a490a4feb 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -439,6 +439,7 @@ NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
 			       const char *name_user,
 			       const char *name_domain,
 			       const char *workstation,
+			       const uint64_t logon_id,
 			       const uint8_t chal[8],
 			       DATA_BLOB lm_response,
 			       DATA_BLOB nt_response,
diff --git a/source4/auth/ntlm/auth_winbind.c b/source4/auth/ntlm/auth_winbind.c
index d7c26e7a1a7..d7879966603 100644
--- a/source4/auth/ntlm/auth_winbind.c
+++ b/source4/auth/ntlm/auth_winbind.c
@@ -174,7 +174,7 @@ static struct tevent_req *winbind_check_password_send(TALLOC_CTX *mem_ctx,
 
 	identity_info->domain_name.string	= user_info->client.domain_name;
 	identity_info->parameter_control	= user_info->logon_parameters; /* see MSV1_0_* */
-	identity_info->logon_id			= 0;
+	identity_info->logon_id			= user_info->logon_id;
 	identity_info->account_name.string	= user_info->client.account_name;
 	identity_info->workstation.string	= user_info->workstation_name;
 
-- 
2.18.1


From d595414be125d26f1482ecd376e9086dbc12d9c2 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 28 Jan 2019 15:31:46 +1300
Subject: [PATCH 11/12] winbind: Log PAM and NTLM authentications.

Generate JSON authentication messages for winbind PAM_AUTH and
PAM_AUTH_CRAP requests.  The logon_id in these messages can be used to
link them to the SamLogon messages.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 selftest/knownfail.d/winbind_auth    |   4 -
 source3/winbindd/winbindd_dual_srv.c |   4 +
 source3/winbindd/winbindd_pam.c      | 237 +++++++++++++++++++++++++--
 source3/winbindd/winbindd_proto.h    |   4 +
 4 files changed, 227 insertions(+), 22 deletions(-)
 delete mode 100644 selftest/knownfail.d/winbind_auth

diff --git a/selftest/knownfail.d/winbind_auth b/selftest/knownfail.d/winbind_auth
deleted file mode 100644
index e0e0dd08906..00000000000
--- a/selftest/knownfail.d/winbind_auth
+++ /dev/null
@@ -1,4 +0,0 @@
-^samba.tests.auth_log_winbind.samba.tests.auth_log_winbind.AuthLogTestsWinbind.test_ntlm_auth
-^samba.tests.auth_log_winbind.samba.tests.auth_log_winbind.AuthLogTestsWinbind.test_wbinfo
-^samba.tests.auth_log_winbind.samba.tests.auth_log_winbind.AuthLogTestsWinbind.test_wbinfo_ntlmv1
-
diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c
index a34fce4bbe5..13345caa41b 100644
--- a/source3/winbindd/winbindd_dual_srv.c
+++ b/source3/winbindd/winbindd_dual_srv.c
@@ -1002,8 +1002,12 @@ NTSTATUS _winbind_SamLogon(struct pipes_struct *p,
 				       identity_info->domain_name.string,
 				       identity_info->workstation.string,
 				       identity_info->logon_id,
+				       "SamLogon",
+				       0,
 				       challenge,
 				       lm_response, nt_response,
+				       p->remote_address,
+				       p->local_address,
 				       &r->out.authoritative,
 				       true, /* skip_sam */
 				       &flags,
diff --git a/source3/winbindd/winbindd_pam.c b/source3/winbindd/winbindd_pam.c
index 4405205a5f2..1e6591aea65 100644
--- a/source3/winbindd/winbindd_pam.c
+++ b/source3/winbindd/winbindd_pam.c
@@ -46,6 +46,8 @@
 #include "libsmb/samlogon_cache.h"
 #include "rpc_client/util_netlogon.h"
 #include "libads/krb5_errs.h"
+#include "param/param.h"
+#include "messaging/messaging.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_WINBIND
@@ -1340,9 +1342,13 @@ static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
 					  const char *domain,
 					  const char *user,
 					  const uint64_t logon_id,
+					  const char *client_name,
+					  const int client_pid,
 					  const DATA_BLOB *challenge,
 					  const DATA_BLOB *lm_resp,
 					  const DATA_BLOB *nt_resp,
+					  const struct tsocket_address *remote,
+					  const struct tsocket_address *local,
 					  bool interactive,
 					  uint8_t *pauthoritative,
 					  struct netr_SamInfo3 **pinfo3)
@@ -1350,11 +1356,9 @@ static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
 	struct auth_context *auth_context;
 	struct auth_serversupplied_info *server_info;
 	struct auth_usersupplied_info *user_info = NULL;
-	struct tsocket_address *local;
 	struct netr_SamInfo3 *info3;
 	NTSTATUS status;
 	bool ok;
-	int rc;
 	TALLOC_CTX *frame = talloc_stackframe();
 
 	/*
@@ -1362,23 +1366,8 @@ static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
 	 */
 	*pauthoritative = 1;
 
-	rc = tsocket_address_inet_from_strings(frame,
-					       "ip",
-					       "127.0.0.1",
-					       0,
-					       &local);
-	if (rc < 0) {
-		TALLOC_FREE(frame);
-		return NT_STATUS_NO_MEMORY;
-	}
-
-	/*
-	 * TODO: We should get the service description passed in from
-	 * the winbind client, so we can have "smb2", "squid" or "samr" logged
-	 * here.
-	 */
 	status = make_user_info(frame, &user_info, user, user, domain, domain,
-				lp_netbios_name(), local, local,
+				lp_netbios_name(), remote, local,
 				"winbind",
 				lm_resp, nt_resp, NULL, NULL,
 				NULL, AUTH_PASSWORD_RESPONSE);
@@ -1390,6 +1379,12 @@ static NTSTATUS winbindd_dual_auth_passdb(TALLOC_CTX *mem_ctx,
 
 	user_info->logon_parameters = logon_parameters;
 	user_info->logon_id = logon_id;
+	user_info->auth_description = talloc_asprintf(
+		frame, "PASSDB, %s, %d", client_name, client_pid);
+	if (user_info->auth_description == NULL) {
+		TALLOC_FREE(frame);
+		return NT_STATUS_NO_MEMORY;
+	}
 
 	/* We don't want any more mapping of the username */
 	user_info->mapped_state = True;
@@ -1691,7 +1686,11 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(
 	const char *user,
 	const char *pass,
 	uint64_t logon_id,
+	const char *client_name,
+	const int client_pid,
 	uint32_t request_flags,
+	const struct tsocket_address *remote,
+	const struct tsocket_address *local,
 	uint16_t *_validation_level,
 	union netr_Validation **_validation)
 {
@@ -1769,7 +1768,11 @@ static NTSTATUS winbindd_dual_pam_auth_samlogon(
 		result = winbindd_dual_auth_passdb(
 			talloc_tos(), 0, name_domain, name_user,
 			logon_id,
+			client_name,
+			client_pid,
 			&chal_blob, &lm_resp, &nt_resp,
+			remote,
+			local,
 			true, /* interactive */
 			&authoritative,
 			&info3);
@@ -1908,6 +1911,139 @@ done:
 	return result;
 }
 
+/*
+ * @brief build a tsocket_address for the remote address of the supplied socket
+ *
+ */
+static struct tsocket_address *get_remote_address(TALLOC_CTX *mem_ctx, int sock)
+{
+	struct sockaddr_storage st = {0};
+	struct sockaddr *sar = (struct sockaddr *)&st;
+	socklen_t sa_len = sizeof(st);
+	struct tsocket_address *remote = NULL;
+	int ret = 0;
+
+	ret = getpeername(sock, sar, &sa_len);
+	if (ret != 0) {
+		DBG_ERR("getpeername failed - %s", strerror(errno));
+		return NULL;
+	}
+	ret = tsocket_address_bsd_from_sockaddr(mem_ctx, sar, sa_len, &remote);
+	if (ret != 0) {
+		DBG_ERR("tsocket_address_bsd_from_sockaddr failed - %s",
+			strerror(errno));
+		return NULL;
+	}
+	return remote;
+}
+
+/*
+ * @brief build a tsocket_address for the local address of the supplied socket
+ *
+ */
+static struct tsocket_address *get_local_address(TALLOC_CTX *mem_ctx, int sock)
+{
+	struct sockaddr_storage st = {0};
+	struct sockaddr *sar = (struct sockaddr *)&st;
+	socklen_t sa_len = sizeof(st);
+	struct tsocket_address *local = NULL;
+	int ret = 0;
+
+	ret = getsockname(sock, sar, &sa_len);
+	if (ret != 0) {
+		DBG_ERR("getsockname failed - %s", strerror(errno));
+		return NULL;
+	}
+	ret = tsocket_address_bsd_from_sockaddr(mem_ctx, sar, sa_len, &local);
+	if (ret != 0) {
+		DBG_ERR("tsocket_address_bsd_from_sockaddr failed - %s",
+			strerror(errno));
+		return NULL;
+	}
+	return local;
+}
+
+/*
+ * @brief generate an authentication message in the logs.
+ *
+ */
+static void log_authentication(
+	TALLOC_CTX *mem_ctx,
+	const struct winbindd_domain *domain,
+	const struct winbindd_cli_state *state,
+	const struct timeval start_time,
+	const uint64_t logon_id,
+	const char *command,
+	const char *user_name,
+	const char *domain_name,
+	const char *workstation,
+	const DATA_BLOB lm_resp,
+	const DATA_BLOB nt_resp,
+	const struct tsocket_address *remote,
+	const struct tsocket_address *local,
+	NTSTATUS result)
+{
+
+	struct auth_usersupplied_info *ui = NULL;
+	struct dom_sid *sid = NULL;
+	struct loadparm_context *lp_ctx = NULL;
+	struct imessaging_context *msg_ctx = NULL;
+
+	ui = talloc_zero(mem_ctx, struct auth_usersupplied_info);
+	ui->logon_id = logon_id;
+	ui->service_description = "winbind";
+	ui->password.response.nt.length = nt_resp.length;
+	ui->password.response.nt.data = nt_resp.data;
+	ui->password.response.lanman.length = lm_resp.length;
+	ui->password.response.lanman.data = lm_resp.data;
+	if (nt_resp.length == 0 && lm_resp.length == 0) {
+		ui->password_state = AUTH_PASSWORD_PLAIN;
+	} else {
+		ui->password_state = AUTH_PASSWORD_RESPONSE;
+	}
+	/*
+	 * In the event of a failure ui->auth_description will be null,
+	 * the logging code handles this correctly so it can be ignored.
+	 */
+	ui->auth_description = talloc_asprintf(
+		ui,
+		"%s, %s, %d",
+		command,
+		state->request->client_name,
+		state->pid);
+	if (ui->auth_description == NULL) {
+		DBG_ERR("OOM Unable to create auth_description");
+	}
+	ui->client.account_name = user_name;
+	ui->client.domain_name = domain_name;
+	ui->workstation_name = workstation;
+	ui->remote_host = remote;
+	ui->local_host = local;
+
+	sid = dom_sid_parse_talloc(
+	    ui, state->response->data.auth.info3.dom_sid);
+	if (sid != NULL) {
+		sid_append_rid(sid, state->response->data.auth.info3.user_rid);
+	}
+
+	if (lp_auth_event_notification()) {
+		lp_ctx = loadparm_init_s3(ui, loadparm_s3_helpers());
+		msg_ctx = imessaging_client_init(
+		    ui, lp_ctx, global_event_context());
+	}
+	log_authentication_event(
+	    msg_ctx,
+	    lp_ctx,
+	    &start_time,
+	    ui,
+	    result,
+	    state->response->data.auth.info3.logon_dom,
+	    state->response->data.auth.info3.user_name,
+	    state->response->data.auth.unix_username,
+	    sid);
+	TALLOC_FREE(ui);
+}
+
 enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
 					    struct winbindd_cli_state *state)
 {
@@ -1921,6 +2057,9 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
 	NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
 	bool ok;
 	uint64_t logon_id = 0;
+	const struct timeval start_time = timeval_current();
+	const struct tsocket_address *remote = NULL;
+	const struct tsocket_address *local = NULL;
 
 	/* Ensure null termination */
 	state->request->data.auth.user[sizeof(state->request->data.auth.user)-1]='\0';
@@ -1932,6 +2071,8 @@ enum winbindd_result winbindd_dual_pam_auth(struct winbindd_domain *domain,
 	 * Generate a logon_id for this session.
 	 */
 	logon_id = generate_random_u64();
+	remote = get_remote_address(state->mem_ctx, state->sock);
+	local = get_local_address(state->mem_ctx, state->sock);
 	DEBUG(3, ("[%5lu]: dual pam auth %s\n", (unsigned long)state->pid,
 		  state->request->data.auth.user));
 
@@ -2053,7 +2194,11 @@ sam_logon:
 			state->request->data.auth.user,
 			state->request->data.auth.pass,
 			logon_id,
+			state->request->client_name,
+			state->pid,
 			state->request->flags,
+			remote,
+			local,
 			&validation_level,
 			&validation);
 
@@ -2252,6 +2397,26 @@ done:
 	      state->response->data.auth.nt_status_string,
 	      state->response->data.auth.pam_error));
 
+	/*
+	 * Log the winbind pam authentication, the logon_id will tie this to
+	 * any of the logons invoked from this request.
+	 */
+	log_authentication(
+	    state->mem_ctx,
+	    domain,
+	    state,
+	    start_time,
+	    logon_id,
+	    "PAM_AUTH",
+	    name_user,
+	    name_domain,
+	    NULL,
+	    data_blob_null,
+	    data_blob_null,
+	    remote,
+	    local,
+	    result);
+
 	return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
 }
 
@@ -2263,9 +2428,13 @@ NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
 			       const char *name_domain,
 			       const char *workstation,
 			       const uint64_t logon_id,
+			       const char* client_name,
+			       const int client_pid,
 			       const uint8_t chal[8],
 			       DATA_BLOB lm_response,
 			       DATA_BLOB nt_response,
+			       const struct tsocket_address *remote,
+			       const struct tsocket_address *local,
 			       uint8_t *authoritative,
 			       bool skip_sam,
 			       uint32_t *flags,
@@ -2295,7 +2464,11 @@ NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
 			logon_parameters,
 			name_domain, name_user,
 			logon_id,
+			client_name,
+			client_pid,
 			&chal_blob, &lm_response, &nt_response,
+			remote,
+			local,
 			interactive,
 			authoritative,
 			&info3);
@@ -2436,6 +2609,9 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
 	uint16_t validation_level;
 	union netr_Validation *validation = NULL;
 	DATA_BLOB lm_resp, nt_resp;
+	const struct timeval start_time = timeval_current();
+	const struct tsocket_address *remote = NULL;
+	const struct tsocket_address *local = NULL;
 
 	/* This is child-only, so no check for privileged access is needed
 	   anymore */
@@ -2448,6 +2624,8 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
 	name_domain = state->request->data.auth_crap.domain;
 	workstation = state->request->data.auth_crap.workstation;
 	logon_id = generate_random_u64();
+	remote = get_remote_address(state->mem_ctx, state->sock);
+	local = get_local_address(state->mem_ctx, state->sock);
 
 	DEBUG(3, ("[%5lu]: pam auth crap domain: %s user: %s\n", (unsigned long)state->pid,
 		  name_domain, name_user));
@@ -2486,9 +2664,13 @@ enum winbindd_result winbindd_dual_pam_auth_crap(struct winbindd_domain *domain,
 				       /* Bug #3248 - found by Stefan Burkei. */
 				       workstation, /* We carefully set this above so use it... */
 				       logon_id,
+				       state->request->client_name,
+				       state->request->pid,
 				       state->request->data.auth_crap.chal,
 				       lm_resp,
 				       nt_resp,
+				       remote,
+				       local,
 				       &authoritative,
 				       false,
 				       &flags,
@@ -2539,6 +2721,25 @@ done:
 	}
 
 	set_auth_errors(state->response, result);
+	/*
+	 * Log the winbind pam authentication, the logon_id will tie this to
+	 * any of the logons invoked from this request.
+	 */
+	log_authentication(
+	    state->mem_ctx,
+	    domain,
+	    state,
+	    start_time,
+	    logon_id,
+	    "NTLM_AUTH",
+	    name_user,
+	    name_domain,
+	    workstation,
+	    lm_resp,
+            nt_resp,
+	    remote,
+	    local,
+	    result);
 
 	return NT_STATUS_IS_OK(result) ? WINBINDD_OK : WINBINDD_ERROR;
 }
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index 85a490a4feb..c524d2050df 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -440,9 +440,13 @@ NTSTATUS winbind_dual_SamLogon(struct winbindd_domain *domain,
 			       const char *name_domain,
 			       const char *workstation,
 			       const uint64_t logon_id,
+			       const char *client_name,
+			       const int pid,
 			       const uint8_t chal[8],
 			       DATA_BLOB lm_response,
 			       DATA_BLOB nt_response,
+			       const struct tsocket_address *remote,
+			       const struct tsocket_address *local,
 			       uint8_t *authoritative,
 			       bool skip_sam,
 			       uint32_t *flags,
-- 
2.18.1


From ebe6cae992becbac968b571df9eee42211f57ee2 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Thu, 7 Feb 2019 09:57:14 +1300
Subject: [PATCH 12/12] WHATSNEW: winbind authentication logging

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
 WHATSNEW.txt | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/WHATSNEW.txt b/WHATSNEW.txt
index 85c417a61a9..79d114883c0 100644
--- a/WHATSNEW.txt
+++ b/WHATSNEW.txt
@@ -30,6 +30,27 @@ worker processes at startup and share the client connections amongst these
 workers. The number of worker processes can be configured by the 'prefork
 children' setting in the smb.conf (the default is 4).
 
+Authentication Logging.
+-----------------------
+
+Winbind now logs PAM_AUTH and NTLM_AUTH events, a new attribute "logonId" has
+been added to the Authentication JSON log messages.  This contains a random
+logon id that is generated for each PAM_AUTH and NTLM_AUTH request and is passed
+to SamLogon, linking the windbind and SamLogon requests.
+
+The serviceDescription of the messages is set to "winbind", the authDescription
+is set to one of:
+   "PASSDB, <command>, <pid>"
+   "PAM_AUTH, <command>, <pid>"
+   "NTLM_AUTH, <command>, <pid>"
+where:
+   <command> is the name of the command makinmg the winbind request i.e. wbinfo
+   <pid>     is the process id of the requesting process.
+
+The version of the JSON Authentication messages has been changed to 1.2 from 1.1
+
+
+
 REMOVED FEATURES
 ================
 
-- 
2.18.1

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20190208/2c2290bf/signature-0001.sig>


More information about the samba-technical mailing list