[WIP] Log database changes.
Gary Lockyer
gary at catalyst.net.nz
Sun May 6 19:20:50 UTC 2018
Current state of this task.
Comments appreciated.
Thanks
Gary
On 05/04/18 15:13, Gary Lockyer via samba-technical wrote:
> I'm currently adding logging of Database changes, the attached patch set
> is the work to date.
>
> It still needs the the following done:
> * separating out the common code with the auth logging
> * cleaning up the code
> * plumbing the remote IP through
> - it's not currently available in all cases
> * Add a session ID GUID and log that in the Authorization messages as
> well
> * Log/Audit group membership changes.
> * Write unit and integration Tests.
>
> Notes:
> * Currently the attribute truncation limit is set artificially low to
> aid development
>
> Sample Human readable messages.
> Values enclosed in {} are base64 encoded
> Values ending with ... have been truncated.
>
> Line breaks added for clarity
>
> Samdb Change [Add] at [Thu, 05 Apr 2018 14:56:28.086708 NZST]
> transaction id [d9184e5e-aeca-4793-b7a7-a8a558378421]
> status [Success] remote host [Unknown]
> SID [S-1-5-21-202143440-2159076023-2784540227-500]
> DN [CN=krbtgt,CN=Users,DC=addom,DC=samba,DC=example,DC=com]
> attributes [
> accountExpires [9223372036854775807]
> adminCount [1]
> clearTextPassword [REDACTED SECRET ATTRIBUTE]
> description [Key Distribution Cen...]
> isCriticalSystemObject [TRUE]
> objectClass [top] [person] [organizationalPerson] [user]
> objectSid {AQUAAAAAAAUVAAAA0HYMDLfisIA=...}
> sAMAccountName [krbtgt]
> servicePrincipalName [kadmin/changepw]
> showInAdvancedViewOnly [TRUE] userAccountControl [514]]
>
> Password Change [Reset] at [Thu, 05 Apr 2018 14:56:28.086738 NZST]
> transaction id [d9184e5e-aeca-4793-b7a7-a8a558378421]
> status [Success] remote host [Unknown]
> SID [S-1-5-21-202143440-2159076023-2784540227-500]
> DN [CN=krbtgt,CN=Users,DC=addom,DC=samba,DC=example,DC=com]
>
>
> Sample JSON messages:
> JSON samdbChange: {
> "timestamp": "2018-04-05T14:56:28.086749+1200",
> "type": "samdbChange",
> "samdbChange": {
> "status": "Success",
> "version": {"major": 1, "minor": 0},
> "remoteAddress": "NULL",
> "operation": "Add",
> "userSid": "S-1-5-21-202143440-2159076023-2784540227-500",
> "dn": "CN=krbtgt,CN=Users,DC=addom,DC=samba,DC=example,DC=com",
> "transactionId": "d9184e5e-aeca-4793-b7a7-a8a558378421",
> "attributes": {
> "adminCount": {"values": [{"value": "1"}]},
> "objectSid": {"values": [
> {"truncated": true, "base64": true,
> "value": "AQUAAAAAAAUVAAAA0HYMDLfisIA="}]},
> "accountExpires": {"values": [{"value": "9223372036854775807"}]},
> "objectClass": {"values": [
> {"value": "top"},
> {"value": "person"},
> {"value": "organizationalPerson"},
> {"value": "user"}
> ]},
> "clearTextPassword": {"redacted": true},
> "description": {"values": [
> {"truncated": true, "value": "Key Distribution Cen"}
> ]},
> "isCriticalSystemObject": {"values": [{"value": "TRUE"}]},
> "sAMAccountName": {"values": [{"value": "krbtgt"}]},
> "servicePrincipalName": {"values": [{"value": "kadmin/changepw"}]},
> "showInAdvancedViewOnly": {"values": [{"value": "TRUE"}]},
> "userAccountControl": {"values": [{"value": "514"}]}}}}
>
> JSON passwordChange: {
> "timestamp": "2018-04-05T14:56:28.086947+1200",
> "type": "passwordChange",
> "passwordChange": {
> "status": "Success",
> "version": {"major": 1, "minor": 0},
> "remoteAddress": "NULL",
> "userSid": "S-1-5-21-202143440-2159076023-2784540227-500",
> "action": "Reset",
> "dn": "CN=krbtgt,CN=Users,DC=addom,DC=samba,DC=example,DC=com",
> "transactionId": "d9184e5e-aeca-4793-b7a7-a8a558378421"}}
>
> Comments appreciated
> Gary
>
-------------- next part --------------
From bc942578632d50936bc18ff537ece760b2c83c4b Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Wed, 4 Apr 2018 11:55:00 +1200
Subject: [PATCH 01/21] dsdb: refactor password attibutes to constant
The password attributes are defined as literal in two places in the
password_hash code. They will also be needed to support password change
logging. This patch replaces the individual definitions with a shared
constant.
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
source4/dsdb/common/util.h | 6 ++++++
source4/dsdb/samdb/ldb_modules/password_hash.c | 8 ++------
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/source4/dsdb/common/util.h b/source4/dsdb/common/util.h
index ede6d8b..85fabde 100644
--- a/source4/dsdb/common/util.h
+++ b/source4/dsdb/common/util.h
@@ -65,6 +65,12 @@ bool is_attr_in_list(const char * const * attrs, const char *attr);
#define DSDB_SECRET_ATTRIBUTES_COMMA ,
#define DSDB_SECRET_ATTRIBUTES DSDB_SECRET_ATTRIBUTES_EX(DSDB_SECRET_ATTRIBUTES_COMMA)
+#define DSDB_PASSWORD_ATTRIBUTES \
+ "userPassword", \
+ "clearTextPassword", \
+ "unicodePwd", \
+ "dBCSPwd"
+
struct GUID;
char *NS_GUID_string(TALLOC_CTX *mem_ctx, const struct GUID *guid);
diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
index 146fb6f..2641cb9 100644
--- a/source4/dsdb/samdb/ldb_modules/password_hash.c
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -4008,10 +4008,7 @@ static int password_hash_needed(struct ldb_module *module,
const struct ldb_message *msg = NULL;
struct ph_context *ac = NULL;
const char *passwordAttrs[] = {
- "userPassword",
- "clearTextPassword",
- "unicodePwd",
- "dBCSPwd",
+ DSDB_PASSWORD_ATTRIBUTES,
NULL
};
const char **a = NULL;
@@ -4242,8 +4239,7 @@ static int password_hash_modify(struct ldb_module *module, struct ldb_request *r
{
struct ldb_context *ldb = ldb_module_get_ctx(module);
struct ph_context *ac = NULL;
- const char *passwordAttrs[] = { "userPassword", "clearTextPassword",
- "unicodePwd", "dBCSPwd", NULL }, **l;
+ const char *passwordAttrs[] = {DSDB_PASSWORD_ATTRIBUTES, NULL}, **l;
unsigned int del_attr_cnt, add_attr_cnt, rep_attr_cnt;
struct ldb_message_element *passwordAttr;
struct ldb_message *msg;
--
2.7.4
From a3f4e69cd34e907fe3094327692d3933de57c5e2 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Tue, 10 Apr 2018 06:44:00 +1200
Subject: [PATCH 02/21] auth: Add unique session GUID identifier
Generate a GUID for each successful authorization, this will allow the
tying of events in the logs back to a specific session.
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
librpc/idl/auth.idl | 7 +++++++
source3/auth/auth_util.c | 6 ++++++
source4/auth/session.c | 2 ++
3 files changed, 15 insertions(+)
diff --git a/librpc/idl/auth.idl b/librpc/idl/auth.idl
index 6cc7dcf..d26f575 100644
--- a/librpc/idl/auth.idl
+++ b/librpc/idl/auth.idl
@@ -105,6 +105,13 @@ interface auth
[noprint] DATA_BLOB session_key;
[value(NULL), ignore] cli_credentials *credentials;
+
+ /*
+ * It is really handy to have our authorization code log a
+ * token that can be used to tie later requests togeather.
+ * We generate this in auth_generate_session_info()
+ */
+ GUID unique_session_token;
} auth_session_info;
typedef [public] struct {
diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c
index 3b951e7..24d1e37 100644
--- a/source3/auth/auth_util.c
+++ b/source3/auth/auth_util.c
@@ -488,6 +488,8 @@ NTSTATUS create_local_token(TALLOC_CTX *mem_ctx,
return NT_STATUS_NO_MEMORY;
}
+ session_info->unique_session_token = GUID_random();
+
*session_info_out = session_info;
return NT_STATUS_OK;
}
@@ -658,6 +660,8 @@ NTSTATUS create_local_token(TALLOC_CTX *mem_ctx,
return status;
}
+ session_info->unique_session_token = GUID_random();
+
*session_info_out = session_info;
return NT_STATUS_OK;
}
@@ -1209,6 +1213,8 @@ done:
return status;
}
+ session_info->unique_session_token = GUID_random();
+
*session_info_out = talloc_move(mem_ctx, &session_info);
TALLOC_FREE(frame);
return NT_STATUS_OK;
diff --git a/source4/auth/session.c b/source4/auth/session.c
index 982d51d..c27d273 100644
--- a/source4/auth/session.c
+++ b/source4/auth/session.c
@@ -220,6 +220,8 @@ _PUBLIC_ NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx,
return nt_status;
}
+ session_info->unique_session_token = GUID_random();
+
session_info->credentials = NULL;
talloc_steal(mem_ctx, session_info);
--
2.7.4
From 2235afa0e985a6a72f492e6e2f8f89625de7b515 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Tue, 10 Apr 2018 06:47:40 +1200
Subject: [PATCH 03/21] auth log: Log the unique session GUID
Log the unique_session_token GUID on successful Authorizations.
This patch adds the "sessionID" attribute to the Authorization object
and increments the version to 1.1
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
auth/auth_log.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/auth/auth_log.c b/auth/auth_log.c
index c143ae3..97b6537 100644
--- a/auth/auth_log.c
+++ b/auth/auth_log.c
@@ -43,7 +43,7 @@
#define AUTH_MAJOR 1
#define AUTH_MINOR 0
#define AUTHZ_MAJOR 1
-#define AUTHZ_MINOR 0
+#define AUTHZ_MINOR 1
#include "includes.h"
#include "../lib/tsocket/tsocket.h"
@@ -56,6 +56,7 @@
#include "source4/lib/messaging/irpc.h"
#include "lib/util/server_id_db.h"
#include "lib/param/param.h"
+#include "librpc/ndr/libndr.h"
/*
* Get a human readable timestamp.
@@ -431,6 +432,26 @@ static void add_sid(struct json_context *context,
}
/*
+ * Add a formatted string representation of a GUID to a json object.
+ *
+ */
+static void add_guid(struct json_context *context,
+ const char *name,
+ struct GUID *guid)
+{
+
+ char *guid_str;
+ struct GUID_txt_buf guid_buff;
+
+ if (context->error) {
+ return;
+ }
+
+ guid_str = GUID_buf_string(guid, &guid_buff);
+ add_string(context, name, guid_str);
+}
+
+/*
* Write a machine parsable json formatted authentication log entry.
*
* IF removing or changing the format/meaning of a field please update the
@@ -561,6 +582,9 @@ static void log_successful_authz_event_json(
add_string(&authorization, "domain", session_info->info->domain_name);
add_string(&authorization, "account", session_info->info->account_name);
add_sid(&authorization, "sid", &session_info->security_token->sids[0]);
+ add_guid(&authorization,
+ "sessionId",
+ &session_info->unique_session_token);
add_string(&authorization,
"logonServer",
session_info->info->logon_server);
--
2.7.4
From 85bd9a164ab6f6fe84fc62d083d79efd47dd3b8f Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 30 Apr 2018 09:13:58 +1200
Subject: [PATCH 04/21] auth logging tests: Add tests for sessionId
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
python/samba/tests/auth_log.py | 5 +++++
python/samba/tests/auth_log_base.py | 8 ++++++++
python/samba/tests/auth_log_ncalrpc.py | 1 +
python/samba/tests/auth_log_netlogon.py | 1 +
python/samba/tests/auth_log_netlogon_bad_creds.py | 1 +
python/samba/tests/auth_log_samlogon.py | 1 +
6 files changed, 17 insertions(+)
diff --git a/python/samba/tests/auth_log.py b/python/samba/tests/auth_log.py
index 9e68c4f..34312cb 100644
--- a/python/samba/tests/auth_log.py
+++ b/python/samba/tests/auth_log.py
@@ -94,6 +94,7 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals(authTypes[2], msg["Authorization"]["authType"])
self.assertEquals("SMB", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
# Check the third message it should be an Authentication
# if we are expecting 4 messages
@@ -148,6 +149,7 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals(authTypes[3], msg["Authorization"]["authType"])
self.assertEquals("SMB", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
def test_rpc_ncacn_np_ntlm_dns_sign(self):
@@ -277,6 +279,7 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals(authTypes[1], msg["Authorization"]["authType"])
self.assertEquals("NONE", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
# Check the second message it should be an Authentication
msg = messages[1]
@@ -301,6 +304,7 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals(authTypes[1], msg["Authorization"]["authType"])
self.assertEquals("NONE", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
# Check the second message it should be an Authentication
msg = messages[1]
@@ -1313,3 +1317,4 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals("schannel", msg["Authorization"]["authType"])
self.assertEquals("SEAL", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
diff --git a/python/samba/tests/auth_log_base.py b/python/samba/tests/auth_log_base.py
index 6c1baea..5bb9821 100644
--- a/python/samba/tests/auth_log_base.py
+++ b/python/samba/tests/auth_log_base.py
@@ -27,6 +27,7 @@ from samba.dcerpc import srvsvc, dnsserver
import time
import json
import os
+import re
from samba import smb
from samba.samdb import SamDB
@@ -120,3 +121,10 @@ class AuthLogTestBase(samba.tests.TestCase):
return sd != "NETLOGON"
return list(filter(is_not_netlogon, messages))
+
+ GUID_RE = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
+ #
+ # Is the supplied GUID string correctly formatted
+ #
+ def is_guid(self, guid):
+ return re.match(self.GUID_RE, guid)
diff --git a/python/samba/tests/auth_log_ncalrpc.py b/python/samba/tests/auth_log_ncalrpc.py
index be7f6b2..2f61cc5 100644
--- a/python/samba/tests/auth_log_ncalrpc.py
+++ b/python/samba/tests/auth_log_ncalrpc.py
@@ -74,6 +74,7 @@ class AuthLogTestsNcalrpc(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals(authTypes[1], msg["Authorization"]["authType"])
self.assertEquals("NONE", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
# Check the second message it should be an Authentication
msg = messages[1]
diff --git a/python/samba/tests/auth_log_netlogon.py b/python/samba/tests/auth_log_netlogon.py
index 228fbe9..9b0512d 100644
--- a/python/samba/tests/auth_log_netlogon.py
+++ b/python/samba/tests/auth_log_netlogon.py
@@ -114,6 +114,7 @@ class AuthLogTestsNetLogon(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals("ncalrpc", msg["Authorization"]["authType"])
self.assertEquals("NONE", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
# Check the fourth message it should be a NETLOGON Authentication
msg = messages[3]
diff --git a/python/samba/tests/auth_log_netlogon_bad_creds.py b/python/samba/tests/auth_log_netlogon_bad_creds.py
index 2bae02e..c18d270 100644
--- a/python/samba/tests/auth_log_netlogon_bad_creds.py
+++ b/python/samba/tests/auth_log_netlogon_bad_creds.py
@@ -115,6 +115,7 @@ class AuthLogTestsNetLogonBadCreds(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals("ncalrpc", msg["Authorization"]["authType"])
self.assertEquals("NONE", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
def test_netlogon_bad_machine_name(self):
self._test_netlogon("bad_name",
diff --git a/python/samba/tests/auth_log_samlogon.py b/python/samba/tests/auth_log_samlogon.py
index a3a9f50..105a16d 100644
--- a/python/samba/tests/auth_log_samlogon.py
+++ b/python/samba/tests/auth_log_samlogon.py
@@ -171,6 +171,7 @@ class AuthLogTestsSamLogon(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals("ncalrpc", msg["Authorization"]["authType"])
self.assertEquals("NONE", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
def test_ncalrpc_samlogon(self):
--
2.7.4
From a4244b6bfc95bc4db3ef2a99eb28618bccadc35a Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 30 Apr 2018 10:35:25 +1200
Subject: [PATCH 05/21] auth logging tests: Clean up flake8 warnings
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
python/samba/tests/auth_log.py | 471 +++++++++++-----------
python/samba/tests/auth_log_base.py | 27 +-
python/samba/tests/auth_log_ncalrpc.py | 27 +-
python/samba/tests/auth_log_netlogon_bad_creds.py | 1 +
python/samba/tests/auth_log_pass_change.py | 148 +++----
python/samba/tests/auth_log_samlogon.py | 23 +-
6 files changed, 326 insertions(+), 371 deletions(-)
diff --git a/python/samba/tests/auth_log.py b/python/samba/tests/auth_log.py
index 34312cb..6cec63a 100644
--- a/python/samba/tests/auth_log.py
+++ b/python/samba/tests/auth_log.py
@@ -18,22 +18,18 @@
from __future__ import print_function
"""Tests for the Auth and AuthZ logging.
"""
-from samba import auth
import samba.tests
-from samba.messaging import Messaging
-from samba.dcerpc.messaging import MSG_AUTH_LOG, AUTH_EVENT_NAME
from samba.dcerpc import srvsvc, dnsserver
-import time
-import json
import os
from samba import smb
from samba.samdb import SamDB
import samba.tests.auth_log_base
-from samba.credentials import Credentials, DONT_USE_KERBEROS, MUST_USE_KERBEROS
+from samba.credentials import DONT_USE_KERBEROS, MUST_USE_KERBEROS
from samba import NTSTATUSError
from subprocess import call
from ldb import LdbError
+
class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
def setUp(self):
@@ -43,8 +39,6 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
def tearDown(self):
super(AuthLogTests, self).tearDown()
-
-
def _test_rpc_ncacn_np(self, authTypes, creds, service,
binding, protection, checkFunction):
def isLastExpectedMessage(msg):
@@ -59,8 +53,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
if service == "dnsserver":
x = dnsserver.dnsserver("ncacn_np:%s%s" % (self.server, binding),
- self.get_loadparm(),
- creds)
+ self.get_loadparm(),
+ creds)
elif service == "srvsvc":
x = srvsvc.srvsvc("ncacn_np:%s%s" % (self.server, binding),
self.get_loadparm(),
@@ -84,8 +78,9 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("SMB",
- msg["Authentication"]["serviceDescription"])
- self.assertEquals(authTypes[1], msg["Authentication"]["authDescription"])
+ msg["Authentication"]["serviceDescription"])
+ self.assertEquals(authTypes[1],
+ msg["Authentication"]["authDescription"])
# Check the second message it should be an Authorization
msg = messages[1]
@@ -106,11 +101,19 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertTrue(
- checkServiceDescription(msg["Authentication"]["serviceDescription"]))
+ checkServiceDescription(
+ msg["Authentication"]["serviceDescription"]))
- self.assertEquals(authTypes[3], msg["Authentication"]["authDescription"])
+ self.assertEquals(authTypes[3],
+ msg["Authentication"]["authDescription"])
- def rpc_ncacn_np_krb5_check(self, messages, authTypes, service, binding, protection):
+ def rpc_ncacn_np_krb5_check(
+ self,
+ messages,
+ authTypes,
+ service,
+ binding,
+ protection):
expected_messages = len(authTypes)
self.assertEquals(expected_messages,
@@ -124,8 +127,9 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("Kerberos KDC",
- msg["Authentication"]["serviceDescription"])
- self.assertEquals(authTypes[1], msg["Authentication"]["authDescription"])
+ msg["Authentication"]["serviceDescription"])
+ self.assertEquals(authTypes[1],
+ msg["Authentication"]["authDescription"])
# Check the second message it should be an Authentication
# This this the TCP Authentication in response to the message too big
@@ -134,8 +138,9 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("Kerberos KDC",
- msg["Authentication"]["serviceDescription"])
- self.assertEquals(authTypes[2], msg["Authentication"]["authDescription"])
+ msg["Authentication"]["serviceDescription"])
+ self.assertEquals(authTypes[2],
+ msg["Authentication"]["authDescription"])
# Check the third message it should be an Authorization
msg = messages[2]
@@ -151,7 +156,6 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("SMB", msg["Authorization"]["transportProtection"])
self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
-
def test_rpc_ncacn_np_ntlm_dns_sign(self):
creds = self.insta_creds(template=self.get_credentials(),
kerberos_state=DONT_USE_KERBEROS)
@@ -197,8 +201,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
"ENC-TS Pre-authentication",
"ENC-TS Pre-authentication",
"krb5"],
- creds, "dnsserver", "sign", "SIGN",
- self.rpc_ncacn_np_krb5_check)
+ creds, "dnsserver", "sign", "SIGN",
+ self.rpc_ncacn_np_krb5_check)
def test_rpc_ncacn_np_krb_srv_sign(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -207,8 +211,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
"ENC-TS Pre-authentication",
"ENC-TS Pre-authentication",
"krb5"],
- creds, "srvsvc", "sign", "SIGN",
- self.rpc_ncacn_np_krb5_check)
+ creds, "srvsvc", "sign", "SIGN",
+ self.rpc_ncacn_np_krb5_check)
def test_rpc_ncacn_np_krb_dns(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -234,9 +238,9 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
creds = self.insta_creds(template=self.get_credentials(),
kerberos_state=MUST_USE_KERBEROS)
self._test_rpc_ncacn_np(["ncacn_np",
- "ENC-TS Pre-authentication",
- "ENC-TS Pre-authentication",
- "krb5"],
+ "ENC-TS Pre-authentication",
+ "ENC-TS Pre-authentication",
+ "krb5"],
creds, "srvsvc", "", "SMB",
self.rpc_ncacn_np_krb5_check)
@@ -252,15 +256,15 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
binding = "[%s]" % binding
if service == "dnsserver":
- conn = dnsserver.dnsserver("ncacn_ip_tcp:%s%s" % (self.server, binding),
- self.get_loadparm(),
- creds)
+ conn = dnsserver.dnsserver(
+ "ncacn_ip_tcp:%s%s" % (self.server, binding),
+ self.get_loadparm(),
+ creds)
elif service == "srvsvc":
conn = srvsvc.srvsvc("ncacn_ip_tcp:%s%s" % (self.server, binding),
self.get_loadparm(),
creds)
-
messages = self.waitForMessages(isLastExpectedMessage, conn)
checkFunction(messages, authTypes, service, binding, protection)
@@ -286,8 +290,9 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("DCE/RPC",
- msg["Authentication"]["serviceDescription"])
- self.assertEquals(authTypes[2], msg["Authentication"]["authDescription"])
+ msg["Authentication"]["serviceDescription"])
+ self.assertEquals(authTypes[2],
+ msg["Authentication"]["authDescription"])
def rpc_ncacn_ip_tcp_krb5_check(self, messages, authTypes, service,
binding, protection):
@@ -311,16 +316,18 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("Kerberos KDC",
- msg["Authentication"]["serviceDescription"])
- self.assertEquals(authTypes[2], msg["Authentication"]["authDescription"])
+ msg["Authentication"]["serviceDescription"])
+ self.assertEquals(authTypes[2],
+ msg["Authentication"]["authDescription"])
# Check the third message it should be an Authentication
msg = messages[2]
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("Kerberos KDC",
- msg["Authentication"]["serviceDescription"])
- self.assertEquals(authTypes[2], msg["Authentication"]["authDescription"])
+ msg["Authentication"]["serviceDescription"])
+ self.assertEquals(authTypes[2],
+ msg["Authentication"]["authDescription"])
def test_rpc_ncacn_ip_tcp_ntlm_dns_sign(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -328,8 +335,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self._test_rpc_ncacn_ip_tcp(["NTLMSSP",
"ncacn_ip_tcp",
"NTLMSSP"],
- creds, "dnsserver", "sign", "SIGN",
- self.rpc_ncacn_ip_tcp_ntlm_check)
+ creds, "dnsserver", "sign", "SIGN",
+ self.rpc_ncacn_ip_tcp_ntlm_check)
def test_rpc_ncacn_ip_tcp_krb5_dns_sign(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -338,8 +345,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
"ncacn_ip_tcp",
"ENC-TS Pre-authentication",
"ENC-TS Pre-authentication"],
- creds, "dnsserver", "sign", "SIGN",
- self.rpc_ncacn_ip_tcp_krb5_check)
+ creds, "dnsserver", "sign", "SIGN",
+ self.rpc_ncacn_ip_tcp_krb5_check)
def test_rpc_ncacn_ip_tcp_ntlm_dns(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -347,8 +354,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self._test_rpc_ncacn_ip_tcp(["NTLMSSP",
"ncacn_ip_tcp",
"NTLMSSP"],
- creds, "dnsserver", "", "SIGN",
- self.rpc_ncacn_ip_tcp_ntlm_check)
+ creds, "dnsserver", "", "SIGN",
+ self.rpc_ncacn_ip_tcp_ntlm_check)
def test_rpc_ncacn_ip_tcp_krb5_dns(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -357,8 +364,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
"ncacn_ip_tcp",
"ENC-TS Pre-authentication",
"ENC-TS Pre-authentication"],
- creds, "dnsserver", "", "SIGN",
- self.rpc_ncacn_ip_tcp_krb5_check)
+ creds, "dnsserver", "", "SIGN",
+ self.rpc_ncacn_ip_tcp_krb5_check)
def test_rpc_ncacn_ip_tcp_ntlm_dns_connect(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -366,8 +373,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self._test_rpc_ncacn_ip_tcp(["NTLMSSP",
"ncacn_ip_tcp",
"NTLMSSP"],
- creds, "dnsserver", "connect", "NONE",
- self.rpc_ncacn_ip_tcp_ntlm_check)
+ creds, "dnsserver", "connect", "NONE",
+ self.rpc_ncacn_ip_tcp_ntlm_check)
def test_rpc_ncacn_ip_tcp_krb5_dns_connect(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -376,8 +383,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
"ncacn_ip_tcp",
"ENC-TS Pre-authentication",
"ENC-TS Pre-authentication"],
- creds, "dnsserver", "connect", "NONE",
- self.rpc_ncacn_ip_tcp_krb5_check)
+ creds, "dnsserver", "connect", "NONE",
+ self.rpc_ncacn_ip_tcp_krb5_check)
def test_rpc_ncacn_ip_tcp_ntlm_dns_seal(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -385,8 +392,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self._test_rpc_ncacn_ip_tcp(["NTLMSSP",
"ncacn_ip_tcp",
"NTLMSSP"],
- creds, "dnsserver", "seal", "SEAL",
- self.rpc_ncacn_ip_tcp_ntlm_check)
+ creds, "dnsserver", "seal", "SEAL",
+ self.rpc_ncacn_ip_tcp_ntlm_check)
def test_rpc_ncacn_ip_tcp_krb5_dns_seal(self):
creds = self.insta_creds(template=self.get_credentials(),
@@ -395,8 +402,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
"ncacn_ip_tcp",
"ENC-TS Pre-authentication",
"ENC-TS Pre-authentication"],
- creds, "dnsserver", "seal", "SEAL",
- self.rpc_ncacn_ip_tcp_krb5_check)
+ creds, "dnsserver", "seal", "SEAL",
+ self.rpc_ncacn_ip_tcp_krb5_check)
def test_ldap(self):
@@ -407,7 +414,7 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["authType"] == "krb5")
self.samdb = SamDB(url="ldap://%s" % os.environ["SERVER"],
- lp = self.get_loadparm(),
+ lp=self.get_loadparm(),
credentials=self.get_credentials())
messages = self.waitForMessages(isLastExpectedMessage)
@@ -420,18 +427,18 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("Kerberos KDC",
- msg["Authentication"]["serviceDescription"])
+ msg["Authentication"]["serviceDescription"])
self.assertEquals("ENC-TS Pre-authentication",
- msg["Authentication"]["authDescription"])
+ msg["Authentication"]["authDescription"])
- # Check the first message it should be an Authentication
+ # Check the second message it should be an Authentication
msg = messages[1]
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("Kerberos KDC",
- msg["Authentication"]["serviceDescription"])
+ msg["Authentication"]["serviceDescription"])
self.assertEquals("ENC-TS Pre-authentication",
- msg["Authentication"]["authDescription"])
+ msg["Authentication"]["authDescription"])
def test_ldap_ntlm(self):
@@ -442,7 +449,7 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["authType"] == "NTLMSSP")
self.samdb = SamDB(url="ldap://%s" % os.environ["SERVER_IP"],
- lp = self.get_loadparm(),
+ lp=self.get_loadparm(),
credentials=self.get_credentials())
messages = self.waitForMessages(isLastExpectedMessage)
@@ -454,7 +461,7 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("LDAP",
- msg["Authentication"]["serviceDescription"])
+ msg["Authentication"]["serviceDescription"])
self.assertEquals("NTLMSSP", msg["Authentication"]["authDescription"])
def test_ldap_simple_bind(self):
@@ -466,10 +473,10 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
creds = self.insta_creds(template=self.get_credentials())
creds.set_bind_dn("%s\\%s" % (creds.get_domain(),
- creds.get_username()))
+ creds.get_username()))
self.samdb = SamDB(url="ldaps://%s" % os.environ["SERVER"],
- lp = self.get_loadparm(),
+ lp=self.get_loadparm(),
credentials=creds)
messages = self.waitForMessages(isLastExpectedMessage)
@@ -482,27 +489,27 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("LDAP",
- msg["Authentication"]["serviceDescription"])
+ msg["Authentication"]["serviceDescription"])
self.assertEquals("simple bind",
- msg["Authentication"]["authDescription"])
+ msg["Authentication"]["authDescription"])
def test_ldap_simple_bind_bad_password(self):
def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
msg["Authentication"]["serviceDescription"] == "LDAP" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD" and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD") and
msg["Authentication"]["authDescription"] == "simple bind")
creds = self.insta_creds(template=self.get_credentials())
creds.set_password("badPassword")
creds.set_bind_dn("%s\\%s" % (creds.get_domain(),
- creds.get_username()))
+ creds.get_username()))
thrown = False
try:
self.samdb = SamDB(url="ldaps://%s" % os.environ["SERVER"],
- lp = self.get_loadparm(),
+ lp=self.get_loadparm(),
credentials=creds)
except LdbError:
thrown = True
@@ -513,13 +520,12 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
len(messages),
"Did not receive the expected number of messages")
-
def test_ldap_simple_bind_bad_user(self):
def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
msg["Authentication"]["serviceDescription"] == "LDAP" and
- msg["Authentication"]["status"]
- == "NT_STATUS_NO_SUCH_USER" and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_NO_SUCH_USER") and
msg["Authentication"]["authDescription"] == "simple bind")
creds = self.insta_creds(template=self.get_credentials())
@@ -528,7 +534,7 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
thrown = False
try:
self.samdb = SamDB(url="ldaps://%s" % os.environ["SERVER"],
- lp = self.get_loadparm(),
+ lp=self.get_loadparm(),
credentials=creds)
except LdbError:
thrown = True
@@ -539,13 +545,12 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
len(messages),
"Did not receive the expected number of messages")
-
def test_ldap_simple_bind_unparseable_user(self):
def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
msg["Authentication"]["serviceDescription"] == "LDAP" and
- msg["Authentication"]["status"]
- == "NT_STATUS_NO_SUCH_USER" and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_NO_SUCH_USER") and
msg["Authentication"]["authDescription"] == "simple bind")
creds = self.insta_creds(template=self.get_credentials())
@@ -554,7 +559,7 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
thrown = False
try:
self.samdb = SamDB(url="ldaps://%s" % os.environ["SERVER"],
- lp = self.get_loadparm(),
+ lp=self.get_loadparm(),
credentials=creds)
except LdbError:
thrown = True
@@ -572,23 +577,23 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
def test_ldap_anonymous_access_bind_only(self):
# Should be no logging for anonymous bind
# so receiving any message indicates a failure.
- def isLastExpectedMessage( msg):
+ def isLastExpectedMessage(msg):
return True
creds = self.insta_creds(template=self.get_credentials())
creds.set_anonymous()
self.samdb = SamDB(url="ldaps://%s" % os.environ["SERVER"],
- lp = self.get_loadparm(),
+ lp=self.get_loadparm(),
credentials=creds)
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
self.assertEquals(0,
len(messages),
"Did not receive the expected number of messages")
def test_ldap_anonymous_access(self):
- def isLastExpectedMessage( msg):
+ def isLastExpectedMessage(msg):
return (msg["type"] == "Authorization" and
msg["Authorization"]["serviceDescription"] == "LDAP" and
msg["Authorization"]["transportProtection"] == "TLS" and
@@ -599,19 +604,20 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
creds.set_anonymous()
self.samdb = SamDB(url="ldaps://%s" % os.environ["SERVER"],
- lp = self.get_loadparm(),
+ lp=self.get_loadparm(),
credentials=creds)
try:
- res = self.samdb.search(base=self.samdb.domain_dn())
- self.fail( "Expected an LdbError exception")
+ self.samdb.search(base=self.samdb.domain_dn())
+ self.fail("Expected an LdbError exception")
except LdbError:
pass
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
self.assertEquals(1,
len(messages),
"Did not receive the expected number of messages")
+
def test_smb(self):
def isLastExpectedMessage(msg):
return (msg["type"] == "Authorization" and
@@ -634,28 +640,28 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("Kerberos KDC",
- msg["Authentication"]["serviceDescription"])
+ msg["Authentication"]["serviceDescription"])
self.assertEquals("ENC-TS Pre-authentication",
- msg["Authentication"]["authDescription"])
+ msg["Authentication"]["authDescription"])
# Check the second message it should be an Authentication
msg = messages[1]
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("Kerberos KDC",
- msg["Authentication"]["serviceDescription"])
+ msg["Authentication"]["serviceDescription"])
self.assertEquals("ENC-TS Pre-authentication",
- msg["Authentication"]["authDescription"])
+ msg["Authentication"]["authDescription"])
def test_smb_bad_password(self):
def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "Kerberos KDC" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD" and
- msg["Authentication"]["authDescription"]
- == "ENC-TS Pre-authentication")
+ (msg["Authentication"]["serviceDescription"] ==
+ "Kerberos KDC") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD") and
+ (msg["Authentication"]["authDescription"] ==
+ "ENC-TS Pre-authentication"))
creds = self.insta_creds(template=self.get_credentials())
creds.set_password("badPassword")
@@ -675,16 +681,15 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
len(messages),
"Did not receive the expected number of messages")
-
def test_smb_bad_user(self):
def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "Kerberos KDC" and
- msg["Authentication"]["status"]
- == "NT_STATUS_NO_SUCH_USER" and
- msg["Authentication"]["authDescription"]
- == "ENC-TS Pre-authentication")
+ (msg["Authentication"]["serviceDescription"] ==
+ "Kerberos KDC") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_NO_SUCH_USER") and
+ (msg["Authentication"]["authDescription"] ==
+ "ENC-TS Pre-authentication"))
creds = self.insta_creds(template=self.get_credentials())
creds.set_username("badUser")
@@ -829,8 +834,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authentication"]["serviceDescription"] == "SMB" and
msg["Authentication"]["authDescription"] == "NTLMSSP" and
msg["Authentication"]["passwordType"] == "NTLMv2" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD")
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD"))
creds = self.insta_creds(template=self.get_credentials(),
kerberos_state=DONT_USE_KERBEROS)
@@ -857,8 +862,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authentication"]["serviceDescription"] == "SMB" and
msg["Authentication"]["authDescription"] == "NTLMSSP" and
msg["Authentication"]["passwordType"] == "NTLMv2" and
- msg["Authentication"]["status"]
- == "NT_STATUS_NO_SUCH_USER")
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_NO_SUCH_USER"))
creds = self.insta_creds(template=self.get_credentials(),
kerberos_state=DONT_USE_KERBEROS)
@@ -916,8 +921,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authentication"]["serviceDescription"] == "SMB" and
msg["Authentication"]["authDescription"] == "bare-NTLM" and
msg["Authentication"]["passwordType"] == "NTLMv1" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD")
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD"))
creds = self.insta_creds(template=self.get_credentials(),
kerberos_state=DONT_USE_KERBEROS)
@@ -935,7 +940,6 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
thrown = True
self.assertEquals(thrown, True)
-
messages = self.waitForMessages(isLastExpectedMessage)
self.assertEquals(1,
len(messages),
@@ -947,8 +951,8 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authentication"]["serviceDescription"] == "SMB" and
msg["Authentication"]["authDescription"] == "bare-NTLM" and
msg["Authentication"]["passwordType"] == "NTLMv1" and
- msg["Authentication"]["status"]
- == "NT_STATUS_NO_SUCH_USER")
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_NO_SUCH_USER"))
creds = self.insta_creds(template=self.get_credentials(),
kerberos_state=DONT_USE_KERBEROS)
@@ -966,7 +970,6 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
thrown = True
self.assertEquals(thrown, True)
-
messages = self.waitForMessages(isLastExpectedMessage)
self.assertEquals(1,
len(messages),
@@ -976,25 +979,24 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
+ def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "interactive" and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ (msg["Authentication"]["authDescription"] ==
+ "interactive") and
msg["Authentication"]["status"] == "NT_STATUS_OK" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = os.environ["USERNAME"]
password = os.environ["PASSWORD"]
samlogon = "samlogon %s %s %s %d" % (user, password, workstation, 1)
-
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1005,26 +1007,25 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
+ def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "interactive" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ (msg["Authentication"]["authDescription"] ==
+ "interactive") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD") and
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = os.environ["USERNAME"]
password = "badPassword"
samlogon = "samlogon %s %s %s %d" % (user, password, workstation, 1)
-
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1035,26 +1036,25 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
+ def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "interactive" and
- msg["Authentication"]["status"]
- == "NT_STATUS_NO_SUCH_USER" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ (msg["Authentication"]["authDescription"] ==
+ "interactive") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_NO_SUCH_USER") and
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = "badUser"
password = os.environ["PASSWORD"]
samlogon = "samlogon %s %s %s %d" % (user, password, workstation, 1)
-
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1065,25 +1065,23 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
+ def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "network" and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ msg["Authentication"]["authDescription"] == "network" and
msg["Authentication"]["status"] == "NT_STATUS_OK" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = os.environ["USERNAME"]
password = os.environ["PASSWORD"]
samlogon = "samlogon %s %s %s %d" % (user, password, workstation, 2)
-
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1094,26 +1092,24 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
+ def isLastExpectedMessage(msg):
return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "network" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ msg["Authentication"]["authDescription"] == "network" and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD") and
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = os.environ["USERNAME"]
password = "badPassword"
samlogon = "samlogon %s %s %s %d" % (user, password, workstation, 2)
-
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1124,26 +1120,24 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "network" and
- msg["Authentication"]["status"]
- == "NT_STATUS_NO_SUCH_USER" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ def isLastExpectedMessage(msg):
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ (msg["Authentication"]["authDescription"] == "network") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_NO_SUCH_USER") and
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = "badUser"
- password = os.environ["PASSWORD"]
+ password = os.environ["PASSWORD"]
samlogon = "samlogon %s %s %s %d" % (user, password, workstation, 2)
-
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1154,26 +1148,25 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "network" and
- msg["Authentication"]["status"] == "NT_STATUS_OK" and
- msg["Authentication"]["passwordType"] == "MSCHAPv2" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ def isLastExpectedMessage(msg):
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ (msg["Authentication"]["authDescription"] == "network") and
+ (msg["Authentication"]["status"] == "NT_STATUS_OK") and
+ (msg["Authentication"]["passwordType"] == "MSCHAPv2") and
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = os.environ["USERNAME"]
password = os.environ["PASSWORD"]
- samlogon = "samlogon %s %s %s %d 0x00010000" % (user, password, workstation, 2)
-
+ samlogon = "samlogon %s %s %s %d 0x00010000" % (
+ user, password, workstation, 2)
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1184,27 +1177,26 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "network" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD" and
- msg["Authentication"]["passwordType"] == "MSCHAPv2" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ def isLastExpectedMessage(msg):
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ (msg["Authentication"]["authDescription"] == "network") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD") and
+ (msg["Authentication"]["passwordType"] == "MSCHAPv2") and
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = os.environ["USERNAME"]
password = "badPassword"
- samlogon = "samlogon %s %s %s %d 0x00010000" % (user, password, workstation, 2)
-
+ samlogon = "samlogon %s %s %s %d 0x00010000" % (
+ user, password, workstation, 2)
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1215,27 +1207,26 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "network" and
- msg["Authentication"]["status"]
- == "NT_STATUS_NO_SUCH_USER" and
- msg["Authentication"]["passwordType"] == "MSCHAPv2" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ def isLastExpectedMessage(msg):
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ (msg["Authentication"]["authDescription"] == "network") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_NO_SUCH_USER") and
+ (msg["Authentication"]["passwordType"] == "MSCHAPv2") and
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = "badUser"
password = os.environ["PASSWORD"]
- samlogon = "samlogon %s %s %s %d 0x00010000" % (user, password, workstation, 2)
-
+ samlogon = "samlogon %s %s %s %d 0x00010000" % (
+ user, password, workstation, 2)
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1246,25 +1237,23 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "network" and
- msg["Authentication"]["status"] == "NT_STATUS_OK" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ def isLastExpectedMessage(msg):
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ (msg["Authentication"]["authDescription"] == "network") and
+ (msg["Authentication"]["status"] == "NT_STATUS_OK") and
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = os.environ["USERNAME"]
password = os.environ["PASSWORD"]
samlogon = "schannel;samlogon %s %s %s" % (user, password, workstation)
-
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
@@ -1278,32 +1267,32 @@ class AuthLogTests(samba.tests.auth_log_base.AuthLogTestBase):
msg["Authorization"]["serviceDescription"])
self.assertEquals("schannel", msg["Authorization"]["authType"])
self.assertEquals("SEAL", msg["Authorization"]["transportProtection"])
+ self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
# Signed logons get promoted to sealed, this test ensures that
- # this behaviour is not removed accidently
+ # this behaviour is not removed accidentally
def test_samlogon_schannel_sign(self):
workstation = "AuthLogTests"
- def isLastExpectedMessage( msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SamLogon" and
- msg["Authentication"]["authDescription"]
- == "network" and
- msg["Authentication"]["status"] == "NT_STATUS_OK" and
- msg["Authentication"]["workstation"]
- == r"\\%s" % workstation)
+ def isLastExpectedMessage(msg):
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SamLogon") and
+ (msg["Authentication"]["authDescription"] == "network") and
+ (msg["Authentication"]["status"] == "NT_STATUS_OK") and
+ (msg["Authentication"]["workstation"] ==
+ r"\\%s" % workstation))
server = os.environ["SERVER"]
user = os.environ["USERNAME"]
password = os.environ["PASSWORD"]
- samlogon = "schannelsign;samlogon %s %s %s" % (user, password, workstation)
-
+ samlogon = "schannelsign;samlogon %s %s %s" % (
+ user, password, workstation)
call(["bin/rpcclient", "-c", samlogon, "-U%", server])
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
messages = self.remove_netlogon_messages(messages)
received = len(messages)
self.assertIs(True,
diff --git a/python/samba/tests/auth_log_base.py b/python/samba/tests/auth_log_base.py
index 5bb9821..5a70ce3 100644
--- a/python/samba/tests/auth_log_base.py
+++ b/python/samba/tests/auth_log_base.py
@@ -19,34 +19,31 @@ from __future__ import print_function
"""Tests for the Auth and AuthZ logging.
"""
-from samba import auth
import samba.tests
from samba.messaging import Messaging
from samba.dcerpc.messaging import MSG_AUTH_LOG, AUTH_EVENT_NAME
-from samba.dcerpc import srvsvc, dnsserver
import time
import json
import os
import re
-from samba import smb
-from samba.samdb import SamDB
+
class AuthLogTestBase(samba.tests.TestCase):
def setUp(self):
super(AuthLogTestBase, self).setUp()
lp_ctx = self.get_loadparm()
- self.msg_ctx = Messaging((1,), lp_ctx=lp_ctx);
+ self.msg_ctx = Messaging((1,), lp_ctx=lp_ctx)
self.msg_ctx.irpc_add_name(AUTH_EVENT_NAME)
- def messageHandler( context, msgType, src, message):
+ def messageHandler(context, msgType, src, message):
# This does not look like sub unit output and it
# makes these tests much easier to debug.
print(message)
jsonMsg = json.loads(message)
- context["messages"].append( jsonMsg)
+ context["messages"].append(jsonMsg)
- self.context = { "messages": []}
+ self.context = {"messages": []}
self.msg_handler_and_context = (messageHandler, self.context)
self.msg_ctx.register(self.msg_handler_and_context,
msg_type=MSG_AUTH_LOG)
@@ -62,20 +59,19 @@ class AuthLogTestBase(samba.tests.TestCase):
self.msg_ctx.deregister(self.msg_handler_and_context,
msg_type=MSG_AUTH_LOG)
-
def waitForMessages(self, isLastExpectedMessage, connection=None):
"""Wait for all the expected messages to arrive
The connection is passed through to keep the connection alive
until all the logging messages have been received.
"""
- def completed( messages):
+ def completed(messages):
for message in messages:
- if isRemote( message) and isLastExpectedMessage( message):
+ if isRemote(message) and isLastExpectedMessage(message):
return True
return False
- def isRemote( message):
+ def isRemote(message):
remote = None
if message["type"] == "Authorization":
remote = message["Authorization"]["remoteAddress"]
@@ -93,19 +89,19 @@ class AuthLogTestBase(samba.tests.TestCase):
self.connection = connection
start_time = time.time()
- while not completed( self.context["messages"]):
+ while not completed(self.context["messages"]):
self.msg_ctx.loop_once(0.1)
if time.time() - start_time > 1:
self.connection = None
return []
self.connection = None
- return filter( isRemote, self.context["messages"])
+ return filter(isRemote, self.context["messages"])
# Discard any previously queued messages.
def discardMessages(self):
self.msg_ctx.loop_once(0.001)
- while len( self.context["messages"]):
+ while len(self.context["messages"]):
self.msg_ctx.loop_once(0.001)
self.context["messages"] = []
@@ -123,6 +119,7 @@ class AuthLogTestBase(samba.tests.TestCase):
return list(filter(is_not_netlogon, messages))
GUID_RE = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
+
#
# Is the supplied GUID string correctly formatted
#
diff --git a/python/samba/tests/auth_log_ncalrpc.py b/python/samba/tests/auth_log_ncalrpc.py
index 2f61cc5..849cee7 100644
--- a/python/samba/tests/auth_log_ncalrpc.py
+++ b/python/samba/tests/auth_log_ncalrpc.py
@@ -18,19 +18,12 @@
"""Tests for the Auth and AuthZ logging.
"""
-from samba import auth
import samba.tests
-from samba.messaging import Messaging
-from samba.dcerpc.messaging import MSG_AUTH_LOG, AUTH_EVENT_NAME
+from samba.credentials import DONT_USE_KERBEROS
from samba.dcerpc.dcerpc import AS_SYSTEM_MAGIC_PATH_TOKEN
from samba.dcerpc import samr
-import time
-import json
-import os
-from samba import smb
-from samba.samdb import SamDB
import samba.tests.auth_log_base
-from samba.credentials import Credentials, DONT_USE_KERBEROS, MUST_USE_KERBEROS
+
class AuthLogTestsNcalrpc(samba.tests.auth_log_base.AuthLogTestBase):
@@ -39,25 +32,23 @@ class AuthLogTestsNcalrpc(samba.tests.auth_log_base.AuthLogTestBase):
self.remoteAddress = AS_SYSTEM_MAGIC_PATH_TOKEN
def tearDown(self):
- super(AuthLogTestsNcalrpc , self).tearDown()
-
+ super(AuthLogTestsNcalrpc, self).tearDown()
def _test_rpc_ncaclrpc(self, authTypes, binding, creds,
protection, checkFunction):
- def isLastExpectedMessage( msg):
+ def isLastExpectedMessage(msg):
return (
msg["type"] == "Authorization" and
msg["Authorization"]["serviceDescription"] == "DCE/RPC" and
msg["Authorization"]["authType"] == authTypes[0] and
- msg["Authorization"]["transportProtection"] == protection
- )
+ msg["Authorization"]["transportProtection"] == protection)
if binding:
binding = "[%s]" % binding
samr.samr("ncalrpc:%s" % binding, self.get_loadparm(), creds)
- messages = self.waitForMessages( isLastExpectedMessage)
+ messages = self.waitForMessages(isLastExpectedMessage)
checkFunction(messages, authTypes, protection)
def rpc_ncacn_np_ntlm_check(self, messages, authTypes, protection):
@@ -81,9 +72,9 @@ class AuthLogTestsNcalrpc(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("Authentication", msg["type"])
self.assertEquals("NT_STATUS_OK", msg["Authentication"]["status"])
self.assertEquals("DCE/RPC",
- msg["Authentication"]["serviceDescription"])
- self.assertEquals(authTypes[2], msg["Authentication"]["authDescription"])
-
+ msg["Authentication"]["serviceDescription"])
+ self.assertEquals(authTypes[2],
+ msg["Authentication"]["authDescription"])
def test_ncalrpc_ntlm_dns_sign(self):
diff --git a/python/samba/tests/auth_log_netlogon_bad_creds.py b/python/samba/tests/auth_log_netlogon_bad_creds.py
index c18d270..a0a2e28 100644
--- a/python/samba/tests/auth_log_netlogon_bad_creds.py
+++ b/python/samba/tests/auth_log_netlogon_bad_creds.py
@@ -38,6 +38,7 @@ from samba.dsdb import UF_WORKSTATION_TRUST_ACCOUNT, UF_PASSWD_NOTREQD
from samba.dcerpc.misc import SEC_CHAN_WKSTA
from samba.dcerpc.netlogon import NETLOGON_NEG_STRONG_KEYS
+
class AuthLogTestsNetLogonBadCreds(samba.tests.auth_log_base.AuthLogTestBase):
def setUp(self):
diff --git a/python/samba/tests/auth_log_pass_change.py b/python/samba/tests/auth_log_pass_change.py
index 9782389..8890694 100644
--- a/python/samba/tests/auth_log_pass_change.py
+++ b/python/samba/tests/auth_log_pass_change.py
@@ -19,23 +19,20 @@ from __future__ import print_function
"""Tests for the Auth and AuthZ logging of password changes.
"""
-from samba import auth
import samba.tests
-from samba.messaging import Messaging
from samba.samdb import SamDB
from samba.auth import system_session
-import json
import os
import samba.tests.auth_log_base
from samba.tests import delete_force
from samba.net import Net
-from samba import ntstatus
import samba
from subprocess import call
from ldb import LdbError
USER_NAME = "authlogtestuser"
-USER_PASS = samba.generate_random_password(32,32)
+USER_PASS = samba.generate_random_password(32, 32)
+
class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
@@ -56,9 +53,6 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
base_dn = self.ldb.domain_dn()
print("base_dn %s" % base_dn)
- # Gets back the configuration basedn
- configuration_dn = self.ldb.get_config_basedn().get_linearized()
-
# Get the old "dSHeuristics" if it was set
dsheuristics = self.ldb.get_dsheuristics()
@@ -82,10 +76,10 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
# (Re)adds the test user USER_NAME with password USER_PASS
delete_force(self.ldb, "cn=" + USER_NAME + ",cn=users," + self.base_dn)
self.ldb.add({
- "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn,
- "objectclass": "user",
- "sAMAccountName": USER_NAME,
- "userPassword": USER_PASS
+ "dn": "cn=" + USER_NAME + ",cn=users," + self.base_dn,
+ "objectclass": "user",
+ "sAMAccountName": USER_NAME,
+ "userPassword": USER_PASS
})
# discard any auth log messages for the password setup
@@ -94,18 +88,16 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
def tearDown(self):
super(AuthLogPassChangeTests, self).tearDown()
-
def test_admin_change_password(self):
def isLastExpectedMessage(msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["status"]
- == "NT_STATUS_OK" and
- msg["Authentication"]["serviceDescription"]
- == "SAMR Password Change" and
- msg["Authentication"]["authDescription"]
- == "samr_ChangePasswordUser3")
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["status"] == "NT_STATUS_OK") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SAMR Password Change") and
+ (msg["Authentication"]["authDescription"] ==
+ "samr_ChangePasswordUser3"))
- creds = self.insta_creds(template = self.get_credentials())
+ creds = self.insta_creds(template=self.get_credentials())
lp = self.get_loadparm()
net = Net(creds, lp, server=self.server_ip)
@@ -115,7 +107,6 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
username=USER_NAME,
oldpassword=USER_PASS)
-
messages = self.waitForMessages(isLastExpectedMessage)
print("Received %d messages" % len(messages))
self.assertEquals(8,
@@ -124,13 +115,13 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
def test_admin_change_password_new_password_fails_restriction(self):
def isLastExpectedMessage(msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["status"]
- == "NT_STATUS_PASSWORD_RESTRICTION" and
- msg["Authentication"]["serviceDescription"]
- == "SAMR Password Change" and
- msg["Authentication"]["authDescription"]
- == "samr_ChangePasswordUser3")
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_PASSWORD_RESTRICTION") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SAMR Password Change") and
+ (msg["Authentication"]["authDescription"] ==
+ "samr_ChangePasswordUser3"))
creds = self.insta_creds(template=self.get_credentials())
@@ -143,7 +134,7 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
net.change_password(newpassword=password.encode('utf-8'),
oldpassword=USER_PASS,
username=USER_NAME)
- except Exception as msg:
+ except Exception:
exception_thrown = True
self.assertEquals(True, exception_thrown,
"Expected exception not thrown")
@@ -155,13 +146,13 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
def test_admin_change_password_unknown_user(self):
def isLastExpectedMessage(msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["status"]
- == "NT_STATUS_NO_SUCH_USER" and
- msg["Authentication"]["serviceDescription"]
- == "SAMR Password Change" and
- msg["Authentication"]["authDescription"]
- == "samr_ChangePasswordUser3")
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_NO_SUCH_USER") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SAMR Password Change") and
+ (msg["Authentication"]["authDescription"] ==
+ "samr_ChangePasswordUser3"))
creds = self.insta_creds(template=self.get_credentials())
@@ -174,7 +165,7 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
net.change_password(newpassword=password.encode('utf-8'),
oldpassword=USER_PASS,
username="badUser")
- except Exception as msg:
+ except Exception:
exception_thrown = True
self.assertEquals(True, exception_thrown,
"Expected exception not thrown")
@@ -186,13 +177,13 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
def test_admin_change_password_bad_original_password(self):
def isLastExpectedMessage(msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD" and
- msg["Authentication"]["serviceDescription"]
- == "SAMR Password Change" and
- msg["Authentication"]["authDescription"]
- == "samr_ChangePasswordUser3")
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SAMR Password Change") and
+ (msg["Authentication"]["authDescription"] ==
+ "samr_ChangePasswordUser3"))
creds = self.insta_creds(template=self.get_credentials())
@@ -205,7 +196,7 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
net.change_password(newpassword=password.encode('utf-8'),
oldpassword="badPassword",
username=USER_NAME)
- except Exception as msg:
+ except Exception:
exception_thrown = True
self.assertEquals(True, exception_thrown,
"Expected exception not thrown")
@@ -221,19 +212,19 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
# correctly, so we just check it triggers the wrong password path.
def test_rap_change_password(self):
def isLastExpectedMessage(msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["serviceDescription"]
- == "SAMR Password Change" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD" and
- msg["Authentication"]["authDescription"]
- == "OemChangePasswordUser2")
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "SAMR Password Change") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD") and
+ (msg["Authentication"]["authDescription"] ==
+ "OemChangePasswordUser2"))
username = os.environ["USERNAME"]
server = os.environ["SERVER"]
password = os.environ["PASSWORD"]
server_param = "--server=%s" % server
- creds = "-U%s%%%s" % (username,password)
+ creds = "-U%s%%%s" % (username, password)
call(["bin/net", "rap", server_param,
"password", USER_NAME, "notMyPassword", "notGoingToBeMyPassword",
server, creds, "--option=client ipc max protocol=nt1"])
@@ -245,23 +236,21 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
def test_ldap_change_password(self):
def isLastExpectedMessage(msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["status"]
- == "NT_STATUS_OK" and
- msg["Authentication"]["serviceDescription"]
- == "LDAP Password Change" and
- msg["Authentication"]["authDescription"]
- == "LDAP Modify")
-
- new_password = samba.generate_random_password(32,32)
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["status"] == "NT_STATUS_OK") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "LDAP Password Change") and
+ (msg["Authentication"]["authDescription"] ==
+ "LDAP Modify"))
+
+ new_password = samba.generate_random_password(32, 32)
self.ldb.modify_ldif(
"dn: cn=" + USER_NAME + ",cn=users," + self.base_dn + "\n" +
"changetype: modify\n" +
"delete: userPassword\n" +
"userPassword: " + USER_PASS + "\n" +
"add: userPassword\n" +
- "userPassword: " + new_password + "\n"
- )
+ "userPassword: " + new_password + "\n")
messages = self.waitForMessages(isLastExpectedMessage)
print("Received %d messages" % len(messages))
@@ -276,11 +265,10 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
def test_ldap_change_password_bad_user(self):
def isLastExpectedMessage(msg):
return (msg["type"] == "Authorization" and
- msg["Authorization"]["serviceDescription"]
- == "LDAP" and
+ msg["Authorization"]["serviceDescription"] == "LDAP" and
msg["Authorization"]["authType"] == "krb5")
- new_password = samba.generate_random_password(32,32)
+ new_password = samba.generate_random_password(32, 32)
try:
self.ldb.modify_ldif(
"dn: cn=" + "badUser" + ",cn=users," + self.base_dn + "\n" +
@@ -288,8 +276,7 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
"delete: userPassword\n" +
"userPassword: " + USER_PASS + "\n" +
"add: userPassword\n" +
- "userPassword: " + new_password + "\n"
- )
+ "userPassword: " + new_password + "\n")
self.fail()
except LdbError as e:
(num, msg) = e.args
@@ -303,15 +290,15 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
def test_ldap_change_password_bad_original_password(self):
def isLastExpectedMessage(msg):
- return (msg["type"] == "Authentication" and
- msg["Authentication"]["status"]
- == "NT_STATUS_WRONG_PASSWORD" and
- msg["Authentication"]["serviceDescription"]
- == "LDAP Password Change" and
- msg["Authentication"]["authDescription"]
- == "LDAP Modify")
-
- new_password = samba.generate_random_password(32,32)
+ return ((msg["type"] == "Authentication") and
+ (msg["Authentication"]["status"] ==
+ "NT_STATUS_WRONG_PASSWORD") and
+ (msg["Authentication"]["serviceDescription"] ==
+ "LDAP Password Change") and
+ (msg["Authentication"]["authDescription"] ==
+ "LDAP Modify"))
+
+ new_password = samba.generate_random_password(32, 32)
try:
self.ldb.modify_ldif(
"dn: cn=" + USER_NAME + ",cn=users," + self.base_dn + "\n" +
@@ -319,8 +306,7 @@ class AuthLogPassChangeTests(samba.tests.auth_log_base.AuthLogTestBase):
"delete: userPassword\n" +
"userPassword: " + "badPassword" + "\n" +
"add: userPassword\n" +
- "userPassword: " + new_password + "\n"
- )
+ "userPassword: " + new_password + "\n")
self.fail()
except LdbError as e1:
(num, msg) = e1.args
diff --git a/python/samba/tests/auth_log_samlogon.py b/python/samba/tests/auth_log_samlogon.py
index 105a16d..d3b14f3 100644
--- a/python/samba/tests/auth_log_samlogon.py
+++ b/python/samba/tests/auth_log_samlogon.py
@@ -19,14 +19,8 @@
Tests auth logging tests that exercise SamLogon
"""
-from samba import auth
import samba.tests
-from samba.messaging import Messaging
-from samba.dcerpc.messaging import MSG_AUTH_LOG, AUTH_EVENT_NAME
-import time
-import json
import os
-from samba import smb
from samba.samdb import SamDB
import samba.tests.auth_log_base
from samba.credentials import (
@@ -42,6 +36,7 @@ from samba.tests import delete_force
from samba.dsdb import UF_WORKSTATION_TRUST_ACCOUNT, UF_PASSWD_NOTREQD
from samba.dcerpc.misc import SEC_CHAN_WKSTA
+
class AuthLogTestsSamLogon(samba.tests.auth_log_base.AuthLogTestBase):
def setUp(self):
@@ -63,9 +58,8 @@ class AuthLogTestsSamLogon(samba.tests.auth_log_base.AuthLogTestBase):
self.samlogon_dn = ("cn=%s,cn=users,%s" %
(self.netbios_name, self.base_dn))
-
def tearDown(self):
- super(AuthLogTestsSamLogon , self).tearDown()
+ super(AuthLogTestsSamLogon, self).tearDown()
delete_force(self.ldb, self.samlogon_dn)
def _test_samlogon(self, binding, creds, checkFunction):
@@ -119,7 +113,6 @@ class AuthLogTestsSamLogon(samba.tests.auth_log_base.AuthLogTestBase):
eol.AvId = ntlmssp.MsvAvEOL
target_info.pair = [domainname, computername, eol]
-
target_info_blob = ndr_pack(target_info)
response = creds.get_ntlm_response(flags=CLI_CRED_NTLMv2_AUTH,
@@ -144,15 +137,14 @@ class AuthLogTestsSamLogon(samba.tests.auth_log_base.AuthLogTestBase):
validation_level = samba.dcerpc.netlogon.NetlogonValidationSamInfo4
-
- result = netlogon_conn.netr_LogonSamLogonEx(os.environ["SERVER"],
- machine_creds.get_workstation(),
- logon_level, logon,
- validation_level, netr_flags)
+ result = netlogon_conn.netr_LogonSamLogonEx(
+ os.environ["SERVER"],
+ machine_creds.get_workstation(),
+ logon_level, logon,
+ validation_level, netr_flags)
(validation, authoritative, netr_flags_out) = result
-
messages = self.waitForMessages(isLastExpectedMessage, netlogon_conn)
checkFunction(messages)
@@ -173,7 +165,6 @@ class AuthLogTestsSamLogon(samba.tests.auth_log_base.AuthLogTestBase):
self.assertEquals("NONE", msg["Authorization"]["transportProtection"])
self.assertTrue(self.is_guid(msg["Authorization"]["sessionId"]))
-
def test_ncalrpc_samlogon(self):
creds = self.insta_creds(template=self.get_credentials(),
--
2.7.4
From 60faf08cb796c77c9ba0413b5d71c372803baea5 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Tue, 10 Apr 2018 06:45:47 +1200
Subject: [PATCH 06/21] auth logging: Extract common audit logging code
Extract the common audit logging code into a library to allow it's
re-use in other logging modules.
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
lib/audit_logging/audit_logging.c | 816 +++++++++++++++++++++++++++
lib/audit_logging/audit_logging.h | 98 ++++
lib/audit_logging/tests/audit_logging_test.c | 557 ++++++++++++++++++
lib/audit_logging/wscript_build | 23 +
source4/selftest/tests.py | 2 +
wscript_build | 1 +
6 files changed, 1497 insertions(+)
create mode 100644 lib/audit_logging/audit_logging.c
create mode 100644 lib/audit_logging/audit_logging.h
create mode 100644 lib/audit_logging/tests/audit_logging_test.c
create mode 100644 lib/audit_logging/wscript_build
diff --git a/lib/audit_logging/audit_logging.c b/lib/audit_logging/audit_logging.c
new file mode 100644
index 0000000..a2a8471
--- /dev/null
+++ b/lib/audit_logging/audit_logging.c
@@ -0,0 +1,816 @@
+/*
+ common routines for audit logging
+
+ Copyright (C) Andrew Bartlett <abartlet at samba.org> 2018
+
+ 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/>.
+*/
+
+/*
+ * Error handling:
+ *
+ * The json_object structure contains a boolean 'error'. This is set whenever
+ * an error is detected. All the library functions check this flag and return
+ * immediately if it is set.
+ *
+ * if (object->error) {
+ * return;
+ * }
+ *
+ * This allows the operations to be sequenced naturally with out the clutter
+ * of error status checks.
+ *
+ * audit = json_new_object();
+ * json_add_version(&audit, OPERATION_MAJOR, OPERATION_MINOR);
+ * json_add_int(&audit, "statusCode", ret);
+ * json_add_string(&audit, "status", ldb_strerror(ret));
+ * json_add_string(&audit, "operation", operation);
+ * json_add_address(&audit, "remoteAddress", remote);
+ * json_add_sid(&audit, "userSid", sid);
+ * json_add_string(&audit, "dn", dn);
+ * json_add_guid(&audit, "transactionId", &ac->transaction_guid);
+ * json_add_guid(&audit, "sessionId", unique_session_token);
+ *
+ * The assumptions are that errors will be rare, and that the audit logging
+ * code should not cause failures. So errors are logged but processing
+ * continues on a best effort basis.
+ */
+
+#include "includes.h"
+
+#include "librpc/ndr/libndr.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/messaging/messaging.h"
+#include "auth/common_auth.h"
+#include "audit_logging.h"
+
+/*
+ * @brief Get a human readable timestamp.
+ *
+ * Returns the current time formatted as
+ * "Tue, 14 Mar 2017 08:38:42.209028 NZDT"
+ *
+ * The returned string is allocated by talloc in the supplied context.
+ * It is the callers responsibility to free it.
+ *
+ * @param mem_ctx talloc memory context that owns the returned string.
+ *
+ * @return a human readable time stamp.
+ *
+ */
+char* audit_get_timestamp(TALLOC_CTX *frame)
+{
+ char buffer[40]; /* formatted time less usec and timezone */
+ char tz[10]; /* formatted time zone */
+ struct tm* tm_info; /* current local time */
+ struct timeval tv; /* current system time */
+ int r; /* response code from gettimeofday */
+ char * ts; /* formatted time stamp */
+
+ r = gettimeofday(&tv, NULL);
+ if (r) {
+ DBG_ERR("Unable to get time of day: (%d) %s\n",
+ errno,
+ strerror(errno));
+ return NULL;
+ }
+
+ tm_info = localtime(&tv.tv_sec);
+ if (tm_info == NULL) {
+ DBG_ERR("Unable to determine local time\n");
+ return NULL;
+ }
+
+ strftime(buffer, sizeof(buffer)-1, "%a, %d %b %Y %H:%M:%S", tm_info);
+ strftime(tz, sizeof(tz)-1, "%Z", tm_info);
+ ts = talloc_asprintf(frame, "%s.%06ld %s", buffer, tv.tv_usec, tz);
+ if (ts == NULL) {
+ DBG_ERR("Out of memory formatting time stamp\n");
+ }
+ return ts;
+}
+
+/*
+ * @brief write an audit message to the audit logs.
+ *
+ * Write the audit message to the samba logs.
+ *
+ * @param prefix Text to be printed at the start of the log line
+ * @param message The content of the log line.
+ * @param debub_class The debug class to log the message with.
+ * @param debug_level The debug level to log the message with.
+ */
+void audit_log_hr(
+ const char* prefix,
+ const char* message,
+ int debug_class,
+ int debug_level)
+{
+ DEBUGC(debug_class, debug_level, ("%s %s\n", prefix, message));
+}
+
+#ifdef HAVE_JANSSON
+/*
+ * @brief write a json object to the samba audit logs.
+ *
+ * Write the json object to the audit logs as a formatted stringjson object to the audit logs as a formatted string
+ *
+ * @param prefix Text to be printed at the start of the log line
+ * @param message The content of the log line.
+ * @param debub_class The debug class to log the message with.
+ * @param debug_level The debug level to log the message with.
+ */
+void audit_log_json(
+ const char* prefix,
+ struct json_object* message,
+ int debug_class,
+ int debug_level)
+{
+ TALLOC_CTX *ctx = talloc_new(NULL);
+ char *s = json_to_string(ctx, message);
+ DEBUGC(debug_class, debug_level, ("JSON %s: %s\n", prefix, s));
+ TALLOC_FREE(ctx);
+}
+
+/*
+ * @brief get a connection to the messaging event server.
+ *
+ * Get a connection to the messaging event server registered by server_name.
+ *
+ * @param msg_ctx a valid imessaging_context.
+ * @param server_name name of messaging event server to connect to.
+ * @param server_id The event server details to populate
+ *
+ * @return NTSTATUS
+ */
+static NTSTATUS get_event_server(
+ struct imessaging_context *msg_ctx,
+ const char *server_name,
+ struct server_id *event_server)
+{
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ unsigned num_servers, i;
+ struct server_id *servers;
+
+ status = irpc_servers_byname(
+ msg_ctx,
+ frame,
+ server_name,
+ &num_servers,
+ &servers);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_NOTICE(
+ "Failed to find '%s' registered on the message bus to "
+ "send audit events to: %s\n",
+ server_name,
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return status;
+ }
+
+ /*
+ * Select the first server that is listening, because we get
+ * connection refused as NT_STATUS_OBJECT_NAME_NOT_FOUND
+ * without waiting
+ */
+ for (i = 0; i < num_servers; i++) {
+ status = imessaging_send(
+ msg_ctx,
+ servers[i],
+ MSG_PING,
+ &data_blob_null);
+ if (NT_STATUS_IS_OK(status)) {
+ *event_server = servers[i];
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+ }
+ DBG_NOTICE(
+ "Failed to find '%s' registered on the message bus to "
+ "send audit events to: %s\n",
+ server_name,
+ nt_errstr(status));
+ TALLOC_FREE(frame);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/*
+ * @brief send an audit message to a messaging event server.
+ *
+ * Send the message to a registered and listening event server.
+ * Note: Any errors are logged, and the message is not sent. This is to ensure
+ * that a poorly behaved event server does not impact Samba.
+ *
+ * As it is possible to lose messages, especially during server
+ * shut down, currently this function is primarily intended for use
+ * in integration tests.
+ *
+ * @param msg_ctx an imessaging_context, can be NULL in which case no message
+ * will be sent.
+ * @param server_name the naname of the event server to send the message to.
+ * @param messag_type A message type defined in librpc/idl/messaging.idl
+ * @param message The message to send.
+ *
+ */
+void audit_message_send(
+ struct imessaging_context *msg_ctx,
+ const char *server_name,
+ uint32_t message_type,
+ struct json_object *message)
+{
+ struct server_id event_server;
+ NTSTATUS status;
+
+ const char *message_string = NULL;
+ DATA_BLOB message_blob = data_blob_null;
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ if (msg_ctx == NULL) {
+ DBG_DEBUG("No messaging context\n");
+ return;
+ }
+
+ /* Need to refetch the address each time as the destination server may
+ * have disconnected and reconnected in the interim, in which case
+ * messages may get lost
+ */
+ status = get_event_server(msg_ctx, server_name, &event_server);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ DBG_ERR("get_event_server for %s returned (%s)\n",
+ server_name,
+ nt_errstr(status));
+ return;
+ }
+
+ message_string = json_to_string(ctx, message);
+ message_blob = data_blob_string_const(message_string);
+ status = imessaging_send(
+ msg_ctx,
+ event_server,
+ message_type,
+ &message_blob);
+
+ /*
+ * If the server crashed, try to find it again
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ status = get_event_server(msg_ctx, server_name, &event_server);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ DBG_ERR("get_event_server for %s returned (%s)\n",
+ server_name,
+ nt_errstr(status));
+ TALLOC_FREE(ctx);
+ return;
+ }
+ imessaging_send(
+ msg_ctx,
+ event_server,
+ message_type,
+ &message_blob);
+ }
+ TALLOC_FREE(ctx);
+}
+
+/*
+ * @brief Create a new struct json_object, wrapping a JSON Object.
+ *
+ * Create a new json object, the json_object wraps the underlying json
+ * implementations JSON Object representation.
+ *
+ * Free with a call to json_free_object, note that the jansson inplementation
+ * allocates memory with malloc and not talloc.
+ *
+ * @return a struct json_object, error will be set to true if the object
+ * could not be created.
+ *
+ */
+struct json_object json_new_object(void) {
+
+ struct json_object object;
+ object.error = false;
+
+ object.root = json_object();
+ if (object.root == NULL) {
+ object.error = true;
+ DBG_ERR("Unable to create json_object\n");
+ }
+ return object;
+}
+
+/*
+ * @brief Create a new struct json_object wrapping a JSON Array.
+ *
+ * Create a new json object, the json_object wraps the underlying json
+ * implementations JSON Array representation.
+ *
+ * Free with a call to json_free_object, note that the jansson inplementation
+ * allocates memory with malloc and not talloc.
+ *
+ * @return a struct json_object, error will be set to true if the array
+ * could not be created.
+ *
+ */
+struct json_object json_new_array(void) {
+
+ struct json_object array;
+ array.error = false;
+
+ array.root = json_array();
+ if (array.root == NULL) {
+ array.error = true;
+ DBG_ERR("Unable to create json_array\n");
+ }
+ return array;
+}
+
+
+/*
+ * @brief free and invalidate a previously created JSON object.
+ *
+ * Release any resources owned by a json_object, and then mark the structure
+ * as invalid. It is safe to call this multiple times on an object.
+ *
+ */
+void json_free(struct json_object *object)
+{
+ if (object->root != NULL) {
+ json_decref(object->root);
+ }
+ object->root = NULL;
+ object->error = true;
+}
+
+/*
+ * @brief is the current JSON object invalid?
+ *
+ * Check the state of the object to determine if it is invalid.
+ *
+ * @return is the object valid?
+ *
+ */
+bool json_is_invalid(struct json_object *object)
+{
+ return object->error;
+}
+
+/*
+ * @brief Add an integer value to a JSON object.
+ *
+ * Add an integer value named 'name' to the json object.
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name of the value.
+ * @param value the value.
+ *
+ */
+void json_add_int(struct json_object *object,
+ const char* name,
+ const int value)
+{
+ int rc = 0;
+
+ if (object->error) {
+ return;
+ }
+
+ rc = json_object_set_new(object->root, name, json_integer(value));
+ if (rc) {
+ DBG_ERR("Unable to set name [%s] value [%d]\n", name, value);
+ object->error = true;
+ }
+}
+
+/*
+ * @brief Add a boolean value to a JSON object.
+ *
+ * Add a boolean value named 'name' to the json object.
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param value the value.
+ *
+ */
+void json_add_bool(struct json_object *object,
+ const char* name,
+ const bool value)
+{
+ int rc = 0;
+
+ if (object->error) {
+ return;
+ }
+
+ rc = json_object_set_new(object->root, name, json_boolean(value));
+ if (rc) {
+ DBG_ERR("Unable to set name [%s] value [%d]\n", name, value);
+ object->error = true;
+ }
+
+}
+
+/*
+ * @brief Add a string value to a JSON object.
+ *
+ * Add a string value named 'name' to the json object.
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param value the value.
+ *
+ */
+void json_add_string(struct json_object *object,
+ const char* name,
+ const char* value)
+{
+ int rc = 0;
+
+ if (object->error) {
+ return;
+ }
+
+ if (value) {
+ rc = json_object_set_new(
+ object->root,
+ name,
+ json_string(value));
+ } else {
+ rc = json_object_set_new(object->root, name, json_null());
+ }
+ if (rc) {
+ DBG_ERR("Unable to set name [%s] value [%s]\n", name, value);
+ object->error = true;
+ }
+}
+
+/*
+ * @brief Assert that the current JSON object is an array.
+ *
+ * Check that the current object is a JSON array, and if not
+ * invalidate the object. We also log an error message as this indicates
+ * bug in the calling code.
+ *
+ * @param object the JSON object to be validated.
+ */
+void json_assert_is_array(struct json_object *array) {
+
+ if (array->error) {
+ return;
+ }
+
+ if (json_is_array(array->root) == false) {
+ DBG_ERR("JSON object is not an array\n");
+ array->error = true;
+ return;
+ }
+}
+
+/*
+ * @brief Add a JSON object to a JSON object.
+ *
+ * Add a JSON object named 'name' to the json object.
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param value the value.
+ *
+ */
+void json_add_object(struct json_object *object,
+ const char* name,
+ struct json_object *value)
+{
+ int rc = 0;
+ json_t *jv = NULL;
+
+ if (object->error) {
+ return;
+ }
+
+ if (value != NULL && value->error) {
+ object->error = true;
+ return;
+ }
+
+ jv = value == NULL ? json_null() : value->root;
+
+ if (json_is_array(object->root)) {
+ rc = json_array_append_new(object->root, jv);
+ } else if (json_is_object(object->root)) {
+ rc = json_object_set_new(object->root, name, jv);
+ } else {
+ DBG_ERR("Invalid JSON object type\n");
+ object->error = true;
+ }
+ if (rc) {
+ DBG_ERR("Unable to add object [%s]\n", name);
+ object->error = true;
+ }
+}
+
+/*
+ * @brief Add a string to a JSON object, truncating if necessary.
+ *
+ *
+ * Add a string value named 'name' to the json object, the string will be
+ * truncated if it is more than len characters long. If len is 0 the value
+ * is encoded as a JSON null.
+ *
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param value the value.
+ * @param len the maximum number of characters to be copied.
+ *
+ */
+void json_add_stringn(
+ struct json_object *object,
+ const char *name,
+ const char *value,
+ const size_t len)
+{
+
+ int rc = 0;
+ if (object->error) {
+ return;
+ }
+
+ if (value != NULL && len > 0) {
+ char buffer[len+1];
+ strncpy(buffer, value, len);
+ buffer[len] = '\0';
+ rc = json_object_set_new(object->root,
+ name,
+ json_string(buffer));
+ } else {
+ rc = json_object_set_new(object->root, name, json_null());
+ }
+ if (rc) {
+ DBG_ERR("Unable to set name [%s] value [%s]\n", name, value);
+ object->error = true;
+ }
+}
+
+/*
+ * @brief Add a version object to a JSON object
+ *
+ * Add a version object to the JSON object
+ * "version":{"major":1, "minor":0}
+ *
+ * The version tag is intended to aid the processing of the JSON messages
+ * The major version number should change when an attribute is:
+ * - renamed
+ * - removed
+ * - its meaning changes
+ * - its contents change format
+ * The minor version should change whenever a new attribute is added and for
+ * minor bug fixes to an attributes content.
+ *
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ * @param major the major version number
+ * @param minor the minor version number
+ */
+void json_add_version(struct json_object *object, int major, int minor)
+{
+ struct json_object version = json_new_object();
+ json_add_int(&version, "major", major);
+ json_add_int(&version, "minor", minor);
+ json_add_object(object, "version", &version);
+}
+
+/*
+ * @brief add an ISO 8601 timestamp to the object.
+ *
+ * Add the current date and time as a timestamp in ISO 8601 format
+ * to a JSON object
+ *
+ * "timestamp":"2017-03-06T17:18:04.455081+1300"
+ *
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ */
+void json_add_timestamp(struct json_object *object)
+{
+ char buffer[40]; /* formatted time less usec and timezone */
+ char timestamp[50]; /* the formatted ISO 8601 time stamp */
+ char tz[10]; /* formatted time zone */
+ struct tm* tm_info; /* current local time */
+ struct timeval tv; /* current system time */
+ int r; /* response code from gettimeofday */
+
+ if (object->error) {
+ return;
+ }
+
+ r = gettimeofday(&tv, NULL);
+ if (r) {
+ DBG_ERR("Unable to get time of day: (%d) %s\n",
+ errno,
+ strerror(errno));
+ object->error = true;
+ return;
+ }
+
+ tm_info = localtime(&tv.tv_sec);
+ if (tm_info == NULL) {
+ DBG_ERR("Unable to determine local time\n");
+ object->error = true;
+ return;
+ }
+
+ strftime(buffer, sizeof(buffer)-1, "%Y-%m-%dT%T", tm_info);
+ strftime(tz, sizeof(tz)-1, "%z", tm_info);
+ snprintf(
+ timestamp,
+ sizeof(timestamp),
+ "%s.%06ld%s",
+ buffer,
+ tv.tv_usec,
+ tz);
+ json_add_string(object, "timestamp", timestamp);
+}
+
+
+/*
+ *@brief Add a tsocket_address to a JSON object
+ *
+ * Add the string representation of a Samba tsocket_address to the object.
+ *
+ * "localAddress":"ipv6::::0"
+ *
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param address the tsocket_address.
+ *
+ */
+void json_add_address(
+ struct json_object *object,
+ const char *name,
+ const struct tsocket_address *address)
+{
+
+ if (object->error) {
+ return;
+ }
+ if (address == NULL) {
+ int rc = json_object_set_new(object->root, name, json_null());
+ if (rc) {
+ DBG_ERR("Unable to set address [%s] to null\n", name);
+ object->error = true;
+ }
+ } else {
+ TALLOC_CTX *ctx = talloc_new(NULL);
+ char *s = NULL;
+
+ s = tsocket_address_string(address, ctx);
+ json_add_string(object, name, s);
+ TALLOC_FREE(ctx);
+ }
+}
+
+/*
+ * @brief Add a formatted string representation of a sid to a json object.
+ *
+ * Add the string representation of a Samba sid to the object.
+ *
+ * "sid":"S-1-5-18"
+ *
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param sid the sid
+ *
+ */
+void json_add_sid(
+ struct json_object *object,
+ const char *name,
+ const struct dom_sid *sid)
+{
+
+ if (object->error) {
+ return;
+ }
+ if (sid == NULL) {
+ int rc = json_object_set_new(object->root, name, json_null());
+ if (rc) {
+ DBG_ERR("Unable to set SID [%s] to null\n", name);
+ object->error = true;
+ }
+ } else {
+ char sid_buf[DOM_SID_STR_BUFLEN];
+
+ dom_sid_string_buf(sid, sid_buf, sizeof(sid_buf));
+ json_add_string(object, name, sid_buf);
+ }
+}
+
+/*
+ * @brief Add a formatted string representation of a guid to a json object.
+ *
+ * Add the string representation of a Samba GUID to the object.
+ *
+ * "guid":"1fb9f2ee-2a4d-4bf8-af8b-cb9d4529a9ab"
+ *
+ * In the event of an error object will be invalidated.
+ *
+ * @param object the JSON object to be updated.
+ * @param name the name.
+ * @param guid the guid.
+ *
+ *
+ */
+void json_add_guid(
+ struct json_object *object,
+ const char *name,
+ const struct GUID *guid)
+{
+
+
+ if (object->error) {
+ return;
+ }
+ if (guid == NULL) {
+ int rc = json_object_set_new(object->root, name, json_null());
+ if (rc) {
+ DBG_ERR("Unable to set GUID [%s] to null\n", name);
+ object->error = true;
+ }
+ } else {
+ char *guid_str;
+ struct GUID_txt_buf guid_buff;
+
+ guid_str = GUID_buf_string(guid, &guid_buff);
+ json_add_string(object, name, guid_str);
+ }
+}
+
+
+/*
+ * @brief Convert a JSON object into a string
+ *
+ * Convert the jsom object into a string suitable for printing on a log line,
+ * i.e. with no embedded line breaks.
+ *
+ * If the object is invalid it returns NULL.
+ *
+ * @param mem_ctx the talloc memory context owning the returned string
+ * @param object the json object.
+ *
+ * @return A string representation of the object or NULL if the object
+ * is invalid.
+ */
+char *json_to_string(TALLOC_CTX *mem_ctx, struct json_object *object)
+{
+ char *json = NULL;
+ char *json_string = NULL;
+
+ if (object->error) {
+ return NULL;
+ }
+
+ /*
+ * json_dumps uses malloc, so need to call free(json) to release
+ * the memory
+ */
+ json = json_dumps(object->root, 0);
+ if (json == NULL) {
+ DBG_ERR("Unable to convert JSON object to string\n");
+ return NULL;
+ }
+
+ json_string = talloc_strdup(mem_ctx, json);
+ if (json_string == NULL) {
+ free(json);
+ DBG_ERR("Unable to copy JSON object string to talloc string\n");
+ return NULL;
+ }
+ free(json);
+
+ return json_string;
+}
+#endif
diff --git a/lib/audit_logging/audit_logging.h b/lib/audit_logging/audit_logging.h
new file mode 100644
index 0000000..596ad6d
--- /dev/null
+++ b/lib/audit_logging/audit_logging.h
@@ -0,0 +1,98 @@
+/*
+ common routines for audit logging
+
+ Copyright (C) Andrew Bartlett <abartlet at samba.org> 2018
+
+ 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 <talloc.h>
+#include "lib/messaging/irpc.h"
+#include "lib/tsocket/tsocket.h"
+
+char *audit_get_timestamp(
+ TALLOC_CTX *frame);
+void audit_log_hr(
+ const char *prefix,
+ const char *message,
+ int debug_class,
+ int debug_level);
+#ifdef HAVE_JANSSON
+#include <jansson.h>
+/*
+ * Wrapper for jannson JSON object
+ *
+ */
+struct json_object {
+ json_t *root;
+ bool error;
+};
+
+void audit_log_json(
+ const char *prefix,
+ struct json_object *message,
+ int debug_class,
+ int debug_level);
+void audit_message_send(
+ struct imessaging_context *msg_ctx,
+ const char *server_name,
+ uint32_t message_type,
+ struct json_object *message);
+struct json_object json_new_object(void);
+struct json_object json_new_array(void);
+void json_free(struct json_object *object);
+void json_assert_is_array(struct json_object *array);
+bool json_is_invalid(struct json_object *object);
+
+void json_add_int(
+ struct json_object *object,
+ const char *name,
+ const int value);
+void json_add_bool(
+ struct json_object *object,
+ const char *name,
+ const bool value);
+void json_add_string(
+ struct json_object *object,
+ const char *name,
+ const char *value);
+void json_add_object(
+ struct json_object *object,
+ const char *name,
+ struct json_object *value);
+void json_add_stringn(
+ struct json_object *object,
+ const char *name,
+ const char *value,
+ const size_t len);
+void json_add_version(
+ struct json_object *object,
+ int major,
+ int minor);
+void json_add_timestamp(struct json_object *object);
+void json_add_address(
+ struct json_object *object,
+ const char *name,
+ const struct tsocket_address *address);
+void json_add_sid(
+ struct json_object *object,
+ const char *name,
+ const struct dom_sid *sid);
+void json_add_guid(
+ struct json_object *object,
+ const char *name,
+ const struct GUID *guid);
+
+char *json_to_string(TALLOC_CTX *mem_ctx, struct json_object *object);
+#endif
diff --git a/lib/audit_logging/tests/audit_logging_test.c b/lib/audit_logging/tests/audit_logging_test.c
new file mode 100644
index 0000000..7481df5
--- /dev/null
+++ b/lib/audit_logging/tests/audit_logging_test.c
@@ -0,0 +1,557 @@
+/*
+ * Unit tests for the audit_logging library.
+ *
+ * Copyright (C) Andrew Bartlett <abartlet at samba.org> 2018
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * from cmocka.c:
+ * These headers or their equivalents should be included prior to
+ * including
+ * this header file.
+ *
+ * #include <stdarg.h>
+ * #include <stddef.h>
+ * #include <setjmp.h>
+ *
+ * This allows test applications to use custom definitions of C standard
+ * library functions and types.
+ *
+ */
+
+/*
+ * Note that the messaging routines (audit_message_send and get_event_server)
+ * are not tested by these unit tests. Currently they are for integration
+ * test support, and as such are exercised by the integration tests.
+ */
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <string.h>
+#include <time.h>
+#include <tevent.h>
+#include <config.h>
+#include <talloc.h>
+#include "lib/util/talloc_stack.h"
+
+#include "lib/util/data_blob.h"
+#include "lib/util/time.h"
+#include "libcli/util/werror.h"
+#include "lib/param/loadparm.h"
+#include "libcli/security/dom_sid.h"
+#include "librpc/ndr/libndr.h"
+
+#include "lib/audit_logging/audit_logging.h"
+
+#ifdef HAVE_JANSSON
+static void test_json_add_int(void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ double n;
+
+ object = json_new_object();
+ json_add_int(&object, "positive_one", 1);
+ json_add_int(&object, "zero", 0);
+ json_add_int(&object, "negative_one", -1);
+
+
+ assert_int_equal(3, json_object_size(object.root));
+
+ value = json_object_get(object.root, "positive_one");
+ assert_true(json_is_integer(value));
+ n = json_number_value(value);
+ assert_true(n == 1.0);
+
+ value = json_object_get(object.root, "zero");
+ assert_true(json_is_integer(value));
+ n = json_number_value(value);
+ assert_true(n == 0.0);
+
+ value = json_object_get(object.root, "negative_one");
+ assert_true(json_is_integer(value));
+ n = json_number_value(value);
+ assert_true(n == -1.0);
+
+ json_free(&object);
+}
+
+static void test_json_add_bool(void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+
+ object = json_new_object();
+ json_add_bool(&object, "true", true);
+ json_add_bool(&object, "false", false);
+
+
+ assert_int_equal(2, json_object_size(object.root));
+
+ value = json_object_get(object.root, "true");
+ assert_true(json_is_boolean(value));
+ assert_true(value == json_true());
+
+ value = json_object_get(object.root, "false");
+ assert_true(json_is_boolean(value));
+ assert_true(value == json_false());
+
+ json_free(&object);
+}
+
+static void test_json_add_string(void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ const char *s = NULL;
+
+ object = json_new_object();
+ json_add_string(&object, "null", NULL);
+ json_add_string(&object, "empty", "");
+ json_add_string(&object, "name", "value");
+
+
+
+ assert_int_equal(3, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "empty");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("", s);
+
+ value = json_object_get(object.root, "name");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("value", s);
+ json_free(&object);
+}
+
+static void test_json_add_object(void **state)
+{
+ struct json_object object;
+ struct json_object other;
+ struct json_t *value = NULL;
+
+ object = json_new_object();
+ other = json_new_object();
+ json_add_object(&object, "null", NULL);
+ json_add_object(&object, "other", &other);
+
+
+
+ assert_int_equal(2, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "other");
+ assert_true(json_is_object(value));
+ assert_ptr_equal(other.root, value);
+
+ json_free(&object);
+}
+
+static void test_json_add_to_array(void **state)
+{
+ struct json_object array;
+ struct json_object o1;
+ struct json_object o2;
+ struct json_object o3;
+ struct json_t *value = NULL;
+
+ array = json_new_array();
+ assert_true(json_is_array(array.root));
+
+ o1 = json_new_object();
+ o2 = json_new_object();
+ o3 = json_new_object();
+
+ json_add_object(&array, NULL, &o3);
+ json_add_object(&array, "", &o2);
+ json_add_object(&array, "will-be-ignored", &o1);
+ json_add_object(&array, NULL, NULL);
+
+ assert_int_equal(4, json_array_size(array.root));
+
+ value = json_array_get(array.root, 0);
+ assert_ptr_equal(o3.root, value);
+
+ value = json_array_get(array.root, 1);
+ assert_ptr_equal(o2.root, value);
+
+ value = json_array_get(array.root, 2);
+ assert_ptr_equal(o1.root, value);
+
+ value = json_array_get(array.root, 3);
+ assert_true(json_is_null(value));
+
+ json_free(&array);
+
+}
+
+static void test_json_add_timestamp(void **state)
+{
+ struct json_object object;
+ struct json_t *ts = NULL;
+ const char *t = NULL;
+ int rc;
+ int usec, tz;
+ char c[2];
+ struct tm tm;
+ time_t before;
+ time_t after;
+ time_t actual;
+
+
+ object = json_new_object();
+ before = time(NULL);
+ json_add_timestamp(&object);
+ after = time(NULL);
+
+ ts = json_object_get(object.root, "timestamp");
+ assert_true(json_is_string(ts));
+
+ /*
+ * Convert the returned ISO 8601 timestamp into a time_t
+ * Note for convenience we ignore the value of the microsecond
+ * part of the time stamp.
+ */
+ t = json_string_value(ts);
+ rc = sscanf(
+ t,
+ "%4d-%2d-%2dT%2d:%2d:%2d.%6d%1c%4d",
+ &tm.tm_year,
+ &tm.tm_mon,
+ &tm.tm_mday,
+ &tm.tm_hour,
+ &tm.tm_min,
+ &tm.tm_sec,
+ &usec,
+ c,
+ &tz);
+ assert_int_equal(9, rc);
+ tm.tm_year = tm.tm_year - 1900;
+ tm.tm_mon = tm.tm_mon - 1;
+ tm.tm_isdst = -1;
+ actual = mktime(&tm);
+
+ /*
+ * The timestamp should be before <= actual <= after
+ */
+ assert_true(difftime(actual, before) >= 0);
+ assert_true(difftime(after, actual) >= 0);
+
+ json_free(&object);
+}
+
+static void test_json_add_stringn(void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ const char *s = NULL;
+
+ object = json_new_object();
+ json_add_stringn(&object, "null", NULL, 10);
+ json_add_stringn(&object, "null-zero-len", NULL, 0);
+ json_add_stringn(&object, "empty", "", 1);
+ json_add_stringn(&object, "empty-zero-len", "", 0);
+ json_add_stringn(&object, "value-less-than-len", "123456", 7);
+ json_add_stringn(&object, "value-greater-than-len", "abcd", 3);
+ json_add_stringn(&object, "value-equal-len", "ZYX", 3);
+ json_add_stringn(&object, "value-len-is-zero", "this will be null", 0);
+
+
+ assert_int_equal(8, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "null-zero-len");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "empty");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("", s);
+
+ value = json_object_get(object.root, "empty-zero-len");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "value-greater-than-len");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("abc", s);
+ assert_int_equal(3, strlen(s));
+
+ value = json_object_get(object.root, "value-equal-len");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("ZYX", s);
+ assert_int_equal(3, strlen(s));
+
+ value = json_object_get(object.root, "value-len-is-zero");
+ assert_true(json_is_null(value));
+
+ json_free(&object);
+}
+
+static void test_json_add_version(void **state)
+{
+ struct json_object object;
+ struct json_t *version = NULL;
+ struct json_t *v = NULL;
+ double n;
+
+ object = json_new_object();
+ json_add_version(&object, 3, 1);
+
+ assert_int_equal(1, json_object_size(object.root));
+
+ version = json_object_get(object.root, "version");
+ assert_true(json_is_object(version));
+ assert_int_equal(2, json_object_size(version));
+
+ v = json_object_get(version, "major");
+ assert_true(json_is_integer(v));
+ n = json_number_value(v);
+ assert_true(n == 3.0);
+
+ v = json_object_get(version, "minor");
+ assert_true(json_is_integer(v));
+ n = json_number_value(v);
+ assert_true(n == 1.0);
+
+ json_free(&object);
+}
+
+static void test_json_add_address(void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ struct tsocket_address *ip4 = NULL;
+ struct tsocket_address *ip6 = NULL;
+ struct tsocket_address *pipe = NULL;
+ const char *s = NULL;
+ int rc;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ object = json_new_object();
+
+ json_add_address(&object, "null", NULL);
+
+ rc = tsocket_address_inet_from_strings(
+ ctx,
+ "ip",
+ "127.0.0.1",
+ 21,
+ &ip4);
+ assert_int_equal(0, rc);
+ json_add_address(&object, "ip4", ip4);
+
+ rc = tsocket_address_inet_from_strings(
+ ctx,
+ "ip",
+ "2001:db8:0:0:1:0:0:1",
+ 42,
+ &ip6);
+ assert_int_equal(0, rc);
+ json_add_address(&object, "ip6", ip6);
+
+ rc = tsocket_address_unix_from_path(ctx, "/samba/pipe", &pipe);
+ assert_int_equal(0, rc);
+ json_add_address(&object, "pipe", pipe);
+
+ assert_int_equal(4, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "ip4");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("ipv4:127.0.0.1:21", s);
+
+ value = json_object_get(object.root, "ip6");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("ipv6:2001:db8::1:0:0:1:42", s);
+
+ value = json_object_get(object.root, "pipe");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal("unix:/samba/pipe", s);
+
+ json_free(&object);
+ TALLOC_FREE(ctx);
+}
+
+static void test_json_add_sid(void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ const char *SID = "S-1-5-21-2470180966-3899876309-2637894779";
+ struct dom_sid sid;
+ const char *s = NULL;
+
+
+ object = json_new_object();
+
+ json_add_sid(&object, "null", NULL);
+
+ assert_true(string_to_sid(&sid, SID));
+ json_add_sid(&object, "sid", &sid);
+
+ assert_int_equal(2, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "sid");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal(SID, s);
+ json_free(&object);
+}
+
+static void test_json_add_guid(void **state)
+{
+ struct json_object object;
+ struct json_t *value = NULL;
+ const char *GUID = "3ab88633-1e57-4c1a-856c-d1bc4b15bbb1";
+ struct GUID guid;
+ const char *s = NULL;
+ NTSTATUS status;
+
+
+ object = json_new_object();
+
+ json_add_guid(&object, "null", NULL);
+
+ status = GUID_from_string(GUID, &guid);
+ assert_true(NT_STATUS_IS_OK(status));
+ json_add_guid(&object, "guid", &guid);
+
+ assert_int_equal(2, json_object_size(object.root));
+
+ value = json_object_get(object.root, "null");
+ assert_true(json_is_null(value));
+
+ value = json_object_get(object.root, "guid");
+ assert_true(json_is_string(value));
+ s = json_string_value(value);
+ assert_string_equal(GUID, s);
+
+ json_free(&object);
+}
+
+static void test_json_to_string(void **state)
+{
+ struct json_object object;
+ char *s = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ object = json_new_object();
+ object.error = true;
+
+ s = json_to_string(ctx, &object);
+ assert_null(s);
+
+ object.error = false;
+ s = json_to_string(ctx, &object);
+ assert_string_equal("{}", s);
+ TALLOC_FREE(s);
+
+ json_add_string(&object, "name", "value");
+ s = json_to_string(ctx, &object);
+ assert_string_equal("{\"name\": \"value\"}", s);
+ TALLOC_FREE(s);
+
+ json_free(&object);
+ TALLOC_FREE(ctx);
+}
+#endif
+
+static void test_audit_get_timestamp(void **state)
+{
+ const char *t = NULL;
+ char *c;
+ struct tm tm;
+ time_t before;
+ time_t after;
+ time_t actual;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ before = time(NULL);
+ t = audit_get_timestamp(ctx);
+ after = time(NULL);
+
+
+ c = strptime(t, "%a, %d %b %Y %H:%M:%S", &tm);
+ tm.tm_isdst = -1;
+ if (c != NULL && *c == '.') {
+ char *e;
+ strtod(c, &e);
+ c = e;
+ }
+ if (c != NULL && *c == ' ') {
+ struct tm tz;
+ c = strptime(c, " %Z", &tz);
+ }
+ assert_int_equal(0, strlen(c));
+
+ actual = mktime(&tm);
+
+ /*
+ * The timestamp should be before <= actual <= after
+ */
+ assert_true(difftime(actual, before) >= 0);
+ assert_true(difftime(after, actual) >= 0);
+
+ TALLOC_FREE(ctx);
+}
+
+int main(int argc, const char **argv)
+{
+ const struct CMUnitTest tests[] = {
+#ifdef HAVE_JANSSON
+ cmocka_unit_test(test_json_add_int),
+ cmocka_unit_test(test_json_add_bool),
+ cmocka_unit_test(test_json_add_string),
+ cmocka_unit_test(test_json_add_object),
+ cmocka_unit_test(test_json_add_to_array),
+ cmocka_unit_test(test_json_add_timestamp),
+ cmocka_unit_test(test_json_add_stringn),
+ cmocka_unit_test(test_json_add_version),
+ cmocka_unit_test(test_json_add_address),
+ cmocka_unit_test(test_json_add_sid),
+ cmocka_unit_test(test_json_add_guid),
+ cmocka_unit_test(test_json_to_string),
+#endif
+ cmocka_unit_test(test_audit_get_timestamp),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/lib/audit_logging/wscript_build b/lib/audit_logging/wscript_build
new file mode 100644
index 0000000..35bf79c
--- /dev/null
+++ b/lib/audit_logging/wscript_build
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM(
+ 'audit_logging',
+ deps='',
+ source='audit_logging.c'
+)
+
+bld.SAMBA_BINARY(
+ 'audit_logging_test',
+ source='tests/audit_logging_test.c',
+ deps='''
+ audit_logging
+ jansson
+ cmocka
+ talloc
+ samba-debug
+ samba-util
+ LIBTSOCKET
+ MESSAGING_SEND
+ ''',
+ install=False
+)
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index b1483aa..129901c 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -1052,3 +1052,5 @@ plantestsuite("samba4.dsdb.samdb.ldb_modules.unique_object_sids" , "none",
[os.path.join(bindir(), "test_unique_object_sids")])
plantestsuite("samba4.dsdb.samdb.ldb_modules.encrypted_secrets", "none",
[os.path.join(bindir(), "test_encrypted_secrets")])
+plantestsuite("lib.audit_logging.audit_logging", "none",
+ [os.path.join(bindir(), "audit_logging_test")])
diff --git a/wscript_build b/wscript_build
index 8a59bdf..5454876 100644
--- a/wscript_build
+++ b/wscript_build
@@ -46,6 +46,7 @@ bld.RECURSE('lib/texpect')
bld.RECURSE('lib/addns')
bld.RECURSE('lib/ldb')
bld.RECURSE('lib/param')
+bld.RECURSE('lib/audit_logging')
bld.RECURSE('dynconfig')
bld.RECURSE('lib/util/charset')
bld.RECURSE('python')
--
2.7.4
From 4daf72457d802586749d45f452581dd7f1fac655 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Wed, 4 Apr 2018 11:56:30 +1200
Subject: [PATCH 07/21] logging: add ldb audit classes
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
docs-xml/smbdotconf/logging/loglevel.xml | 28 +++++++++++++++++++++++++---
lib/util/debug.c | 6 ++++++
lib/util/debug.h | 6 ++++++
3 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/docs-xml/smbdotconf/logging/loglevel.xml b/docs-xml/smbdotconf/logging/loglevel.xml
index d3b5c45..3535b1b 100644
--- a/docs-xml/smbdotconf/logging/loglevel.xml
+++ b/docs-xml/smbdotconf/logging/loglevel.xml
@@ -46,6 +46,12 @@
<listitem><para><parameter moreinfo="none">auth_audit</parameter></para></listitem>
<listitem><para><parameter moreinfo="none">auth_json_audit</parameter></para></listitem>
<listitem><para><parameter moreinfo="none">kerberos</parameter></para></listitem>
+ <listitem><para><parameter moreinfo="none">samdb_audit</parameter></para></listitem>
+ <listitem><para><parameter moreinfo="none">samdb_json_audit</parameter></para></listitem>
+ <listitem><para><parameter moreinfo="none">password_audit</parameter></para></listitem>
+ <listitem><para><parameter moreinfo="none">password_json_audit</parameter></para></listitem>
+ <listitem><para><parameter moreinfo="none">transaction_audit</parameter></para></listitem>
+ <listitem><para><parameter moreinfo="none">transaction_json_audit</parameter></para></listitem>
</itemizedlist>
<para>Authentication and authorization audit information is logged
@@ -58,7 +64,7 @@
as well as the implicit authentication in password changes. In
the file server, NTLM authentication, SMB and RPC authorization is
covered.</para>
-
+
<para>Log levels for auth_audit and auth_audit_json are:</para>
<itemizedlist>
<listitem><para>2: Authentication Failure</para></listitem>
@@ -66,9 +72,25 @@
<listitem><para>4: Authorization Success</para></listitem>
<listitem><para>5: Anonymous Authentication and Authorization Success</para></listitem>
</itemizedlist>
-
-
+ <para>Changes to the samdb database are logged
+ under the samdb_audit, and if Samba is compiled against the jansson
+ JSON library, a JSON representation is logged under
+ samdb_json_audit.</para>
+
+ <para>Password changes and Password resets are logged under
+ the password_audit, and if Samba is compiled against the jansson
+ JSON library, a JSON representation is logged under the
+ password_json_audit.</para>
+
+ <para>Transaction rollbacks and prepare commit failures are logged under
+ the transaction_audit the password_audit, and if Samba is compiled against
+ the jansson JSON library, a JSON representation is logged under the
+ password_json_audit. Logging the transaction details allows the
+ identification of password and samdb operations that have been rolled
+ back.</para>
+
+
</description>
<value type="default">0</value>
<value type="example">3 passdb:5 auth:10 winbind:2</value>
diff --git a/lib/util/debug.c b/lib/util/debug.c
index d010b72..db4f71a 100644
--- a/lib/util/debug.c
+++ b/lib/util/debug.c
@@ -543,6 +543,12 @@ static const char *default_classname_table[] = {
[DBGC_DRS_REPL] = "drs_repl",
[DBGC_SMB2] = "smb2",
[DBGC_SMB2_CREDITS] = "smb2_credits",
+ [DBGC_SAMDB_AUDIT] = "samdb_audit",
+ [DBGC_SAMDB_AUDIT_JSON] = "samdb_json_audit",
+ [DBGC_PWD_AUDIT] = "password_audit",
+ [DBGC_PWD_AUDIT_JSON] = "password_json_audit",
+ [DBGC_TRN_AUDIT] = "transaction_audit",
+ [DBGC_TRN_AUDIT_JSON] = "transaction_json_audit",
};
/*
diff --git a/lib/util/debug.h b/lib/util/debug.h
index 1e184b4..4e425a0 100644
--- a/lib/util/debug.h
+++ b/lib/util/debug.h
@@ -95,6 +95,12 @@ bool dbghdr( int level, const char *location, const char *func);
#define DBGC_DRS_REPL 27
#define DBGC_SMB2 28
#define DBGC_SMB2_CREDITS 29
+#define DBGC_SAMDB_AUDIT 30
+#define DBGC_SAMDB_AUDIT_JSON 31
+#define DBGC_PWD_AUDIT 32
+#define DBGC_PWD_AUDIT_JSON 33
+#define DBGC_TRN_AUDIT 34
+#define DBGC_TRN_AUDIT_JSON 35
/* So you can define DBGC_CLASS before including debug.h */
#ifndef DBGC_CLASS
--
2.7.4
From 1df2279b609a02ea0cf5e98bb33f75c4d0e7be26 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Thu, 12 Apr 2018 10:19:16 +1200
Subject: [PATCH 08/21] smb conf: Add samdb event notification parameter
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
.../smbdotconf/misc/passwordeventnotification.xml | 27 ++++++++++++++++++++++
.../smbdotconf/misc/samdbeventnotification.xml | 27 ++++++++++++++++++++++
2 files changed, 54 insertions(+)
create mode 100644 docs-xml/smbdotconf/misc/passwordeventnotification.xml
create mode 100644 docs-xml/smbdotconf/misc/samdbeventnotification.xml
diff --git a/docs-xml/smbdotconf/misc/passwordeventnotification.xml b/docs-xml/smbdotconf/misc/passwordeventnotification.xml
new file mode 100644
index 0000000..e83f05d
--- /dev/null
+++ b/docs-xml/smbdotconf/misc/passwordeventnotification.xml
@@ -0,0 +1,27 @@
+<samba:parameter name="password event notification"
+ context="G"
+ type="boolean"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>When enabled, this option causes Samba (acting as an
+ Active Directory Domain Controller) to stream password change
+ and reset events across the internal message bus.
+ Scripts built using Samba's python bindings can listen to these
+ events by registering as the service
+ <filename moreinfo="none">password_event</filename>.</para>
+
+ <para>This should be considered a developer option (it assists
+ in the Samba testsuite) rather than a facility for external
+ auditing, as message delivery is not guaranteed (a feature
+ that the testsuite works around). Additionally Samba must be
+ compiled with the jansson support for this option to be
+ effective.</para>
+
+ <para>The password events are also logged via the normal
+ logging methods when the <smbconfoption name="log level"/> is
+ set appropriately.</para>
+
+</description>
+
+<value type="default">no</value>
+</samba:parameter>
diff --git a/docs-xml/smbdotconf/misc/samdbeventnotification.xml b/docs-xml/smbdotconf/misc/samdbeventnotification.xml
new file mode 100644
index 0000000..a1d7aca
--- /dev/null
+++ b/docs-xml/smbdotconf/misc/samdbeventnotification.xml
@@ -0,0 +1,27 @@
+<samba:parameter name="samdb event notification"
+ context="G"
+ type="boolean"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>When enabled, this option causes Samba (acting as an
+ Active Directory Domain Controller) to stream Samba database
+ events across the internal message bus. Scripts built using
+ Samba's python bindings can listen to these events by
+ registering as the service
+ <filename moreinfo="none">samdb_event</filename>.</para>
+
+ <para>This should be considered a developer option (it assists
+ in the Samba testsuite) rather than a facility for external
+ auditing, as message delivery is not guaranteed (a feature
+ that the testsuite works around). Additionally Samba must be
+ compiled with the jansson support for this option to be
+ effective.</para>
+
+ <para>The Samba database events are also logged via the normal
+ logging methods when the <smbconfoption name="log level"/> is
+ set appropriately.</para>
+
+</description>
+
+<value type="default">no</value>
+</samba:parameter>
--
2.7.4
From 25b569e74a74ae6ec76981ee49e97a9ae0cbf89e Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Thu, 12 Apr 2018 13:19:16 +1200
Subject: [PATCH 09/21] idl messaging: Add Samdb and Password events and
message types
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
librpc/idl/messaging.idl | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/librpc/idl/messaging.idl b/librpc/idl/messaging.idl
index 14a6f92..73cceff 100644
--- a/librpc/idl/messaging.idl
+++ b/librpc/idl/messaging.idl
@@ -140,8 +140,11 @@ interface messaging
MSG_NTVFS_OPLOCK_BREAK = 0x0703,
MSG_DREPL_ALLOCATE_RID = 0x0704,
- /* Called during authentication and authorization to allow out-of- */
+ /*
+ * Audit, Authentication and Authorisation event messages */
MSG_AUTH_LOG = 0x0800,
+ MSG_SAMDB_LOG = 0x0801,
+ MSG_PWD_LOG = 0x0802,
/* dbwrap messages 4001-4999 (0x0FA0 - 0x1387) */
/* MSG_DBWRAP_TDB2_CHANGES = 4001, */
@@ -178,5 +181,7 @@ interface messaging
} messaging_reclog;
/* This allows this well known service name to be referenced in python and C */
- const string AUTH_EVENT_NAME = "auth_event";
+ const string AUTH_EVENT_NAME = "auth_event";
+ const string SAMDB_EVENT_NAME = "samdb_event";
+ const string PWD_EVENT_NAME = "password_event";
}
--
2.7.4
From 8dc72eb439c4b661f61fd6dfd9b727d315780522 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Wed, 4 Apr 2018 11:59:41 +1200
Subject: [PATCH 10/21] dsdb: audit samdb and password changes
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
source4/dsdb/samdb/ldb_modules/audit_log.c | 935 ++++++++++++++++++++
source4/dsdb/samdb/ldb_modules/audit_util.c | 470 ++++++++++
source4/dsdb/samdb/ldb_modules/samba_dsdb.c | 3 +-
.../dsdb/samdb/ldb_modules/tests/test_audit_log.c | 468 ++++++++++
.../dsdb/samdb/ldb_modules/tests/test_audit_util.c | 958 +++++++++++++++++++++
source4/dsdb/samdb/ldb_modules/wscript_build | 28 +-
.../dsdb/samdb/ldb_modules/wscript_build_server | 16 +
source4/selftest/tests.py | 4 +
8 files changed, 2879 insertions(+), 3 deletions(-)
create mode 100644 source4/dsdb/samdb/ldb_modules/audit_log.c
create mode 100644 source4/dsdb/samdb/ldb_modules/audit_util.c
create mode 100644 source4/dsdb/samdb/ldb_modules/tests/test_audit_log.c
create mode 100644 source4/dsdb/samdb/ldb_modules/tests/test_audit_util.c
diff --git a/source4/dsdb/samdb/ldb_modules/audit_log.c b/source4/dsdb/samdb/ldb_modules/audit_log.c
new file mode 100644
index 0000000..67182d2
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/audit_log.c
@@ -0,0 +1,935 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett <abartlet at samba.org> 2018
+
+ 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/>.
+*/
+
+/*
+ * Provide an audit log of changes made to the database and at a higher level
+ * details of any password changes and resets.
+ *
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "lib/audit_logging/audit_logging.h"
+
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/samdb/ldb_modules/util.h"
+#include "libcli/security/dom_sid.h"
+#include "auth/common_auth.h"
+#include "param/param.h"
+
+#define OPERATION_JSON_TYPE "samdbChange"
+#define OPERATION_HR_TAG "Samdb Change"
+#define OPERATION_MAJOR 1
+#define OPERATION_MINOR 0
+#define OPERATION_LOG_LVL 5
+
+#define PASSWORD_JSON_TYPE "passwordChange"
+#define PASSWORD_HR_TAG "Password Change"
+#define PASSWORD_MAJOR 1
+#define PASSWORD_MINOR 0
+#define PASSWORD_LOG_LVL 5
+
+#define TRANSACTION_JSON_TYPE "samdbTransaction"
+#define TRANSACTION_HR_TAG "Samdb Transaction"
+#define TRANSACTION_MAJOR 1
+#define TRANSACTION_MINOR 0
+/*
+ * Currently we only log roll backs and prepare commit failures
+ */
+#define TRANSACTION_LOG_LVL 5
+
+#define MAX_LENGTH 1024
+
+#define min(a, b) (((a)>(b))?(b):(a))
+
+static const char * const secret_attributes[] = {DSDB_SECRET_ATTRIBUTES, NULL};
+static const char * const password_attributes[] = {
+ DSDB_PASSWORD_ATTRIBUTES,
+ NULL};
+
+struct audit_context {
+ bool send_samdb_events;
+ bool send_password_events;
+ struct imessaging_context *msg_ctx;
+ struct GUID transaction_guid;
+};
+
+/*
+ * @brief Has the password changed.
+ *
+ * Does the message contain a change to one of the password attributes? The
+ * password attributes are defined in DSDB_PASSWORD_ATTRIBUTES
+ *
+ * @return true if the message contains a password attribute
+ *
+ */
+static bool has_password_changed(const struct ldb_message *message)
+{
+ int i;
+ if (message == NULL) {
+ return false;
+ }
+ for (i=0;i<message->num_elements;i++) {
+ if (ldb_attr_in_list(
+ password_attributes,
+ message->elements[i].name)) {
+
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * @brief Is the request a password "Change" or a "Reset"
+ *
+ * Get a description of the action being performed on the user password. This
+ * routine assumes that the request contains password attributes and that the
+ * password ACL checks have been performed by acl.c
+ *
+ * @param req the ldb_request to inspect
+ *
+ * @return "Change" if the password is being changed.
+ * "Reset" if the password is being reset.
+ */
+static const char *get_password_action(const struct ldb_request *request)
+{
+ if(request->operation == LDB_ADD) {
+ return "Reset";
+ } else {
+ struct ldb_control *pav_ctrl = NULL;
+ struct dsdb_control_password_acl_validation *pav = NULL;
+
+ pav_ctrl = ldb_request_get_control(
+ discard_const(request),
+ DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID);
+ if (pav_ctrl == NULL) {
+ return "Change";
+ }
+
+ pav = talloc_get_type_abort(
+ pav_ctrl->data,
+ struct dsdb_control_password_acl_validation);
+
+ if (pav->pwd_reset) {
+ return "Reset";
+ } else {
+ return "Change";
+ }
+ }
+}
+
+
+#ifdef HAVE_JANSSON
+/*
+ * @brief generate a JSON object as a
+ */
+static struct json_object operation_json(
+ struct ldb_module *module,
+ const struct ldb_request *request,
+ const int ret)
+{
+ struct ldb_context *ldb = NULL;
+ const struct dom_sid *sid = NULL;
+ struct json_object wrapper;
+ struct json_object audit;
+ const struct tsocket_address *remote = NULL;
+ const char *dn = NULL;
+ const char* operation = NULL;
+ const struct GUID *unique_session_token = NULL;
+ const struct ldb_message *message = NULL;
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+
+ ldb = ldb_module_get_ctx(module);
+
+ remote = get_remote_address(ldb);
+ sid = get_user_sid(module);
+ dn = get_primary_dn(request);
+ operation = get_operation_name(request);
+ unique_session_token = get_unique_session_token(module);
+
+ audit = json_new_object();
+ json_add_version(&audit, OPERATION_MAJOR, OPERATION_MINOR);
+ json_add_int(&audit, "statusCode", ret);
+ json_add_string(&audit, "status", ldb_strerror(ret));
+ json_add_string(&audit, "operation", operation);
+ json_add_address(&audit, "remoteAddress", remote);
+ json_add_sid(&audit, "userSid", sid);
+ json_add_string(&audit, "dn", dn);
+ json_add_guid(&audit, "transactionId", &ac->transaction_guid);
+ json_add_guid(&audit, "sessionId", unique_session_token);
+
+ message = get_message(request);
+ if (message != NULL) {
+ struct json_object attributes
+ = attributes_json(request->operation, message);
+ json_add_object(&audit, "attributes", &attributes);
+ }
+
+ wrapper = json_new_object();
+ json_add_timestamp(&wrapper);
+ json_add_string(&wrapper, "type", OPERATION_JSON_TYPE);
+ json_add_object(&wrapper, OPERATION_JSON_TYPE, &audit);
+ return wrapper;
+}
+
+static struct json_object password_change_json(
+ struct ldb_module *module,
+ const struct ldb_request *request,
+ const int ret)
+{
+ struct ldb_context *ldb = NULL;
+ const struct dom_sid *sid = NULL;
+ const char* dn = NULL;
+ struct json_object wrapper;
+ struct json_object audit;
+ const struct tsocket_address *remote = NULL;
+ const char* action = NULL;
+ const struct GUID *unique_session_token = NULL;
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+
+
+ ldb = ldb_module_get_ctx(module);
+
+ remote = get_remote_address(ldb);
+ sid = get_user_sid(module);
+ dn = get_primary_dn(request);
+ action = get_password_action(request);
+ unique_session_token = get_unique_session_token(module);
+
+ audit = json_new_object();
+ json_add_version(&audit, PASSWORD_MAJOR, PASSWORD_MINOR);
+ json_add_int(&audit, "statusCode", ret);
+ json_add_string(&audit, "status", ldb_strerror(ret));
+ json_add_address(&audit, "remoteAddress", remote);
+ json_add_sid(&audit, "userSid", sid);
+ json_add_string(&audit, "dn", dn);
+ json_add_string(&audit, "action", action);
+ json_add_guid(&audit, "transactionId", &ac->transaction_guid);
+ json_add_guid(&audit, "sessionId", unique_session_token);
+
+ wrapper = json_new_object();
+ json_add_timestamp(&wrapper);
+ json_add_string(&wrapper, "type", PASSWORD_JSON_TYPE);
+ json_add_object(&wrapper, PASSWORD_JSON_TYPE, &audit);
+
+ return wrapper;
+}
+
+static struct json_object transaction_json(
+ const char *action,
+ struct GUID *transaction_id)
+{
+ struct json_object wrapper;
+ struct json_object audit;
+
+ audit = json_new_object();
+ json_add_version(&audit, TRANSACTION_MAJOR, TRANSACTION_MINOR);
+ json_add_string(&audit, "action", action);
+ json_add_guid(&audit, "transactionId", transaction_id);
+
+ wrapper = json_new_object();
+ json_add_timestamp(&wrapper);
+ json_add_string(&wrapper, "type", TRANSACTION_JSON_TYPE);
+ json_add_object(&wrapper, TRANSACTION_JSON_TYPE, &audit);
+
+ return wrapper;
+}
+
+
+static struct json_object prepare_commit_failure_json(
+ int status,
+ const char *reason,
+ struct GUID *transaction_id)
+{
+ struct json_object wrapper;
+ struct json_object audit;
+
+ audit = json_new_object();
+ json_add_version(&audit, TRANSACTION_MAJOR, TRANSACTION_MINOR);
+ json_add_string(&audit, "action", "prepare");
+ json_add_guid(&audit, "transactionId", transaction_id);
+ json_add_int(&audit, "statusCode", status);
+ json_add_string(&audit, "status", ldb_strerror(status));
+ json_add_string(&audit, "reason", reason);
+
+ wrapper = json_new_object();
+ json_add_timestamp(&wrapper);
+ json_add_string(&wrapper, "type", TRANSACTION_JSON_TYPE);
+ json_add_object(&wrapper, TRANSACTION_JSON_TYPE, &audit);
+
+ return wrapper;
+}
+
+#endif
+
+static char *password_change_human_readable(
+ TALLOC_CTX *mem_ctx,
+ struct ldb_module *module,
+ const struct ldb_request *request,
+ int ret)
+{
+ struct ldb_context *ldb = NULL;
+ const char *remote_host = NULL;
+ const struct dom_sid *sid = NULL;
+ const char *user_sid = NULL;
+ const char *timestamp = NULL;
+ char *log_entry = NULL;
+ const char *action = NULL;
+ const char *dn = NULL;
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ ldb = ldb_module_get_ctx(module);
+
+ remote_host = get_remote_host(ldb, frame);
+ sid = get_user_sid(module);
+ user_sid = dom_sid_string(frame, sid);
+ timestamp = audit_get_timestamp(frame);
+ action = get_password_action(request);
+ dn = get_primary_dn(request);
+
+ log_entry = talloc_asprintf(
+ frame,
+ "[%s] at [%s] status [%s] "
+ "remote host [%s] SID [%s] DN [%s]",
+ action,
+ timestamp,
+ ldb_strerror(ret),
+ remote_host,
+ user_sid,
+ dn);
+ TALLOC_FREE(frame);
+ return log_entry;
+}
+
+static char *log_attributes(struct ldb_context *ldb,
+ char *buffer,
+ enum ldb_request_type operation,
+ const struct ldb_message *message)
+{
+ int i, j;
+ for (i=0;i<message->num_elements;i++) {
+ if (i > 0) {
+ buffer = talloc_asprintf_append_buffer(buffer, " ");
+ }
+
+ if (message->elements[i].name == NULL) {
+ ldb_debug(ldb,
+ LDB_DEBUG_ERROR,
+ "Error: Invalid element name (NULL) at "
+ "position %d", i);
+ return NULL;
+ }
+
+ if (operation == LDB_MODIFY) {
+ const char *action =NULL;
+ action = get_modification_action(
+ message->elements[i].flags);
+ buffer = talloc_asprintf_append_buffer(
+ buffer,
+ "%s: %s ",
+ action,
+ message->elements[i].name);
+ } else {
+ buffer = talloc_asprintf_append_buffer(
+ buffer,
+ "%s ",
+ message->elements[i].name);
+ }
+
+ if (ldb_attr_in_list(secret_attributes, message->elements[i].name)) {
+ /* Do not log the value of any secret attributes */
+ buffer = talloc_asprintf_append_buffer(
+ buffer,
+ "[REDACTED SECRET ATTRIBUTE]");
+ continue;
+ }
+
+ for (j=0;j<message->elements[i].num_values;j++) {
+ struct ldb_val v;
+ bool use_b64_encode = false;
+ int length;
+ if (j > 0) {
+ buffer = talloc_asprintf_append_buffer(buffer, " ");
+ }
+
+ v = message->elements[i].values[j];
+ length = min(MAX_LENGTH, v.length);
+ use_b64_encode = ldb_should_b64_encode(ldb, &v);
+ if (use_b64_encode) {
+ const char *encoded = ldb_base64_encode(
+ buffer,
+ (char *)v.data,
+ length);
+ buffer = talloc_asprintf_append_buffer(
+ buffer,
+ "{%s%s}",
+ encoded,
+ (v.length > MAX_LENGTH ? "..." : ""));
+ } else {
+ buffer = talloc_asprintf_append_buffer(
+ buffer,
+ "[%*.*s%s]",
+ length,
+ length,
+ (char *)v.data,
+ (v.length > MAX_LENGTH ? "..." : ""));
+ }
+ }
+ }
+ return buffer;
+}
+
+static char *operation_human_readable(
+ TALLOC_CTX *mem_ctx,
+ struct ldb_module *module,
+ const struct ldb_request *request,
+ const int ret)
+{
+ struct ldb_context *ldb = NULL;
+ const char *remote_host = NULL;
+ const struct dom_sid *sid = NULL;
+ const char *user_sid = NULL;
+ const char *timestamp = NULL;
+ const char *op_name = NULL;
+ char *log_entry = NULL;
+ const char *dn = NULL;
+ const char *new_dn = NULL;
+ const struct ldb_message *message = NULL;
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ ldb = ldb_module_get_ctx(module);
+
+ remote_host = get_remote_host(ldb, frame);
+ sid = get_user_sid(module);
+ user_sid = dom_sid_string(frame, sid);
+ timestamp = audit_get_timestamp(frame);
+ op_name = get_operation_name(request);
+ dn = get_primary_dn(request);
+ new_dn = get_secondary_dn(request);
+
+ message = get_message(request);
+
+ log_entry = talloc_asprintf(
+ mem_ctx,
+ "[%s] at [%s] status [%s] "
+ "remote host [%s] SID [%s] DN [%s]",
+ op_name,
+ timestamp,
+ ldb_strerror(ret),
+ remote_host,
+ user_sid,
+ dn);
+ if (new_dn != NULL) {
+ log_entry = talloc_asprintf_append_buffer(
+ log_entry,
+ " New DN [%s]",
+ new_dn);
+ }
+ if (message != NULL) {
+ log_entry = talloc_asprintf_append_buffer(log_entry,
+ " attributes [");
+ log_entry = log_attributes(ldb,
+ log_entry,
+ request->operation,
+ message);
+ log_entry = talloc_asprintf_append_buffer(log_entry, "]");
+ }
+ TALLOC_FREE(frame);
+ return log_entry;
+}
+
+static char *transaction_human_readable(
+ TALLOC_CTX *mem_ctx,
+ const char* action)
+{
+ const char *timestamp = NULL;
+ char *log_entry = NULL;
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ timestamp = audit_get_timestamp(frame);
+
+ log_entry = talloc_asprintf(
+ mem_ctx,
+ "[%s] at [%s]",
+ action,
+ timestamp);
+
+ TALLOC_FREE(frame);
+ return log_entry;
+}
+
+static char *prepare_commit_failure_human_readable(
+ TALLOC_CTX *mem_ctx,
+ int status,
+ const char *reason)
+{
+ const char *timestamp = NULL;
+ char *log_entry = NULL;
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ timestamp = audit_get_timestamp(frame);
+
+ log_entry = talloc_asprintf(
+ mem_ctx,
+ "[prepare] at [%s] status [%d] reason [%s]",
+ timestamp,
+ status,
+ reason);
+
+ TALLOC_FREE(frame);
+ return log_entry;
+}
+
+static void log_operation(
+ struct ldb_module *module,
+ const struct ldb_request *request,
+ const int ret)
+{
+
+ const struct ldb_message *message = get_message(request);
+ bool password_changed = has_password_changed(message);
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (CHECK_DEBUGLVLC(DBGC_SAMDB_AUDIT, OPERATION_LOG_LVL)) {
+ char *entry = NULL;
+ entry = operation_human_readable(
+ frame,
+ module,
+ request,
+ ret);
+ audit_log_hr(
+ OPERATION_HR_TAG,
+ entry,
+ DBGC_SAMDB_AUDIT,
+ OPERATION_LOG_LVL);
+ TALLOC_FREE(entry);
+ }
+ if (CHECK_DEBUGLVLC(DBGC_PWD_AUDIT, PASSWORD_LOG_LVL)) {
+ if (password_changed) {
+ char *entry = NULL;
+ entry = password_change_human_readable(
+ frame,
+ module,
+ request,
+ ret);
+ audit_log_hr(
+ PASSWORD_HR_TAG,
+ entry,
+ DBGC_PWD_AUDIT,
+ PASSWORD_LOG_LVL);
+ TALLOC_FREE(entry);
+ }
+ }
+#ifdef HAVE_JANSSON
+ if (CHECK_DEBUGLVLC(DBGC_SAMDB_AUDIT_JSON, OPERATION_LOG_LVL)) {
+ struct json_object json;
+ json = operation_json(module, request, ret);
+ audit_log_json(
+ OPERATION_JSON_TYPE,
+ &json,
+ DBGC_SAMDB_AUDIT_JSON,
+ OPERATION_LOG_LVL);
+ if (ac->send_samdb_events) {
+ audit_message_send(
+ ac->msg_ctx,
+ SAMDB_EVENT_NAME,
+ MSG_SAMDB_LOG,
+ &json);
+ }
+ json_free(&json);
+ }
+ if (CHECK_DEBUGLVLC(DBGC_PWD_AUDIT_JSON, PASSWORD_LOG_LVL)) {
+ if (password_changed) {
+ struct json_object json;
+ json = password_change_json(module, request, ret);
+ audit_log_json(
+ PASSWORD_JSON_TYPE,
+ &json,
+ DBGC_PWD_AUDIT_JSON,
+ PASSWORD_LOG_LVL);
+ if (ac->send_password_events) {
+ audit_message_send(
+ ac->msg_ctx,
+ PWD_EVENT_NAME,
+ MSG_PWD_LOG,
+ &json);
+ }
+ json_free(&json);
+ }
+ }
+#endif
+ TALLOC_FREE(frame);
+}
+
+static void log_transaction(
+ struct ldb_module *module,
+ const char *action)
+{
+
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (CHECK_DEBUGLVLC(DBGC_TRN_AUDIT, TRANSACTION_LOG_LVL)) {
+ char* entry = NULL;
+ entry = transaction_human_readable(frame, action);
+ audit_log_hr(
+ TRANSACTION_HR_TAG,
+ entry,
+ DBGC_TRN_AUDIT,
+ TRANSACTION_LOG_LVL);
+ TALLOC_FREE(entry);
+ }
+#ifdef HAVE_JANSSON
+ if (CHECK_DEBUGLVLC(DBGC_TRN_AUDIT_JSON, TRANSACTION_LOG_LVL)) {
+ struct json_object json;
+ json = transaction_json(action, &ac->transaction_guid);
+ audit_log_json(
+ TRANSACTION_JSON_TYPE,
+ &json,
+ DBGC_TRN_AUDIT_JSON,
+ TRANSACTION_LOG_LVL);
+ if (ac->send_samdb_events) {
+ audit_message_send(
+ ac->msg_ctx,
+ SAMDB_EVENT_NAME,
+ MSG_SAMDB_LOG,
+ &json);
+ }
+ json_free(&json);
+ }
+#endif
+ TALLOC_FREE(frame);
+}
+
+static void log_prepare_commit_failure(
+ struct ldb_module *module,
+ int status)
+{
+
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+ const char* reason = get_ldb_error_string(module, status);
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (CHECK_DEBUGLVLC(DBGC_TRN_AUDIT, TRANSACTION_LOG_LVL)) {
+ char* entry = NULL;
+ entry = prepare_commit_failure_human_readable(
+ frame,
+ status,
+ reason);
+ audit_log_hr(
+ TRANSACTION_HR_TAG,
+ entry,
+ DBGC_TRN_AUDIT,
+ TRANSACTION_LOG_LVL);
+ TALLOC_FREE(entry);
+ }
+#ifdef HAVE_JANSSON
+ if (CHECK_DEBUGLVLC(DBGC_TRN_AUDIT_JSON, TRANSACTION_LOG_LVL)) {
+ struct json_object json;
+ json = prepare_commit_failure_json(
+ status,
+ reason,
+ &ac->transaction_guid);
+ audit_log_json(
+ TRANSACTION_JSON_TYPE,
+ &json,
+ DBGC_TRN_AUDIT_JSON,
+ TRANSACTION_LOG_LVL);
+ if (ac->send_samdb_events) {
+ audit_message_send(ac->msg_ctx,
+ SAMDB_EVENT_NAME,
+ MSG_SAMDB_LOG,
+ &json);
+ }
+ json_free(&json);
+ }
+#endif
+ TALLOC_FREE(frame);
+}
+
+struct audit_callback_context {
+ struct ldb_request *request;
+ struct ldb_module *module;
+};
+
+static int audit_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct audit_callback_context *ac = NULL;
+
+ ac = talloc_get_type(req->context,
+ struct audit_callback_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->request, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* pass on to the callback */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ return ldb_module_send_entry(ac->request,
+ ares->message,
+ ares->controls);
+
+ case LDB_REPLY_REFERRAL:
+ return ldb_module_send_referral(ac->request,
+ ares->referral);
+
+ case LDB_REPLY_DONE:
+ /* Log on DONE now we have the error or success */
+ log_operation(ac->module, ac->request, ares->error);
+ return ldb_module_done(ac->request,
+ ares->controls,
+ ares->response,
+ ares->error);
+ break;
+
+ default:
+ /* Can't happen */
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+}
+
+static int log_add(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct audit_callback_context *context = NULL;
+ struct ldb_request *new_req = NULL;
+ struct ldb_context *ldb = NULL;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ context = talloc_zero(req, struct audit_callback_context);
+
+ if (context == NULL) {
+ return ldb_oom(ldb);
+ }
+ context->request = req;
+ context->module = module;
+ /*
+ * We want to log the return code status, so we need to register
+ * a callback function to get the actual result.
+ * We need to take a new copy so that we don't alter the callers copy
+ */
+ ret = ldb_build_add_req(&new_req,
+ ldb,
+ req,
+ req->op.add.message,
+ req->controls,
+ context,
+ audit_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return ldb_next_request(module, new_req);
+}
+
+static int log_delete(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct audit_callback_context *context = NULL;
+ struct ldb_request *new_req = NULL;
+ struct ldb_context *ldb = NULL;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ context = talloc_zero(req, struct audit_callback_context);
+
+ if (context == NULL) {
+ return ldb_oom(ldb);
+ }
+ context->request = req;
+ context->module = module;
+ /*
+ * We want to log the return code status, so we need to register
+ * a callback function to get the actual result.
+ * We need to take a new copy so that we don't alter the callers copy
+ */
+ ret = ldb_build_del_req(&new_req,
+ ldb,
+ req,
+ req->op.del.dn,
+ req->controls,
+ context,
+ audit_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return ldb_next_request(module, new_req);
+}
+
+static int log_modify(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct audit_callback_context *context = NULL;
+ struct ldb_request *new_req = NULL;
+ struct ldb_context *ldb = NULL;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ context = talloc_zero(req, struct audit_callback_context);
+
+ if (context == NULL) {
+ return ldb_oom(ldb);
+ }
+ context->request = req;
+ context->module = module;
+ /*
+ * We want to log the return code status, so we need to register
+ * a callback function to get the actual result.
+ * We need to take a new copy so that we don't alter the callers copy
+ */
+ ret = ldb_build_mod_req(&new_req,
+ ldb,
+ req,
+ req->op.mod.message,
+ req->controls,
+ context,
+ audit_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return ldb_next_request(module, new_req);
+}
+
+static int log_start_transaction(struct ldb_module *module)
+{
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+
+ /*
+ * We do not log transaction begins
+ *
+ */
+ ac->transaction_guid = GUID_random();
+ return ldb_next_start_trans(module);
+}
+
+static int log_prepare_commit(struct ldb_module *module)
+{
+
+ int ret = ldb_next_prepare_commit(module);
+ if (ret != LDB_SUCCESS) {
+ /*
+ * We currently only log prepare commit failures
+ */
+ log_prepare_commit_failure(module, ret);
+ }
+ return ret;
+}
+
+static int log_end_transaction(struct ldb_module *module)
+{
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+
+ /*
+ * We do not currently log transaction commits
+ */
+ memset(&ac->transaction_guid, 0, sizeof(struct GUID));
+
+ return ldb_next_end_trans(module);
+}
+
+static int log_del_transaction(struct ldb_module *module)
+{
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+
+ log_transaction(module, "rollback");
+ memset(&ac->transaction_guid, 0, sizeof(struct GUID));
+ return ldb_next_del_trans(module);
+}
+
+static int log_init(struct ldb_module *module)
+{
+
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct audit_context *context = NULL;
+ struct loadparm_context *lp_ctx
+ = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"),
+ struct loadparm_context);
+ struct tevent_context *ec = ldb_get_event_context(ldb);
+ bool sdb_events = false;
+ bool pwd_events = false;
+
+ context = talloc_zero(module, struct audit_context);
+ if (context == NULL) {
+ return ldb_module_oom(module);
+ }
+
+ if (lp_ctx != NULL) {
+ sdb_events = lpcfg_samdb_event_notification(lp_ctx);
+ pwd_events = lpcfg_password_event_notification(lp_ctx);
+ }
+ if (sdb_events || pwd_events) {
+ context->send_samdb_events = sdb_events;
+ context->send_password_events = pwd_events;
+ context->msg_ctx = imessaging_client_init(ec, lp_ctx, ec);
+ }
+
+ ldb_module_set_private(module, context);
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ldb_audit_log_module_ops = {
+ .name = "audit_log",
+ .add = log_add,
+ .modify = log_modify,
+ .del = log_delete,
+ .init_context = log_init,
+ .start_transaction = log_start_transaction,
+ .prepare_commit = log_prepare_commit,
+ .end_transaction = log_end_transaction,
+ .del_transaction = log_del_transaction,
+};
+
+int ldb_audit_log_module_init(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_audit_log_module_ops);
+}
diff --git a/source4/dsdb/samdb/ldb_modules/audit_util.c b/source4/dsdb/samdb/ldb_modules/audit_util.c
new file mode 100644
index 0000000..c85661a
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/audit_util.c
@@ -0,0 +1,470 @@
+/*
+ ldb database module utility library
+
+ Copyright (C) Andrew Bartlett <abartlet at samba.org> 2018
+
+ 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/>.
+*/
+
+/*
+ * Common utility functions for SamDb audit logging.
+ *
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "lib/audit_logging/audit_logging.h"
+
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/samdb/ldb_modules/util.h"
+#include "libcli/security/dom_sid.h"
+#include "auth/common_auth.h"
+#include "param/param.h"
+#include "dsdb/samdb/ldb_modules/util.h"
+
+#define MAX_LENGTH 1024
+
+#define min(a, b) (((a)>(b))?(b):(a))
+
+static const char * const secret_attributes[] = {DSDB_SECRET_ATTRIBUTES, NULL};
+
+
+/*
+ * @brief Get the remote address from the ldb context.
+ *
+ * The remote address is stored in the ldb opaque value "remooteAddress"
+ * it is the responsibility of the higher level code to ensure that this
+ * value is set.
+ *
+ * @param ldb the ldb_context.
+ *
+ * @return the remote address if known, otherwise NULL.
+ */
+const struct tsocket_address *get_remote_address(
+ struct ldb_context *ldb)
+{
+ void *opaque_remote_address = NULL;
+ struct tsocket_address *remote_address;
+
+ opaque_remote_address = ldb_get_opaque(ldb,
+ "remoteAddress");
+ if (opaque_remote_address == NULL) {
+ return NULL;
+ }
+
+ remote_address = talloc_get_type(opaque_remote_address,
+ struct tsocket_address);
+ return remote_address;
+}
+
+/*
+ * @brief get the ldb error string.
+ *
+ * Get the ldb error string if set, otherwise get the generic error code
+ * for the status code.
+ *
+ * @param ldb the ldb_context.
+ * @param status the ldb_status code.
+ *
+ * @return a string descrfing the error.
+ */
+const char *get_ldb_error_string(
+ struct ldb_module *module,
+ int status)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ const char *err_string = ldb_errstring(ldb);
+
+ if (err_string == NULL) {
+ return ldb_strerror(status);
+ }
+ return err_string;
+}
+
+/*
+ * @brief get the SID of the user performing the operation.
+ *
+ * Get the SID of the user performing the operation.
+ *
+ * @param module the ldb_module.
+ *
+ * @return the SID of the currently logged on user.
+ */
+const struct dom_sid *get_user_sid(const struct ldb_module *module)
+{
+ struct security_token *user_token = NULL;
+
+ /*
+ * acl_user_token does not alter module so it's safe
+ * to discard the const.
+ */
+ user_token = acl_user_token(discard_const(module));
+ if (user_token == NULL) {
+ return NULL;
+ }
+ return &user_token->sids[0];
+
+}
+
+/*
+ * @brief get the session identifier GUID
+ *
+ * Get the GUID that uniquely identifies the current authenticated session.
+ *
+ * @param module the ldb_module.
+ *
+ * @return the unique session GUID
+ */
+const struct GUID *get_unique_session_token(const struct ldb_module *module)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(discard_const(module));
+ struct auth_session_info *session_info
+ = (struct auth_session_info *)ldb_get_opaque(
+ ldb,
+ "sessionInfo");
+ if(!session_info) {
+ return NULL;
+ }
+ return &session_info->unique_session_token;
+}
+
+/*
+ * @brief Get a printable string value for the remote host address.
+ *
+ * Get a printable string representation of the remote host, for display in the
+ * the audit logs.
+ *
+ * @param ldb the ldb context.
+ * @param mem_ctx the talloc memory context that will own the returned string.
+ *
+ * @return A string representation of the remote host address or "Unknown"
+ *
+ */
+char *get_remote_host(
+ struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx)
+{
+ const struct tsocket_address *remote_address;
+ char* remote_host = NULL;
+
+ remote_address = get_remote_address(ldb);
+ if (remote_address == NULL) {
+ remote_host = talloc_asprintf(mem_ctx, "Unknown");
+ return remote_host;
+ }
+
+ remote_host = tsocket_address_string(remote_address, mem_ctx);
+ return remote_host;
+}
+
+/*
+ * @brief get a printable representation of the primary DN.
+ *
+ * Get a printable representation of the primary DN. The primary DN is the
+ * DN of the object being added, deleted, modified or renamed.
+ *
+ * @param the ldb_request.
+ *
+ * @return a printable and linearized DN
+ */
+const char* get_primary_dn(
+ const struct ldb_request *request)
+{
+ struct ldb_dn *dn = NULL;
+ switch (request->operation) {
+ case LDB_ADD:
+ if (request->op.add.message != NULL) {
+ dn = request->op.add.message->dn;
+ }
+ break;
+ case LDB_MODIFY:
+ if (request->op.mod.message != NULL) {
+ dn = request->op.mod.message->dn;
+ }
+ break;
+ case LDB_DELETE:
+ dn = request->op.del.dn;
+ break;
+ case LDB_RENAME:
+ dn = request->op.rename.olddn;
+ break;
+ default:
+ dn = NULL;
+ break;
+ }
+ if (dn == NULL) {
+ return NULL;
+ }
+ return ldb_dn_get_linearized(dn);
+}
+
+/*
+ * @brief Get the ldb_message from a request.
+ *
+ * Get the ldb_message for the request, returns NULL is there is no
+ * associated ldb_message
+ *
+ * @param The request
+ *
+ * @return the message associated with this request, or NULL
+ */
+const struct ldb_message *get_message(
+ const struct ldb_request *request)
+{
+ switch (request->operation) {
+ case LDB_ADD:
+ return request->op.add.message;
+ case LDB_MODIFY:
+ return request->op.mod.message;
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * @brief get the secondary dn, i.e. the target dn for a rename.
+ *
+ * Get the secondary dn, i.e. the target for a rename. This is only applicable
+ * got a rename operation, for the non rename operations this function returns
+ * NULL.
+ *
+ * @param request the ldb_request.
+ *
+ * @return the secondary dn in a printable and linearized form.
+ */
+const char *get_secondary_dn(
+ const struct ldb_request *request)
+{
+ switch (request->operation) {
+ case LDB_RENAME:
+ return ldb_dn_get_linearized(request->op.rename.newdn);
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * @brief Map the request operation to a description.
+ *
+ * Get a description of the operation for logging
+ *
+ * @param request the ldb_request
+ *
+ * @return a string describing the operation, or "Unknown" if the operation
+ * is not known.
+ */
+const char *get_operation_name(
+ const struct ldb_request *request)
+{
+ switch (request->operation) {
+ case LDB_SEARCH:
+ return "Search";
+ case LDB_ADD:
+ return "Add";
+ case LDB_MODIFY:
+ return "Modify";
+ case LDB_DELETE:
+ return "Delete";
+ case LDB_RENAME:
+ return "Rename";
+ case LDB_EXTENDED:
+ return "Extended";
+ case LDB_REQ_REGISTER_CONTROL:
+ return "Register Control";
+ case LDB_REQ_REGISTER_PARTITION:
+ return "Register Partition";
+ default:
+ return "Unknown";
+ }
+}
+
+/*
+ * @brief get a description of a modify action for logging.
+ *
+ * Get a brief description of the modification action suitable for logging.
+ *
+ * @param flags the ldb_attributes flags.
+ *
+ * @return a brief description, or "unknown".
+ */
+const char *get_modification_action(
+ unsigned int flags)
+{
+ switch (LDB_FLAG_MOD_TYPE(flags)) {
+ case LDB_FLAG_MOD_ADD:
+ return "add";
+ case LDB_FLAG_MOD_DELETE:
+ return "delete";
+ case LDB_FLAG_MOD_REPLACE:
+ return "replace";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * @brief Add an ldb_value to a json object array
+ *
+ * Convert the current ldb_value to a JSON object and append it to array.
+ * {
+ * "value":"xxxxxxxx",
+ * "base64":true
+ * "truncated":true
+ * }
+ *
+ * value is the JSON string representation of the ldb_val,
+ * will be null if the value is zero length. The value will be
+ * truncated if it is more than MAX_LENGTH bytes long. It will also
+ * be base64 encoded if it contains any non printable characters.
+ *
+ * base64 Indicates that the value is base64 encoded, will be absent if the
+ * value is not encoded.
+ *
+ * truncated Indicates that the length of the value exceeded MAX_LENGTH and was
+ * truncated. Note that vales are truncated and then base64 encoded.
+ * so an encoded value can be longer than MAX_LENGTH.
+ *
+ * @param array the JSON array to append the value to.
+ * @param lv the ldb_val to convert and append to the array.
+ *
+ */
+static void add_ldb_value(
+ struct json_object *array,
+ const struct ldb_val lv)
+{
+
+ json_assert_is_array(array);
+ if (json_is_invalid(array)) {
+ return;
+ }
+
+ if (lv.length == 0 || lv.data == NULL) {
+ json_add_object(array, NULL, NULL);
+ return;
+ }
+
+ bool base64 = ldb_should_b64_encode(NULL, &lv);
+ int len = min(lv.length, MAX_LENGTH);
+ struct json_object value = json_new_object();
+ if (lv.length > MAX_LENGTH) {
+ json_add_bool(&value, "truncated", true);
+ }
+ if (base64) {
+ TALLOC_CTX *ctx = talloc_new(NULL);
+ char *encoded = ldb_base64_encode(
+ ctx,
+ (char*) lv.data,
+ len);
+
+ json_add_bool(&value, "base64", true);
+ json_add_string(&value, "value", encoded);
+ TALLOC_FREE(ctx);
+ } else {
+ json_add_stringn(&value, "value", (char *)lv.data, len);
+ }
+ /*
+ * As array is a JSON array the element name is NULL
+ */
+ json_add_object(array, NULL, &value);
+}
+
+/*
+ * @brief Build a JSON object containing the attributes in an ldb_message.
+ *
+ * Build a JSON object containing all the attributes in an ldb_message.
+ * The attributes are keyed by attribute name, the values of "secret attributes"
+ * are supressed.
+ *
+ * {
+ * "password":{
+ * "redacted":true,
+ * "action":"delete"
+ * },
+ * "name":{
+ * "values": [
+ * {
+ * "value":"xxxxxxxx",
+ * "base64":true
+ * "truncated":true
+ * },
+ * ],
+ * "action":"add",
+ * }
+ * }
+ *
+ * values is an array of json objects generated by add_ldb_value.
+ * redacted indicates that the attribute is secret.
+ * action is only set for modification operations.
+ *
+ * @param operation the ldb operation being performed
+ * @param message the ldb_message to process.
+ *
+ * @return A populated json object.
+ *
+ */
+struct json_object attributes_json(
+ enum ldb_request_type operation,
+ const struct ldb_message* message)
+{
+
+ struct json_object attributes = json_new_object();
+ int i, j;
+ for (i=0;i<message->num_elements;i++) {
+ struct json_object values;
+ struct json_object attribute = json_new_object();
+
+ /*
+ * If this is a modify operation tag the attribute with
+ * the modification action.
+ */
+ if (operation == LDB_MODIFY) {
+ const char *action = NULL;
+ const int flags = message->elements[i].flags;
+ action = get_modification_action(flags);
+ json_add_string(&attribute, "action" , action);
+ }
+
+ /*
+ * If the attribute is a secret attribute, tag it as redacted
+ * and don't include the values
+ */
+ bool redact = ldb_attr_in_list(
+ secret_attributes,
+ message->elements[i].name);
+ if (redact) {
+ json_add_bool(&attribute, "redacted", true);
+ json_add_object(
+ &attributes,
+ message->elements[i].name,
+ &attribute);
+ continue;
+ }
+
+ /*
+ * Add the values for the object
+ */
+ values = json_new_array();
+ for (j=0;j<message->elements[i].num_values;j++) {
+ add_ldb_value(
+ &values,
+ message->elements[i].values[j]);
+ }
+ json_add_object(&attribute, "values", &values);
+ json_add_object(
+ &attributes,
+ message->elements[i].name,
+ &attribute);
+ }
+ return attributes;
+}
diff --git a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
index 54ec6a2..baa30f9 100644
--- a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
+++ b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
@@ -292,7 +292,8 @@ static int samba_dsdb_init(struct ldb_module *module)
"extended_dn_store",
NULL };
/* extended_dn_in or extended_dn_in_openldap goes here */
- static const char *modules_list1a[] = {"objectclass",
+ static const char *modules_list1a[] = {"audit_log",
+ "objectclass",
"tombstone_reanimate",
"descriptor",
"acl",
diff --git a/source4/dsdb/samdb/ldb_modules/tests/test_audit_log.c b/source4/dsdb/samdb/ldb_modules/tests/test_audit_log.c
new file mode 100644
index 0000000..7948322
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/tests/test_audit_log.c
@@ -0,0 +1,468 @@
+/*
+ Unit tests for the dsdb audit logging code code in audit_log.c
+
+ Copyright (C) Andrew Bartlett <abartlet at samba.org> 2018
+
+ 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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include <cmocka.h>
+
+int ldb_audit_log_module_init(const char *version);
+#include "../audit_log.c"
+
+#include "lib/ldb/include/ldb_private.h"
+
+static void test_has_password_changed(void **state)
+{
+ struct ldb_context *ldb = NULL;
+ struct ldb_message *msg = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ ldb = talloc_zero(ctx, struct ldb_context);
+
+ /*
+ * Empty message
+ */
+ msg = ldb_msg_new(ldb);
+ assert_false(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * No password attributes
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "attr01", "value01");
+ assert_false(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * No password attributes >1 entries
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "attr01", "value01");
+ ldb_msg_add_string(msg, "attr02", "value03");
+ ldb_msg_add_string(msg, "attr03", "value03");
+ assert_false(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * userPassword set
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "userPassword", "value01");
+ assert_true(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * clearTextPassword set
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "clearTextPassword", "value01");
+ assert_true(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * unicodePwd set
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "unicodePwd", "value01");
+ assert_true(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * dBCSPwd set
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "dBCSPwd", "value01");
+ assert_true(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * All attributes set
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "userPassword", "value01");
+ ldb_msg_add_string(msg, "clearTextPassword", "value02");
+ ldb_msg_add_string(msg, "unicodePwd", "value03");
+ ldb_msg_add_string(msg, "dBCSPwd", "value04");
+ assert_true(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * first attribute is a password attribute
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "userPassword", "value01");
+ ldb_msg_add_string(msg, "attr02", "value02");
+ ldb_msg_add_string(msg, "attr03", "value03");
+ ldb_msg_add_string(msg, "attr04", "value04");
+ assert_true(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * last attribute is a password attribute
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "attr01", "value01");
+ ldb_msg_add_string(msg, "attr02", "value02");
+ ldb_msg_add_string(msg, "attr03", "value03");
+ ldb_msg_add_string(msg, "clearTextPassword", "value04");
+ assert_true(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ /*
+ * middle attribute is a password attribute
+ */
+ msg = ldb_msg_new(ldb);
+ ldb_msg_add_string(msg, "attr01", "value01");
+ ldb_msg_add_string(msg, "attr02", "value02");
+ ldb_msg_add_string(msg, "unicodePwd", "pwd");
+ ldb_msg_add_string(msg, "attr03", "value03");
+ ldb_msg_add_string(msg, "attr04", "value04");
+ assert_true(has_password_changed(msg));
+ TALLOC_FREE(msg);
+
+ TALLOC_FREE(ctx);
+}
+
+static void test_get_password_action(void **state)
+{
+ struct ldb_context *ldb = NULL;
+ struct ldb_request *req = NULL;
+ struct dsdb_control_password_acl_validation *pav = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+ ldb = talloc_zero(ctx, struct ldb_context);
+
+ /*
+ * Add request, will always be a reset
+ */
+ ldb_build_add_req(&req, ldb, ctx, NULL, NULL, NULL, NULL, NULL);
+ assert_string_equal("Reset", get_password_action(req));
+ TALLOC_FREE(req);
+
+ /*
+ * No password control acl, expect "Change"
+ */
+ ldb_build_mod_req(&req, ldb, ctx, NULL, NULL, NULL, NULL, NULL);
+ assert_string_equal("Change", get_password_action(req));
+ TALLOC_FREE(req);
+
+ /*
+ * dsdb_control_password_acl_validation reset = false, expect "Change"
+ */
+ ldb_build_mod_req(&req, ldb, ctx, NULL, NULL, NULL, NULL, NULL);
+ pav = talloc_zero(req, struct dsdb_control_password_acl_validation);
+
+ ldb_request_add_control(
+ req,
+ DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID,
+ false,
+ pav);
+ assert_string_equal("Change", get_password_action(req));
+ TALLOC_FREE(req);
+
+ /*
+ * dsdb_control_password_acl_validation reset = true, expect "Reset"
+ */
+ ldb_build_mod_req(&req, ldb, ctx, NULL, NULL, NULL, NULL, NULL);
+ pav = talloc_zero(req, struct dsdb_control_password_acl_validation);
+ pav->pwd_reset = true;
+
+ ldb_request_add_control(
+ req,
+ DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID,
+ false,
+ pav);
+ assert_string_equal("Reset", get_password_action(req));
+ TALLOC_FREE(req);
+
+ TALLOC_FREE(ctx);
+}
+
+/*
+ * minimal unit test of operation_json, that ensures that all the expected
+ * attributes and objects are in the json object.
+ */
+static void test_operation_json_empty(void **state)
+{
+ struct ldb_context *ldb = NULL;
+ struct ldb_module *module = NULL;
+ struct ldb_request *req = NULL;
+ struct audit_context *ac = NULL;
+
+ struct json_object json;
+ json_t *audit = NULL;
+ json_t *v = NULL;
+ json_t *o = NULL;
+
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ ldb = talloc_zero(ctx, struct ldb_context);
+ ac = talloc_zero(ctx, struct audit_context);
+
+ module = talloc_zero(ctx, struct ldb_module);
+ module->ldb = ldb;
+ ldb_module_set_private(module, ac);
+
+ req = talloc_zero(ctx, struct ldb_request);
+
+ json = operation_json(module, req, LDB_SUCCESS);
+ assert_int_equal(3, json_object_size(json.root));
+
+
+ v = json_object_get(json.root, "type");
+ assert_non_null(v);
+ assert_string_equal("samdbChange", json_string_value(v));
+
+ v = json_object_get(json.root, "timestamp");
+ assert_non_null(v);
+ assert_true(json_is_string(v));
+
+ audit = json_object_get(json.root, "samdbChange");
+ assert_non_null(audit);
+ assert_true(json_is_object(audit));
+ assert_int_equal(9, json_object_size(audit));
+
+ o = json_object_get(audit, "version");
+ assert_non_null(o);
+
+ v = json_object_get(audit, "statusCode");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "status");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "operation");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "remoteAddress");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "userSid");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "dn");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "transactionId");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "sessionId");
+ assert_non_null(v);
+
+ json_free(&json);
+ TALLOC_FREE(ctx);
+
+}
+
+/*
+ * minimal unit test of password_change_json, that ensures that all the expected
+ * attributes and objects are in the json object.
+ */
+static void test_password_change_json_empty(void **state)
+{
+ struct ldb_context *ldb = NULL;
+ struct ldb_module *module = NULL;
+ struct ldb_request *req = NULL;
+ struct audit_context *ac = NULL;
+
+ struct json_object json;
+ json_t *audit = NULL;
+ json_t *v = NULL;
+ json_t *o = NULL;
+
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ ldb = talloc_zero(ctx, struct ldb_context);
+ ac = talloc_zero(ctx, struct audit_context);
+
+ module = talloc_zero(ctx, struct ldb_module);
+ module->ldb = ldb;
+ ldb_module_set_private(module, ac);
+
+ req = talloc_zero(ctx, struct ldb_request);
+
+ json = password_change_json(module, req, LDB_SUCCESS);
+ assert_int_equal(3, json_object_size(json.root));
+
+
+ v = json_object_get(json.root, "type");
+ assert_non_null(v);
+ assert_string_equal("passwordChange", json_string_value(v));
+
+ v = json_object_get(json.root, "timestamp");
+ assert_non_null(v);
+ assert_true(json_is_string(v));
+
+ audit = json_object_get(json.root, "passwordChange");
+ assert_non_null(audit);
+ assert_true(json_is_object(audit));
+ assert_int_equal(9, json_object_size(audit));
+
+ o = json_object_get(audit, "version");
+ assert_non_null(o);
+
+ v = json_object_get(audit, "statusCode");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "status");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "remoteAddress");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "userSid");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "dn");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "transactionId");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "sessionId");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "action");
+ assert_non_null(v);
+
+ json_free(&json);
+ TALLOC_FREE(ctx);
+
+}
+
+/*
+ * minimal unit test of transaction_json, that ensures that all the expected
+ * attributes and objects are in the json object.
+ */
+static void test_transaction_json_empty(void **state)
+{
+
+ struct GUID guid;
+ const char * const GUID = "7130cb06-2062-6a1b-409e-3514c26b1773";
+
+ struct json_object json;
+ json_t *audit = NULL;
+ json_t *v = NULL;
+ json_t *o = NULL;
+
+ GUID_from_string(GUID, &guid);
+ json = transaction_json("delete", &guid);
+
+ assert_int_equal(3, json_object_size(json.root));
+
+
+ v = json_object_get(json.root, "type");
+ assert_non_null(v);
+ assert_string_equal("samdbTransaction", json_string_value(v));
+
+ v = json_object_get(json.root, "timestamp");
+ assert_non_null(v);
+ assert_true(json_is_string(v));
+
+ audit = json_object_get(json.root, "samdbTransaction");
+ assert_non_null(audit);
+ assert_true(json_is_object(audit));
+ assert_int_equal(3, json_object_size(audit));
+
+ o = json_object_get(audit, "version");
+ assert_non_null(o);
+
+ v = json_object_get(audit, "transactionId");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "action");
+ assert_non_null(v);
+
+ json_free(&json);
+
+}
+
+/*
+ * minimal unit test of prepare_commit_failure_json, that ensures that all the
+ * expected attributes and objects are in the json object.
+ */
+static void test_prepare_commit_failure_json_empty(void **state)
+{
+
+ struct GUID guid;
+ const char * const GUID = "7130cb06-2062-6a1b-409e-3514c26b1773";
+
+ struct json_object json;
+ json_t *audit = NULL;
+ json_t *v = NULL;
+ json_t *o = NULL;
+
+ GUID_from_string(GUID, &guid);
+ json = prepare_commit_failure_json(LDB_SUCCESS, "because", &guid);
+
+ assert_int_equal(3, json_object_size(json.root));
+
+
+ v = json_object_get(json.root, "type");
+ assert_non_null(v);
+ assert_string_equal("samdbTransaction", json_string_value(v));
+
+ v = json_object_get(json.root, "timestamp");
+ assert_non_null(v);
+ assert_true(json_is_string(v));
+
+ audit = json_object_get(json.root, "samdbTransaction");
+ assert_non_null(audit);
+ assert_true(json_is_object(audit));
+ assert_int_equal(6, json_object_size(audit));
+
+ o = json_object_get(audit, "version");
+ assert_non_null(o);
+
+ v = json_object_get(audit, "transactionId");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "action");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "status");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "statusCode");
+ assert_non_null(v);
+
+ v = json_object_get(audit, "reason");
+ assert_non_null(v);
+
+ json_free(&json);
+
+}
+int main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_has_password_changed),
+ cmocka_unit_test(test_get_password_action),
+ cmocka_unit_test(test_operation_json_empty),
+ cmocka_unit_test(test_password_change_json_empty),
+ cmocka_unit_test(test_transaction_json_empty),
+ cmocka_unit_test(test_prepare_commit_failure_json_empty),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/source4/dsdb/samdb/ldb_modules/tests/test_audit_util.c b/source4/dsdb/samdb/ldb_modules/tests/test_audit_util.c
new file mode 100644
index 0000000..5ed4cd6
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/tests/test_audit_util.c
@@ -0,0 +1,958 @@
+/*
+ Unit tests for the dsdb audit logging utility code code in audit_util.c
+
+ Copyright (C) Andrew Bartlett <abartlet at samba.org> 2018
+
+ 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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <unistd.h>
+#include <cmocka.h>
+
+#include "../audit_util.c"
+
+#include "lib/ldb/include/ldb_private.h"
+
+#ifdef HAVE_JANSSON
+static void test_add_ldb_value(void **state)
+{
+ struct json_object object;
+ struct json_object array;
+ struct ldb_val val = data_blob_null;
+ struct json_t *el = NULL;
+ struct json_t *atr = NULL;
+ char* base64 = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+ /*
+ * Test a non array object
+ */
+ object = json_new_object();
+ assert_false(json_is_invalid(&object));
+ add_ldb_value(&object, val);
+ assert_true(json_is_invalid(&object));
+ json_free(&object);
+
+ array = json_new_array();
+ /*
+ * Test a data_blob_null, should encode as a JSON null value.
+ */
+ val = data_blob_null;
+ add_ldb_value(&array, val);
+ el = json_array_get(array.root, 0);
+ assert_true(json_is_null(el));
+
+ /*
+ * Test a +ve length but a null data ptr, should encode as a null.
+ */
+ val = data_blob_null;
+ val.length = 1;
+ add_ldb_value(&array, val);
+ el = json_array_get(array.root, 1);
+ assert_true(json_is_null(el));
+
+ /*
+ * Test a zero length but a non null data ptr, should encode as a null.
+ */
+ val = data_blob_null;
+ val.data = discard_const("Data on the stack");
+ add_ldb_value(&array, val);
+ el = json_array_get(array.root, 2);
+ assert_true(json_is_null(el));
+
+ /*
+ * Test a printable value.
+ * value should not be encoded
+ * truncated and base64 should be missing
+ */
+ val = data_blob_string_const("A value of interest");
+ add_ldb_value(&array, val);
+ el = json_array_get(array.root, 3);
+ assert_true(json_is_object(el));
+ atr = json_object_get(el, "value");
+ assert_true(json_is_string(atr));
+ assert_string_equal("A value of interest", json_string_value(atr));
+ assert_null(json_object_get(el, "truncated"));
+ assert_null(json_object_get(el, "base64"));
+
+ /*
+ * Test non printable value, should be base64 encoded.
+ * truncated should be missing and base64 should be set.
+ */
+ val = data_blob_string_const("A value of interest\n");
+ add_ldb_value(&array, val);
+ el = json_array_get(array.root, 4);
+ assert_true(json_is_object(el));
+ atr = json_object_get(el, "value");
+ assert_true(json_is_string(atr));
+ assert_string_equal(
+ "QSB2YWx1ZSBvZiBpbnRlcmVzdAo=",
+ json_string_value(atr));
+ atr = json_object_get(el, "base64");
+ assert_true(json_is_boolean(atr));
+ assert_true(json_boolean(atr));
+ assert_null(json_object_get(el, "truncated"));
+
+ /*
+ * test a printable value exactly max bytes long
+ * should not be truncated or encoded.
+ */
+ val = data_blob_null;
+ val.length = MAX_LENGTH;
+ val.data = (unsigned char *)generate_random_str_list(
+ ctx,
+ MAX_LENGTH,
+ "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "1234567890!@#$%^&*()");
+
+ add_ldb_value(&array, val);
+
+ el = json_array_get(array.root, 5);
+ assert_true(json_is_object(el));
+ atr = json_object_get(el, "value");
+ assert_true(json_is_string(atr));
+ assert_int_equal(MAX_LENGTH, strlen(json_string_value(atr)));
+ assert_memory_equal(val.data, json_string_value(atr), MAX_LENGTH);
+
+ assert_null(json_object_get(el, "base64"));
+ assert_null(json_object_get(el, "truncated"));
+
+
+ /*
+ * test a printable value exactly max + 1 bytes long
+ * should be truncated and not encoded.
+ */
+ val = data_blob_null;
+ val.length = MAX_LENGTH + 1;
+ val.data = (unsigned char *)generate_random_str_list(
+ ctx,
+ MAX_LENGTH + 1,
+ "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "1234567890!@#$%^&*()");
+
+ add_ldb_value(&array, val);
+
+ el = json_array_get(array.root, 6);
+ assert_true(json_is_object(el));
+ atr = json_object_get(el, "value");
+ assert_true(json_is_string(atr));
+ assert_int_equal(MAX_LENGTH, strlen(json_string_value(atr)));
+ assert_memory_equal(val.data, json_string_value(atr), MAX_LENGTH);
+
+ atr = json_object_get(el, "truncated");
+ assert_true(json_is_boolean(atr));
+ assert_true(json_boolean(atr));
+
+ assert_null(json_object_get(el, "base64"));
+
+ TALLOC_FREE(val.data);
+
+ /*
+ * test a non-printable value exactly max bytes long
+ * should not be truncated but should be encoded.
+ */
+ val = data_blob_null;
+ val.length = MAX_LENGTH;
+ val.data = (unsigned char *)generate_random_str_list(
+ ctx,
+ MAX_LENGTH,
+ "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "1234567890!@#$%^&*()");
+
+ val.data[0] = 0x03;
+ add_ldb_value(&array, val);
+ base64 = ldb_base64_encode(ctx, (char*) val.data, MAX_LENGTH);
+
+ el = json_array_get(array.root, 7);
+ assert_true(json_is_object(el));
+ atr = json_object_get(el, "value");
+ assert_true(json_is_string(atr));
+ assert_int_equal(strlen(base64), strlen(json_string_value(atr)));
+ assert_string_equal(base64, json_string_value(atr));
+
+ atr = json_object_get(el, "base64");
+ assert_true(json_is_boolean(atr));
+ assert_true(json_boolean(atr));
+
+ assert_null(json_object_get(el, "truncated"));
+ TALLOC_FREE(base64);
+ TALLOC_FREE(val.data);
+
+ /*
+ * test a non-printable value exactly max + 1 bytes long
+ * should be truncated and encoded.
+ */
+ val = data_blob_null;
+ val.length = MAX_LENGTH + 1;
+ val.data = (unsigned char *)generate_random_str_list(
+ ctx,
+ MAX_LENGTH + 1,
+ "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "1234567890!@#$%^&*()");
+
+ val.data[0] = 0x03;
+ add_ldb_value(&array, val);
+ /*
+ * The data is truncated before it is base 64 encoded
+ */
+ base64 = ldb_base64_encode(ctx, (char*) val.data, MAX_LENGTH);
+
+ el = json_array_get(array.root, 8);
+ assert_true(json_is_object(el));
+ atr = json_object_get(el, "value");
+ assert_true(json_is_string(atr));
+ assert_int_equal(strlen(base64), strlen(json_string_value(atr)));
+ assert_string_equal(base64, json_string_value(atr));
+
+ atr = json_object_get(el, "base64");
+ assert_true(json_is_boolean(atr));
+ assert_true(json_boolean(atr));
+
+ atr = json_object_get(el, "truncated");
+ assert_true(json_is_boolean(atr));
+ assert_true(json_boolean(atr));
+
+ TALLOC_FREE(base64);
+ TALLOC_FREE(val.data);
+
+ json_free(&array);
+ TALLOC_FREE(ctx);
+}
+
+static void test_attributes_json(void **state)
+{
+ struct ldb_message *msg = NULL;
+
+ struct json_object o;
+ json_t *a = NULL;
+ json_t *v = NULL;
+ json_t *x = NULL;
+ json_t *y = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+
+ /*
+ * Test an empty message
+ * Should get an empty attributes object
+ */
+ msg = talloc_zero(ctx, struct ldb_message);
+
+ o = attributes_json(LDB_ADD, msg);
+ assert_true(json_is_object(o.root));
+ assert_int_equal(0, json_object_size(o.root));
+ json_free(&o);
+
+ o = attributes_json(LDB_MODIFY, msg);
+ assert_true(json_is_object(o.root));
+ assert_int_equal(0, json_object_size(o.root));
+ json_free(&o);
+
+ /*
+ * Test a message with a single secret attribute
+ * should only have that object and it should have no value
+ * attribute and redacted should be set.
+ */
+ msg = talloc_zero(ctx, struct ldb_message);
+ ldb_msg_add_string(msg, "clearTextPassword", "secret");
+
+ o = attributes_json(LDB_ADD, msg);
+ assert_true(json_is_object(o.root));
+ assert_int_equal(1, json_object_size(o.root));
+
+ a = json_object_get(o.root, "clearTextPassword");
+ assert_int_equal(1, json_object_size(a));
+ v = json_object_get(a, "redacted");
+ assert_true(json_is_boolean(v));
+ assert_true(json_boolean(v));
+
+ json_free(&o);
+
+ /*
+ * Test as a modify message, should add an action attribute
+ */
+ o = attributes_json(LDB_MODIFY, msg);
+ assert_true(json_is_object(o.root));
+ assert_int_equal(1, json_object_size(o.root));
+
+ a = json_object_get(o.root, "clearTextPassword");
+ assert_true(json_is_object(a));
+ assert_int_equal(2, json_object_size(a));
+
+ v = json_object_get(a, "redacted");
+ assert_true(json_is_boolean(v));
+ assert_true(json_boolean(v));
+
+ v = json_object_get(a, "action");
+ assert_true(json_is_string(v));
+ assert_string_equal("unknown", json_string_value(v));
+
+ json_free(&o);
+ TALLOC_FREE(msg);
+
+ /*
+ * Test a message with a single attribute, single valued attribute
+ */
+ msg = talloc_zero(ctx, struct ldb_message);
+ ldb_msg_add_string(msg, "attribute", "value");
+
+ o = attributes_json(LDB_ADD, msg);
+ assert_true(json_is_object(o.root));
+ assert_int_equal(1, json_object_size(o.root));
+
+ a = json_object_get(o.root, "attribute");
+ assert_true(json_is_object(a));
+ assert_int_equal(1, json_object_size(a));
+
+ v = json_object_get(a, "values");
+ assert_true(json_is_array(v));
+ assert_int_equal(1, json_array_size(v));
+ x = json_array_get(v, 0);
+ assert_true(json_is_object(x));
+ y = json_object_get(x, "value");
+ assert_string_equal("value", json_string_value(y));
+
+ json_free(&o);
+ TALLOC_FREE(msg);
+
+ /*
+ * Test a message with a single attribute, single valued attribute
+ * And as a modify
+ */
+ msg = talloc_zero(ctx, struct ldb_message);
+ ldb_msg_add_string(msg, "attribute", "value");
+
+ o = attributes_json(LDB_MODIFY, msg);
+ assert_true(json_is_object(o.root));
+ assert_int_equal(1, json_object_size(o.root));
+
+ a = json_object_get(o.root, "attribute");
+ assert_true(json_is_object(a));
+ assert_int_equal(2, json_object_size(a));
+
+ v = json_object_get(a, "action");
+ assert_true(json_is_string(v));
+ assert_string_equal("unknown", json_string_value(v));
+
+ v = json_object_get(a, "values");
+ assert_true(json_is_array(v));
+ assert_int_equal(1, json_array_size(v));
+ x = json_array_get(v, 0);
+ assert_true(json_is_object(x));
+ y = json_object_get(x, "value");
+ assert_string_equal("value", json_string_value(y));
+
+ json_free(&o);
+ TALLOC_FREE(msg);
+
+ /*
+ * Test a message with a single attribute, single valued attribute
+ */
+ msg = talloc_zero(ctx, struct ldb_message);
+ ldb_msg_add_string(msg, "attribute01", "value01");
+ ldb_msg_add_string(msg, "attribute02", "value02");
+ ldb_msg_add_string(msg, "attribute02", "value03");
+
+ o = attributes_json(LDB_ADD, msg);
+ assert_true(json_is_object(o.root));
+ assert_int_equal(2, json_object_size(o.root));
+
+ a = json_object_get(o.root, "attribute01");
+ assert_true(json_is_object(a));
+ assert_int_equal(1, json_object_size(a));
+ v = json_object_get(a, "values");
+ assert_true(json_is_array(v));
+ assert_int_equal(1, json_array_size(v));
+ x = json_array_get(v, 0);
+ assert_true(json_is_object(x));
+ y = json_object_get(x, "value");
+ assert_string_equal("value01", json_string_value(y));
+
+ a = json_object_get(o.root, "attribute02");
+ assert_true(json_is_object(a));
+ assert_int_equal(1, json_object_size(a));
+ v = json_object_get(a, "values");
+ assert_true(json_is_array(v));
+ assert_int_equal(2, json_array_size(v));
+ x = json_array_get(v, 0);
+ assert_true(json_is_object(x));
+ y = json_object_get(x, "value");
+ assert_string_equal("value02", json_string_value(y));
+ x = json_array_get(v, 1);
+ assert_true(json_is_object(x));
+ y = json_object_get(x, "value");
+ assert_string_equal("value03", json_string_value(y));
+
+ json_free(&o);
+ TALLOC_FREE(msg);
+
+ TALLOC_FREE(ctx);
+}
+#endif
+
+static void test_get_remote_address(void **state)
+{
+ struct ldb_context *ldb = NULL;
+ const struct tsocket_address *ts = NULL;
+ struct tsocket_address *in = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ /*
+ * Test a freshly initialized ldb
+ * should return NULL
+ */
+ ldb = talloc_zero(ctx, struct ldb_context);
+ ts = get_remote_address(ldb);
+ assert_null(ts);
+
+ /*
+ * opaque set to null, should return NULL
+ */
+ ldb_set_opaque(ldb, "remoteAddress", NULL);
+ ts = get_remote_address(ldb);
+ assert_null(ts);
+
+ /*
+ * Ensure that the value set is returned
+ */
+ tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 0, &in);
+ ldb_set_opaque(ldb, "remoteAddress", in);
+ ts = get_remote_address(ldb);
+ assert_non_null(ts);
+ assert_ptr_equal(in, ts);
+
+ TALLOC_FREE(ldb);
+ TALLOC_FREE(ctx);
+
+}
+
+static void test_get_ldb_error_string(void **state)
+{
+ struct ldb_context *ldb = NULL;
+ struct ldb_module *module = NULL;
+ const char *s = NULL;
+ const char * const text = "Custom reason";
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ ldb = talloc_zero(ctx, struct ldb_context);
+ module = talloc_zero(ctx, struct ldb_module);
+ module->ldb = ldb;
+
+ /*
+ * No ldb error string set should get the default error description for
+ * the status code
+ */
+ s = get_ldb_error_string(module, LDB_ERR_OPERATIONS_ERROR);
+ assert_string_equal("Operations error", s);
+
+ /*
+ * Set the error string that should now be returned instead of the
+ * default description.
+ */
+ ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, text);
+ s = get_ldb_error_string(module, LDB_ERR_OPERATIONS_ERROR);
+ /*
+ * Only test the start of the string as ldb_error adds location data.
+ */
+ assert_int_equal(0, strncmp(text, s, strlen(text)));
+
+ TALLOC_FREE(ctx);
+}
+
+static void test_get_user_sid(void **state)
+{
+ struct ldb_context *ldb = NULL;
+ struct ldb_module *module = NULL;
+ const struct dom_sid *sid = NULL;
+ struct auth_session_info *sess = NULL;
+ struct security_token *token = NULL;
+ struct dom_sid sid0;
+ struct dom_sid sid1;
+ struct dom_sid *sids[] = {&sid0, &sid1};
+ const char * const SID0 = "S-1-5-21-2470180966-3899876309-2637894779";
+ const char * const SID1 = "S-1-5-21-4284042908-2889457889-3672286761";
+ char sid_buf[DOM_SID_STR_BUFLEN];
+
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ ldb = talloc_zero(ctx, struct ldb_context);
+ module = talloc_zero(ctx, struct ldb_module);
+ module->ldb = ldb;
+
+ /*
+ * Freshly initialised structures, will be no session data
+ * so expect NULL
+ */
+ sid = get_user_sid(module);
+ assert_null(sid);
+
+ /*
+ * Now add a NULL session info
+ */
+ ldb_set_opaque(ldb, "sessionInfo", NULL);
+ sid = get_user_sid(module);
+ assert_null(sid);
+
+ /*
+ * Now add a session info with no user sid
+ */
+ sess = talloc_zero(ctx, struct auth_session_info);
+ ldb_set_opaque(ldb, "sessionInfo", sess);
+ sid = get_user_sid(module);
+ assert_null(sid);
+
+ /*
+ * Now add an empty security token.
+ */
+ token = talloc_zero(ctx, struct security_token);
+ sess->security_token = token;
+ sid = get_user_sid(module);
+ assert_null(sid);
+
+ /*
+ * Add a single SID
+ */
+ string_to_sid(&sid0, SID0);
+ token->num_sids = 1;
+ token->sids = sids[0];
+ sid = get_user_sid(module);
+ assert_non_null(sid);
+ dom_sid_string_buf(sid, sid_buf, sizeof(sid_buf));
+ assert_string_equal(SID0, sid_buf);
+
+ /*
+ * Add a second SID, should still use the first SID
+ */
+ string_to_sid(&sid1, SID1);
+ token->num_sids = 2;
+ sid = get_user_sid(module);
+ assert_non_null(sid);
+ dom_sid_string_buf(sid, sid_buf, sizeof(sid_buf));
+ assert_string_equal(SID0, sid_buf);
+
+
+ /*
+ * Now test a null sid in the first position
+ */
+ token->num_sids = 1;
+ token->sids = NULL;
+ sid = get_user_sid(module);
+ assert_null(sid);
+
+ TALLOC_FREE(ctx);
+}
+
+static void test_get_unique_session_token(void **state)
+{
+ struct ldb_context *ldb = NULL;
+ struct ldb_module *module = NULL;
+ struct auth_session_info *sess = NULL;
+ const struct GUID *guid;
+ const char * const GUID_S = "7130cb06-2062-6a1b-409e-3514c26b1773";
+ struct GUID in;
+ char *guid_str;
+ struct GUID_txt_buf guid_buff;
+
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ ldb = talloc_zero(ctx, struct ldb_context);
+ module = talloc_zero(ctx, struct ldb_module);
+ module->ldb = ldb;
+
+ /*
+ * Test a freshly initialized ldb
+ * should return NULL
+ */
+ guid = get_unique_session_token(module);
+ assert_null(guid);
+
+ /*
+ * Now add a NULL session info
+ */
+ ldb_set_opaque(ldb, "sessionInfo", NULL);
+ guid = get_unique_session_token(module);
+ assert_null(guid);
+
+ /*
+ * Now add a session info with no session id
+ * Note if the memory has not been zeroed correctly all bets are
+ * probably off.
+ */
+ sess = talloc_zero(ctx, struct auth_session_info);
+ ldb_set_opaque(ldb, "sessionInfo", sess);
+ guid = get_unique_session_token(module);
+ /*
+ * We will get a GUID, but it's contents will be undefined
+ */
+ assert_non_null(guid);
+
+ /*
+ * Now set the session id and confirm that we get it back.
+ */
+ GUID_from_string(GUID_S, &in);
+ sess->unique_session_token = in;
+ guid = get_unique_session_token(module);
+ assert_non_null(guid);
+ guid_str = GUID_buf_string(guid, &guid_buff);
+ assert_string_equal(GUID_S, guid_str);
+
+ TALLOC_FREE(ctx);
+
+}
+
+static void test_get_remote_host(void **state)
+{
+ struct ldb_context *ldb = NULL;
+ char *rh = NULL;
+ struct tsocket_address *in = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ ldb = talloc_zero(ctx, struct ldb_context);
+
+ /*
+ * Test a freshly initialized ldb
+ * should return "Unknown"
+ */
+ rh = get_remote_host(ldb, ctx);
+ assert_string_equal("Unknown", rh);
+ TALLOC_FREE(rh);
+
+ /*
+ * opaque set to null, should return NULL
+ */
+ ldb_set_opaque(ldb, "remoteAddress", NULL);
+ rh = get_remote_host(ldb, ctx);
+ assert_string_equal("Unknown", rh);
+ TALLOC_FREE(rh);
+
+ /*
+ * Ensure that the value set is returned
+ */
+ tsocket_address_inet_from_strings(ctx, "ip", "127.0.0.1", 42, &in);
+ ldb_set_opaque(ldb, "remoteAddress", in);
+ rh = get_remote_host(ldb, ctx);
+ assert_string_equal("ipv4:127.0.0.1:42", rh);
+ TALLOC_FREE(rh);
+
+ TALLOC_FREE(ctx);
+
+}
+
+static void test_get_primary_dn(void **state)
+{
+ struct ldb_request *req = NULL;
+ struct ldb_message *msg = NULL;
+ struct ldb_context *ldb = NULL;
+
+ struct ldb_dn *dn = NULL;
+
+ const char * const DN = "dn=CN=USER,CN=Users,DC=SAMBA,DC=ORG";
+ const char *s = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ req = talloc_zero(ctx, struct ldb_request);
+ msg = talloc_zero(ctx, struct ldb_message);
+ ldb = talloc_zero(ctx, struct ldb_context);
+ dn = ldb_dn_new(ctx, ldb, DN);
+
+ /*
+ * Try an empty request.
+ */
+ s = get_primary_dn(req);
+ assert_null(s);
+
+ /*
+ * Now try an add with a null message.
+ */
+ req->operation = LDB_ADD;
+ req->op.add.message = NULL;
+ s = get_primary_dn(req);
+ assert_null(s);
+
+ /*
+ * Now try an mod with a null message.
+ */
+ req->operation = LDB_MODIFY;
+ req->op.mod.message = NULL;
+ s = get_primary_dn(req);
+ assert_null(s);
+
+ /*
+ * Now try an add with a missing dn
+ */
+ req->operation = LDB_ADD;
+ req->op.add.message = msg;
+ s = get_primary_dn(req);
+ assert_null(s);
+
+ /*
+ * Now try a mod with a messing dn
+ */
+ req->operation = LDB_ADD;
+ req->op.mod.message = msg;
+ s = get_primary_dn(req);
+ assert_null(s);
+
+ /*
+ * Add a dn to the message
+ */
+ msg->dn = dn;
+
+ /*
+ * Now try an add with a dn
+ */
+ req->operation = LDB_ADD;
+ req->op.add.message = msg;
+ s = get_primary_dn(req);
+ assert_non_null(s);
+ assert_string_equal(DN, s);
+
+ /*
+ * Now try a mod with a dn
+ */
+ req->operation = LDB_MODIFY;
+ req->op.mod.message = msg;
+ s = get_primary_dn(req);
+ assert_non_null(s);
+ assert_string_equal(DN, s);
+
+ /*
+ * Try a delete without a dn
+ */
+ req->operation = LDB_DELETE;
+ req->op.del.dn = NULL;
+ s = get_primary_dn(req);
+ assert_null(s);
+
+ /*
+ * Try a delete with a dn
+ */
+ req->operation = LDB_DELETE;
+ req->op.del.dn = dn;
+ s = get_primary_dn(req);
+ assert_non_null(s);
+ assert_string_equal(DN, s);
+
+ /*
+ * Try a rename without a dn
+ */
+ req->operation = LDB_RENAME;
+ req->op.rename.olddn = NULL;
+ s = get_primary_dn(req);
+ assert_null(s);
+
+ /*
+ * Try a rename with a dn
+ */
+ req->operation = LDB_RENAME;
+ req->op.rename.olddn = dn;
+ s = get_primary_dn(req);
+ assert_non_null(s);
+ assert_string_equal(DN, s);
+
+ /*
+ * Try an extended operation, i.e. one that does not have a DN
+ * associated with it for logging purposes.
+ */
+ req->operation = LDB_EXTENDED;
+ s = get_primary_dn(req);
+ assert_null(s);
+
+ TALLOC_FREE(ctx);
+}
+
+static void test_get_message(void **state)
+{
+ struct ldb_request *req = NULL;
+ struct ldb_message *msg = NULL;
+ const struct ldb_message *r = NULL;
+
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ req = talloc_zero(ctx, struct ldb_request);
+ msg = talloc_zero(ctx, struct ldb_message);
+
+ /*
+ * Test an empty message
+ */
+ r = get_message(req);
+ assert_null(r);
+
+ /*
+ * Test an add message
+ */
+ req->operation = LDB_ADD;
+ req->op.add.message = msg;
+ r = get_message(req);
+ assert_ptr_equal(msg, r);
+
+ /*
+ * Test a modify message
+ */
+ req->operation = LDB_MODIFY;
+ req->op.mod.message = msg;
+ r = get_message(req);
+ assert_ptr_equal(msg, r);
+
+ /*
+ * Test a Delete message, i.e. trigger the default case
+ */
+ req->operation = LDB_DELETE;
+ r = get_message(req);
+ assert_null(r);
+
+ TALLOC_FREE(ctx);
+}
+
+static void test_get_secondary_dn(void **state)
+{
+ struct ldb_request *req = NULL;
+ struct ldb_context *ldb = NULL;
+
+ struct ldb_dn *dn = NULL;
+
+ const char * const DN = "dn=CN=USER,CN=Users,DC=SAMBA,DC=ORG";
+ const char *s = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ req = talloc_zero(ctx, struct ldb_request);
+ ldb = talloc_zero(ctx, struct ldb_context);
+ dn = ldb_dn_new(ctx, ldb, DN);
+
+ /*
+ * Try an empty request.
+ */
+ s = get_secondary_dn(req);
+ assert_null(s);
+
+ /*
+ * Try a rename without a dn
+ */
+ req->operation = LDB_RENAME;
+ req->op.rename.newdn = NULL;
+ s = get_secondary_dn(req);
+ assert_null(s);
+
+ /*
+ * Try a rename with a dn
+ */
+ req->operation = LDB_RENAME;
+ req->op.rename.newdn = dn;
+ s = get_secondary_dn(req);
+ assert_non_null(s);
+ assert_string_equal(DN, s);
+
+ /*
+ * Try an extended operation, i.e. one that does not have a DN
+ * associated with it for logging purposes.
+ */
+ req->operation = LDB_EXTENDED;
+ s = get_primary_dn(req);
+ assert_null(s);
+
+ TALLOC_FREE(ctx);
+}
+
+static void test_get_operation_name(void **state)
+{
+ struct ldb_request *req = NULL;
+
+ TALLOC_CTX *ctx = talloc_new(NULL);
+
+ req = talloc_zero(ctx, struct ldb_request);
+
+ req->operation = LDB_SEARCH;
+ assert_string_equal("Search", get_operation_name(req));
+
+ req->operation = LDB_ADD;
+ assert_string_equal("Add", get_operation_name(req));
+
+ req->operation = LDB_MODIFY;
+ assert_string_equal("Modify", get_operation_name(req));
+
+ req->operation = LDB_DELETE;
+ assert_string_equal("Delete", get_operation_name(req));
+
+ req->operation = LDB_RENAME;
+ assert_string_equal("Rename", get_operation_name(req));
+
+ req->operation = LDB_EXTENDED;
+ assert_string_equal("Extended", get_operation_name(req));
+
+ req->operation = LDB_REQ_REGISTER_CONTROL;
+ assert_string_equal("Register Control", get_operation_name(req));
+
+ req->operation = LDB_REQ_REGISTER_PARTITION;
+ assert_string_equal("Register Partition", get_operation_name(req));
+
+ /*
+ * Trigger the default case
+ */
+ req->operation = -1;
+ assert_string_equal("Unknown", get_operation_name(req));
+
+ TALLOC_FREE(ctx);
+}
+
+static void test_get_modification_action(void **state)
+{
+ assert_string_equal(
+ "add",
+ get_modification_action(LDB_FLAG_MOD_ADD));
+ assert_string_equal(
+ "delete",
+ get_modification_action(LDB_FLAG_MOD_DELETE));
+ assert_string_equal(
+ "replace",
+ get_modification_action(LDB_FLAG_MOD_REPLACE));
+ /*
+ * Trigger the default case
+ */
+ assert_string_equal(
+ "unknown",
+ get_modification_action(0));
+}
+
+int main(void) {
+ const struct CMUnitTest tests[] = {
+#ifdef HAVE_JANSSON
+ cmocka_unit_test(test_add_ldb_value),
+ cmocka_unit_test(test_attributes_json),
+#endif
+ cmocka_unit_test(test_get_remote_address),
+ cmocka_unit_test(test_get_ldb_error_string),
+ cmocka_unit_test(test_get_user_sid),
+ cmocka_unit_test(test_get_unique_session_token),
+ cmocka_unit_test(test_get_remote_host),
+ cmocka_unit_test(test_get_primary_dn),
+ cmocka_unit_test(test_get_message),
+ cmocka_unit_test(test_get_secondary_dn),
+ cmocka_unit_test(test_get_operation_name),
+ cmocka_unit_test(test_get_modification_action),
+ };
+
+ cmocka_set_message_output(CM_OUTPUT_SUBUNIT);
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/source4/dsdb/samdb/ldb_modules/wscript_build b/source4/dsdb/samdb/ldb_modules/wscript_build
index 9e0ac28..da21e96 100644
--- a/source4/dsdb/samdb/ldb_modules/wscript_build
+++ b/source4/dsdb/samdb/ldb_modules/wscript_build
@@ -7,9 +7,9 @@ bld.SAMBA_LIBRARY('dsdb-module',
grouping_library=True)
bld.SAMBA_SUBSYSTEM('DSDB_MODULE_HELPERS',
- source='util.c acl_util.c schema_util.c netlogon.c',
+ source='util.c acl_util.c schema_util.c netlogon.c audit_util.c',
autoproto='util_proto.h',
- deps='ldb ndr samdb-common samba-security'
+ deps='ldb ndr samdb-common samba-security audit_logging'
)
bld.SAMBA_SUBSYSTEM('DSDB_MODULE_HELPER_RIDALLOC',
@@ -40,6 +40,30 @@ bld.SAMBA_BINARY('test_encrypted_secrets',
DSDB_MODULE_HELPERS
''',
install=False)
+bld.SAMBA_BINARY('test_audit_util',
+ source='tests/test_audit_util.c',
+ deps='''
+ talloc
+ samba-util
+ samdb-common
+ samdb
+ cmocka
+ audit_logging
+ DSDB_MODULE_HELPERS
+ ''',
+ install=False)
+bld.SAMBA_BINARY('test_audit_log',
+ source='tests/test_audit_log.c',
+ deps='''
+ talloc
+ samba-util
+ samdb-common
+ samdb
+ cmocka
+ audit_logging
+ DSDB_MODULE_HELPERS
+ ''',
+ install=False)
if bld.AD_DC_BUILD_IS_ENABLED():
bld.PROCESS_SEPARATE_RULE("server")
diff --git a/source4/dsdb/samdb/ldb_modules/wscript_build_server b/source4/dsdb/samdb/ldb_modules/wscript_build_server
index 368260a..6c821fb 100644
--- a/source4/dsdb/samdb/ldb_modules/wscript_build_server
+++ b/source4/dsdb/samdb/ldb_modules/wscript_build_server
@@ -425,3 +425,19 @@ bld.SAMBA_MODULE('ldb_encrypted_secrets',
gnutls
'''
)
+
+bld.SAMBA_MODULE('ldb_audit_log',
+ source='audit_log.c',
+ subsystem='ldb',
+ init_function='ldb_audit_log_module_init',
+ module_init_name='ldb_init_module',
+ internal_module=False,
+ deps='''
+ audit_logging
+ talloc
+ samba-util
+ samdb-common
+ DSDB_MODULE_HELPERS
+ samdb
+ '''
+ )
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index 129901c..5b943ab 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -1054,3 +1054,7 @@ plantestsuite("samba4.dsdb.samdb.ldb_modules.encrypted_secrets", "none",
[os.path.join(bindir(), "test_encrypted_secrets")])
plantestsuite("lib.audit_logging.audit_logging", "none",
[os.path.join(bindir(), "audit_logging_test")])
+plantestsuite("samba4.dsdb.samdb.ldb_modules.audit_util", "none",
+ [os.path.join(bindir(), "test_audit_util")])
+plantestsuite("samba4.dsdb.samdb.ldb_modules.audit_log", "none",
+ [os.path.join(bindir(), "test_audit_log")])
--
2.7.4
From ff9abe1393fc839afb35ffc1a15dc18ea9d6e6bc Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Wed, 4 Apr 2018 12:38:25 +1200
Subject: [PATCH 11/21] cldap: set the remote ip address
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
source4/cldap_server/rootdse.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/source4/cldap_server/rootdse.c b/source4/cldap_server/rootdse.c
index 3f389ce..8c616ad 100644
--- a/source4/cldap_server/rootdse.c
+++ b/source4/cldap_server/rootdse.c
@@ -166,6 +166,8 @@ void cldapd_rootdse_request(struct cldap_socket *cldap,
cldapd_rootdse_fill(cldapd, tmp_ctx, search, &reply.response,
reply.result);
+ ldb_set_opaque(cldapd->samctx, "remoteAddress", NULL);
+
status = cldap_reply_send(cldap, &reply);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(2,("cldap rootdse query failed '%s' - %s\n",
--
2.7.4
From 5dda3a3c38750ad1c427ef64cf4edb2f1fcf1779 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Wed, 4 Apr 2018 12:39:55 +1200
Subject: [PATCH 12/21] dsdb pass the remote address to samdb connect
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
source3/passdb/pdb_samba_dsdb.c | 2 +-
source4/dns_server/dlz_bind9.c | 2 +-
source4/dsdb/samdb/samdb.c | 37 +++++++++++++++++++++++++++++--------
source4/dsdb/samdb/samdb.h | 2 +-
source4/ldap_server/ldap_backend.c | 9 +++++----
source4/torture/dns/dlz_bind9.c | 4 +++-
6 files changed, 40 insertions(+), 16 deletions(-)
diff --git a/source3/passdb/pdb_samba_dsdb.c b/source3/passdb/pdb_samba_dsdb.c
index 05052a6..f48f1fd 100644
--- a/source3/passdb/pdb_samba_dsdb.c
+++ b/source3/passdb/pdb_samba_dsdb.c
@@ -3893,7 +3893,7 @@ static NTSTATUS pdb_init_samba_dsdb(struct pdb_methods **pdb_method,
state->lp_ctx,
system_session(state->lp_ctx),
0, location,
- &state->ldb, &errstring);
+ NULL, &state->ldb, &errstring);
if (!state->ldb) {
DEBUG(0, ("samdb_connect failed: %s: %s\n",
diff --git a/source4/dns_server/dlz_bind9.c b/source4/dns_server/dlz_bind9.c
index cf171cb..17afd1f 100644
--- a/source4/dns_server/dlz_bind9.c
+++ b/source4/dns_server/dlz_bind9.c
@@ -706,7 +706,7 @@ _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
ret = samdb_connect_url(state, state->ev_ctx, state->lp,
system_session(state->lp), 0,
state->options.url,
- &state->samdb, &errstring);
+ NULL, &state->samdb, &errstring);
if (ret != LDB_SUCCESS) {
state->log(ISC_LOG_ERROR,
"samba_dlz: Failed to connect to %s: %s",
diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c
index 1f80267..7a1bb79 100644
--- a/source4/dsdb/samdb/samdb.c
+++ b/source4/dsdb/samdb/samdb.c
@@ -42,6 +42,7 @@
#include "auth/credentials/credentials.h"
#include "param/secrets.h"
#include "auth/auth.h"
+#include "lib/tsocket/tsocket.h"
/*
connect to the SAM database specified by URL
@@ -51,7 +52,9 @@ int samdb_connect_url(TALLOC_CTX *mem_ctx,
struct tevent_context *ev_ctx,
struct loadparm_context *lp_ctx,
struct auth_session_info *session_info,
- unsigned int flags, const char *url,
+ unsigned int flags,
+ const char *url,
+ struct tsocket_address *remote_address,
struct ldb_context **ldb_ret,
char **errstring)
{
@@ -59,13 +62,17 @@ int samdb_connect_url(TALLOC_CTX *mem_ctx,
int ret;
*ldb_ret = NULL;
*errstring = NULL;
- ldb = ldb_wrap_find(url, ev_ctx, lp_ctx, session_info, NULL, flags);
- if (ldb != NULL) {
- *ldb_ret = talloc_reference(mem_ctx, ldb);
- if (*ldb_ret == NULL) {
- return LDB_ERR_OPERATIONS_ERROR;
+
+ if (remote_address == NULL) {
+ ldb = ldb_wrap_find(url, ev_ctx, lp_ctx,
+ session_info, NULL, flags);
+ if (ldb != NULL) {
+ *ldb_ret = talloc_reference(mem_ctx, ldb);
+ if (*ldb_ret == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ return LDB_SUCCESS;
}
- return LDB_SUCCESS;
}
ldb = samba_ldb_init(mem_ctx, ev_ctx, lp_ctx, session_info, NULL);
@@ -91,6 +98,20 @@ int samdb_connect_url(TALLOC_CTX *mem_ctx,
return LDB_ERR_OPERATIONS_ERROR;
}
+ /*
+ * If a remote_address was specified, then set it on the DB
+ * and do not add to the wrap list (as we need to keep the LDB
+ * pointer unique for the address).
+ *
+ * We use this for audit logging and for the "netlogon" attribute
+ */
+ if (remote_address != NULL) {
+ ldb_set_opaque(ldb, "remoteAddress",
+ remote_address);
+ *ldb_ret = ldb;
+ return LDB_SUCCESS;
+ }
+
if (!ldb_wrap_add(url, ev_ctx, lp_ctx, session_info, NULL, flags, ldb)) {
*errstring = talloc_asprintf(mem_ctx,
"Failed to add cached DB reference"
@@ -118,7 +139,7 @@ struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx,
char *errstring;
struct ldb_context *ldb;
int ret = samdb_connect_url(mem_ctx, ev_ctx, lp_ctx, session_info, flags,
- "sam.ldb", &ldb, &errstring);
+ "sam.ldb", NULL, &ldb, &errstring);
if (ret == LDB_SUCCESS) {
return ldb;
}
diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h
index a095858..d2686af 100644
--- a/source4/dsdb/samdb/samdb.h
+++ b/source4/dsdb/samdb/samdb.h
@@ -28,7 +28,7 @@ struct dsdb_extended_replicated_object;
struct dsdb_extended_replicated_objects;
struct loadparm_context;
struct tevent_context;
-
+struct tsocket_address;
struct dsdb_trust_routing_table;
#include "librpc/gen_ndr/security.h"
diff --git a/source4/ldap_server/ldap_backend.c b/source4/ldap_server/ldap_backend.c
index 95c7ee7..39f1aa2 100644
--- a/source4/ldap_server/ldap_backend.c
+++ b/source4/ldap_server/ldap_backend.c
@@ -31,6 +31,7 @@
#include <ldb_errors.h>
#include <ldb_module.h>
#include "ldb_wrap.h"
+#include "lib/tsocket/tsocket.h"
static int map_ldb_error(TALLOC_CTX *mem_ctx, int ldb_err,
const char *add_err_string, const char **errstring)
@@ -188,7 +189,10 @@ int ldapsrv_backend_Init(struct ldapsrv_connection *conn,
conn->lp_ctx,
conn->session_info,
conn->global_catalog ? LDB_FLG_RDONLY : 0,
- "sam.ldb", &conn->ldb, errstring);
+ "sam.ldb",
+ conn->connection->remote_address,
+ &conn->ldb,
+ errstring);
if (ret != LDB_SUCCESS) {
return ret;
}
@@ -229,9 +233,6 @@ int ldapsrv_backend_Init(struct ldapsrv_connection *conn,
ldb_set_opaque(conn->ldb, "supportedSASLMechanisms", sasl_mechs);
}
- ldb_set_opaque(conn->ldb, "remoteAddress",
- conn->connection->remote_address);
-
return LDB_SUCCESS;
}
diff --git a/source4/torture/dns/dlz_bind9.c b/source4/torture/dns/dlz_bind9.c
index 42b104e..d948487 100644
--- a/source4/torture/dns/dlz_bind9.c
+++ b/source4/torture/dns/dlz_bind9.c
@@ -92,7 +92,9 @@ static isc_result_t dlz_bind9_writeable_zone_hook(dns_view_t *view,
system_session(tctx->lp_ctx),
0,
test_dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"),
- &samdb, &errstring);
+ NULL,
+ &samdb,
+ &errstring);
struct ldb_message *msg;
const char *attrs[] = {
NULL
--
2.7.4
From 55e6a08106f1b7c90a2273ad52f74ebfc6e25bdb Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Thu, 12 Apr 2018 06:41:30 +1200
Subject: [PATCH 13/21] samdb: Add remote address to connect
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
source3/modules/vfs_dfs_samba4.c | 4 +-
source3/passdb/pdb_samba_dsdb.c | 7 +-
source4/auth/ntlm/auth.c | 7 +-
source4/cldap_server/cldap_server.c | 7 +-
source4/dns_server/dlz_bind9.c | 11 +-
source4/dns_server/dns_server.c | 8 +-
source4/dsdb/dns/dns_update.c | 8 +-
source4/dsdb/kcc/kcc_service.c | 7 +-
source4/dsdb/repl/drepl_service.c | 7 +-
source4/dsdb/samdb/samdb.c | 16 ++-
source4/kdc/db-glue.c | 8 +-
source4/kdc/kdc-heimdal.c | 8 +-
source4/kdc/kdc-service-mit.c | 1 +
source4/kdc/kpasswd-helper.c | 1 +
source4/kdc/kpasswd_glue.c | 8 +-
source4/ldap_server/ldap_server.c | 8 +-
source4/libnet/libnet_samsync_ldb.c | 5 +-
source4/nbt_server/nbt_server.c | 7 +-
source4/ntp_signd/ntp_signd.c | 7 +-
source4/rpc_server/backupkey/dcesrv_backupkey.c | 7 +-
.../backupkey/dcesrv_backupkey_heimdal.c | 7 +-
source4/rpc_server/common/server_info.c | 8 +-
source4/rpc_server/dnsserver/dcerpc_dnsserver.c | 8 +-
source4/rpc_server/drsuapi/dcesrv_drsuapi.c | 20 +++-
source4/rpc_server/lsa/dcesrv_lsa.c | 22 +++-
source4/rpc_server/lsa/lsa_init.c | 7 +-
source4/rpc_server/netlogon/dcerpc_netlogon.c | 124 ++++++++++++++++-----
source4/rpc_server/samr/dcesrv_samr.c | 16 ++-
source4/rpc_server/samr/samr_password.c | 28 +++--
source4/smb_server/smb/trans2.c | 7 +-
source4/smbd/server.c | 9 +-
source4/torture/dns/dlz_bind9.c | 17 +--
source4/torture/gpo/apply.c | 8 +-
source4/torture/libnet/libnet_BecomeDC.c | 7 +-
source4/winbind/idmap.c | 7 +-
35 files changed, 332 insertions(+), 105 deletions(-)
diff --git a/source3/modules/vfs_dfs_samba4.c b/source3/modules/vfs_dfs_samba4.c
index d7f4edf..1c7b50e 100644
--- a/source3/modules/vfs_dfs_samba4.c
+++ b/source3/modules/vfs_dfs_samba4.c
@@ -76,7 +76,9 @@ static int dfs_samba4_connect(struct vfs_handle_struct *handle,
data->sam_ctx = samdb_connect(data,
data->ev,
data->lp_ctx,
- system_session(data->lp_ctx), 0);
+ system_session(data->lp_ctx),
+ NULL,
+ 0);
if (!data->sam_ctx) {
DEBUG(0, ("samdb_connect failed\n"));
SMB_VFS_NEXT_DISCONNECT(handle);
diff --git a/source3/passdb/pdb_samba_dsdb.c b/source3/passdb/pdb_samba_dsdb.c
index f48f1fd..957524d 100644
--- a/source3/passdb/pdb_samba_dsdb.c
+++ b/source3/passdb/pdb_samba_dsdb.c
@@ -3892,8 +3892,11 @@ static NTSTATUS pdb_init_samba_dsdb(struct pdb_methods **pdb_method,
state->ev,
state->lp_ctx,
system_session(state->lp_ctx),
- 0, location,
- NULL, &state->ldb, &errstring);
+ 0,
+ location,
+ NULL,
+ &state->ldb,
+ &errstring);
if (!state->ldb) {
DEBUG(0, ("samdb_connect failed: %s: %s\n",
diff --git a/source4/auth/ntlm/auth.c b/source4/auth/ntlm/auth.c
index 7e10a55..1293b28 100644
--- a/source4/auth/ntlm/auth.c
+++ b/source4/auth/ntlm/auth.c
@@ -713,7 +713,12 @@ _PUBLIC_ NTSTATUS auth_context_create_methods(TALLOC_CTX *mem_ctx, const char *
if (sam_ctx) {
ctx->sam_ctx = sam_ctx;
} else {
- ctx->sam_ctx = samdb_connect(ctx, ctx->event_ctx, ctx->lp_ctx, system_session(ctx->lp_ctx), 0);
+ ctx->sam_ctx = samdb_connect(ctx,
+ ctx->event_ctx,
+ ctx->lp_ctx,
+ system_session(ctx->lp_ctx),
+ NULL,
+ 0);
}
for (i=0; methods && methods[i] ; i++) {
diff --git a/source4/cldap_server/cldap_server.c b/source4/cldap_server/cldap_server.c
index a0cde77..d93ac0b 100644
--- a/source4/cldap_server/cldap_server.c
+++ b/source4/cldap_server/cldap_server.c
@@ -221,7 +221,12 @@ static void cldapd_task_init(struct task_server *task)
}
cldapd->task = task;
- cldapd->samctx = samdb_connect(cldapd, task->event_ctx, task->lp_ctx, system_session(task->lp_ctx), 0);
+ cldapd->samctx = samdb_connect(cldapd,
+ task->event_ctx,
+ task->lp_ctx,
+ system_session(task->lp_ctx),
+ NULL,
+ 0);
if (cldapd->samctx == NULL) {
task_server_terminate(task, "cldapd failed to open samdb", true);
return;
diff --git a/source4/dns_server/dlz_bind9.c b/source4/dns_server/dlz_bind9.c
index 17afd1f..9cb85f6 100644
--- a/source4/dns_server/dlz_bind9.c
+++ b/source4/dns_server/dlz_bind9.c
@@ -703,10 +703,15 @@ _PUBLIC_ isc_result_t dlz_create(const char *dlzname,
}
}
- ret = samdb_connect_url(state, state->ev_ctx, state->lp,
- system_session(state->lp), 0,
+ ret = samdb_connect_url(state,
+ state->ev_ctx,
+ state->lp,
+ system_session(state->lp),
+ 0,
state->options.url,
- NULL, &state->samdb, &errstring);
+ NULL,
+ &state->samdb,
+ &errstring);
if (ret != LDB_SUCCESS) {
state->log(ISC_LOG_ERROR,
"samba_dlz: Failed to connect to %s: %s",
diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c
index 5c6aca5..9db1133 100644
--- a/source4/dns_server/dns_server.c
+++ b/source4/dns_server/dns_server.c
@@ -838,8 +838,12 @@ static void dns_task_init(struct task_server *task)
return;
}
- dns->samdb = samdb_connect(dns, dns->task->event_ctx, dns->task->lp_ctx,
- system_session(dns->task->lp_ctx), 0);
+ dns->samdb = samdb_connect(dns,
+ dns->task->event_ctx,
+ dns->task->lp_ctx,
+ system_session(dns->task->lp_ctx),
+ NULL,
+ 0);
if (!dns->samdb) {
task_server_terminate(task, "dns: samdb_connect failed", true);
return;
diff --git a/source4/dsdb/dns/dns_update.c b/source4/dsdb/dns/dns_update.c
index 10be7ce..0655673 100644
--- a/source4/dsdb/dns/dns_update.c
+++ b/source4/dsdb/dns/dns_update.c
@@ -660,8 +660,12 @@ static void dnsupdate_task_init(struct task_server *task)
return;
}
- service->samdb = samdb_connect(service, service->task->event_ctx, task->lp_ctx,
- service->system_session_info, 0);
+ service->samdb = samdb_connect(service,
+ service->task->event_ctx,
+ task->lp_ctx,
+ service->system_session_info,
+ NULL,
+ 0);
if (!service->samdb) {
task_server_terminate(task, "dnsupdate: Failed to connect to local samdb\n",
true);
diff --git a/source4/dsdb/kcc/kcc_service.c b/source4/dsdb/kcc/kcc_service.c
index a5508af..7642378 100644
--- a/source4/dsdb/kcc/kcc_service.c
+++ b/source4/dsdb/kcc/kcc_service.c
@@ -56,7 +56,12 @@ static WERROR kccsrv_connect_samdb(struct kccsrv_service *service, struct loadpa
{
const struct GUID *ntds_guid;
- service->samdb = samdb_connect(service, service->task->event_ctx, lp_ctx, service->system_session_info, 0);
+ service->samdb = samdb_connect(service,
+ service->task->event_ctx,
+ lp_ctx,
+ service->system_session_info,
+ NULL,
+ 0);
if (!service->samdb) {
return WERR_DS_UNAVAILABLE;
}
diff --git a/source4/dsdb/repl/drepl_service.c b/source4/dsdb/repl/drepl_service.c
index 8f16a2e..10772d4 100644
--- a/source4/dsdb/repl/drepl_service.c
+++ b/source4/dsdb/repl/drepl_service.c
@@ -68,7 +68,12 @@ static WERROR dreplsrv_connect_samdb(struct dreplsrv_service *service, struct lo
const struct GUID *ntds_guid;
struct drsuapi_DsBindInfo28 *bind_info28;
- service->samdb = samdb_connect(service, service->task->event_ctx, lp_ctx, service->system_session_info, 0);
+ service->samdb = samdb_connect(service,
+ service->task->event_ctx,
+ lp_ctx,
+ service->system_session_info,
+ NULL,
+ 0);
if (!service->samdb) {
return WERR_DS_UNAVAILABLE;
}
diff --git a/source4/dsdb/samdb/samdb.c b/source4/dsdb/samdb/samdb.c
index 7a1bb79..10db0c5 100644
--- a/source4/dsdb/samdb/samdb.c
+++ b/source4/dsdb/samdb/samdb.c
@@ -54,7 +54,7 @@ int samdb_connect_url(TALLOC_CTX *mem_ctx,
struct auth_session_info *session_info,
unsigned int flags,
const char *url,
- struct tsocket_address *remote_address,
+ const struct tsocket_address *remote_address,
struct ldb_context **ldb_ret,
char **errstring)
{
@@ -107,7 +107,7 @@ int samdb_connect_url(TALLOC_CTX *mem_ctx,
*/
if (remote_address != NULL) {
ldb_set_opaque(ldb, "remoteAddress",
- remote_address);
+ discard_const(remote_address));
*ldb_ret = ldb;
return LDB_SUCCESS;
}
@@ -134,12 +134,20 @@ struct ldb_context *samdb_connect(TALLOC_CTX *mem_ctx,
struct tevent_context *ev_ctx,
struct loadparm_context *lp_ctx,
struct auth_session_info *session_info,
+ const struct tsocket_address *remote_address,
unsigned int flags)
{
char *errstring;
struct ldb_context *ldb;
- int ret = samdb_connect_url(mem_ctx, ev_ctx, lp_ctx, session_info, flags,
- "sam.ldb", NULL, &ldb, &errstring);
+ int ret = samdb_connect_url(mem_ctx,
+ ev_ctx,
+ lp_ctx,
+ session_info,
+ flags,
+ "sam.ldb",
+ remote_address,
+ &ldb,
+ &errstring);
if (ret == LDB_SUCCESS) {
return ldb;
}
diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
index c2dd236..bb428e4 100644
--- a/source4/kdc/db-glue.c
+++ b/source4/kdc/db-glue.c
@@ -2774,8 +2774,12 @@ NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_conte
}
/* Setup the link to LDB */
- kdc_db_ctx->samdb = samdb_connect(kdc_db_ctx, base_ctx->ev_ctx,
- base_ctx->lp_ctx, session_info, 0);
+ kdc_db_ctx->samdb = samdb_connect(kdc_db_ctx,
+ base_ctx->ev_ctx,
+ base_ctx->lp_ctx,
+ session_info,
+ NULL,
+ 0);
if (kdc_db_ctx->samdb == NULL) {
DEBUG(1, ("samba_kdc_setup_db_ctx: Cannot open samdb for KDC backend!"));
talloc_free(kdc_db_ctx);
diff --git a/source4/kdc/kdc-heimdal.c b/source4/kdc/kdc-heimdal.c
index fcc1eb0..83ace26 100644
--- a/source4/kdc/kdc-heimdal.c
+++ b/source4/kdc/kdc-heimdal.c
@@ -305,8 +305,12 @@ static void kdc_task_init(struct task_server *task)
/* get a samdb connection */
- kdc->samdb = samdb_connect(kdc, kdc->task->event_ctx, kdc->task->lp_ctx,
- system_session(kdc->task->lp_ctx), 0);
+ kdc->samdb = samdb_connect(kdc,
+ kdc->task->event_ctx,
+ kdc->task->lp_ctx,
+ system_session(kdc->task->lp_ctx),
+ NULL,
+ 0);
if (!kdc->samdb) {
DEBUG(1,("kdc_task_init: unable to connect to samdb\n"));
task_server_terminate(task, "kdc: krb5_init_context samdb connect failed", true);
diff --git a/source4/kdc/kdc-service-mit.c b/source4/kdc/kdc-service-mit.c
index e5b20ff..1d28fc4 100644
--- a/source4/kdc/kdc-service-mit.c
+++ b/source4/kdc/kdc-service-mit.c
@@ -303,6 +303,7 @@ void mitkdc_task_init(struct task_server *task)
kdc->task->event_ctx,
kdc->task->lp_ctx,
system_session(kdc->task->lp_ctx),
+ NULL,
0);
if (kdc->samdb == NULL) {
task_server_terminate(task,
diff --git a/source4/kdc/kpasswd-helper.c b/source4/kdc/kpasswd-helper.c
index 6de2837..995f548 100644
--- a/source4/kdc/kpasswd-helper.c
+++ b/source4/kdc/kpasswd-helper.c
@@ -180,6 +180,7 @@ NTSTATUS kpasswd_samdb_set_password(TALLOC_CTX *mem_ctx,
event_ctx,
lp_ctx,
session_info,
+ NULL,
0);
if (samdb == NULL) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
diff --git a/source4/kdc/kpasswd_glue.c b/source4/kdc/kpasswd_glue.c
index b12a209..0addfdf 100644
--- a/source4/kdc/kpasswd_glue.c
+++ b/source4/kdc/kpasswd_glue.c
@@ -79,8 +79,12 @@ NTSTATUS samdb_kpasswd_change_password(TALLOC_CTX *mem_ctx,
}
/* Start a SAM with user privileges for the password change */
- samdb = samdb_connect(mem_ctx, event_ctx, lp_ctx,
- session_info, 0);
+ samdb = samdb_connect(mem_ctx,
+ event_ctx,
+ lp_ctx,
+ session_info,
+ NULL,
+ 0);
if (!samdb) {
*error_string = "Failed to open samdb";
return NT_STATUS_ACCESS_DENIED;
diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c
index 91e5d05..80b8c19 100644
--- a/source4/ldap_server/ldap_server.c
+++ b/source4/ldap_server/ldap_server.c
@@ -1055,8 +1055,12 @@ static NTSTATUS add_socket(struct task_server *task,
}
/* Load LDAP database, but only to read our settings */
- ldb = samdb_connect(ldap_service, ldap_service->task->event_ctx,
- lp_ctx, system_session(lp_ctx), 0);
+ ldb = samdb_connect(ldap_service,
+ ldap_service->task->event_ctx,
+ lp_ctx,
+ system_session(lp_ctx),
+ NULL,
+ 0);
if (!ldb) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
diff --git a/source4/libnet/libnet_samsync_ldb.c b/source4/libnet/libnet_samsync_ldb.c
index 5fdef79..9ea7dce 100644
--- a/source4/libnet/libnet_samsync_ldb.c
+++ b/source4/libnet/libnet_samsync_ldb.c
@@ -1245,11 +1245,12 @@ NTSTATUS libnet_samsync_ldb(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, str
state->secrets = NULL;
state->trusted_domains = NULL;
- state->sam_ldb = samdb_connect(mem_ctx,
+ state->sam_ldb = samdb_connect(mem_ctx,
ctx->event_ctx,
ctx->lp_ctx,
r->in.session_info,
- 0);
+ NULL,
+ 0);
if (!state->sam_ldb) {
return NT_STATUS_INTERNAL_DB_ERROR;
}
diff --git a/source4/nbt_server/nbt_server.c b/source4/nbt_server/nbt_server.c
index 834c72f..6bdf9b4 100644
--- a/source4/nbt_server/nbt_server.c
+++ b/source4/nbt_server/nbt_server.c
@@ -73,7 +73,12 @@ static void nbtd_task_init(struct task_server *task)
return;
}
- nbtsrv->sam_ctx = samdb_connect(nbtsrv, task->event_ctx, task->lp_ctx, system_session(task->lp_ctx), 0);
+ nbtsrv->sam_ctx = samdb_connect(nbtsrv,
+ task->event_ctx,
+ task->lp_ctx,
+ system_session(task->lp_ctx),
+ NULL,
+ 0);
if (nbtsrv->sam_ctx == NULL) {
task_server_terminate(task, "nbtd failed to open samdb", true);
return;
diff --git a/source4/ntp_signd/ntp_signd.c b/source4/ntp_signd/ntp_signd.c
index e09c693..508adcf 100644
--- a/source4/ntp_signd/ntp_signd.c
+++ b/source4/ntp_signd/ntp_signd.c
@@ -515,7 +515,12 @@ static void ntp_signd_task_init(struct task_server *task)
ntp_signd->task = task;
/* Must be system to get at the password hashes */
- ntp_signd->samdb = samdb_connect(ntp_signd, task->event_ctx, task->lp_ctx, system_session(task->lp_ctx), 0);
+ ntp_signd->samdb = samdb_connect(ntp_signd,
+ task->event_ctx,
+ task->lp_ctx,
+ system_session(task->lp_ctx),
+ NULL,
+ 0);
if (ntp_signd->samdb == NULL) {
task_server_terminate(task, "ntp_signd failed to open samdb", true);
return;
diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey.c b/source4/rpc_server/backupkey/dcesrv_backupkey.c
index cf9af1f..d2c3f2a 100644
--- a/source4/rpc_server/backupkey/dcesrv_backupkey.c
+++ b/source4/rpc_server/backupkey/dcesrv_backupkey.c
@@ -1774,9 +1774,12 @@ static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call,
return WERR_NOT_SUPPORTED;
}
- ldb_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ ldb_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
dce_call->conn->dce_ctx->lp_ctx,
- system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (samdb_rodc(ldb_ctx, &is_rodc) != LDB_SUCCESS) {
talloc_unlink(mem_ctx, ldb_ctx);
diff --git a/source4/rpc_server/backupkey/dcesrv_backupkey_heimdal.c b/source4/rpc_server/backupkey/dcesrv_backupkey_heimdal.c
index 5985d52..d2fc480 100644
--- a/source4/rpc_server/backupkey/dcesrv_backupkey_heimdal.c
+++ b/source4/rpc_server/backupkey/dcesrv_backupkey_heimdal.c
@@ -1814,9 +1814,12 @@ static WERROR dcesrv_bkrp_BackupKey(struct dcesrv_call_state *dce_call,
return WERR_NOT_SUPPORTED;
}
- ldb_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ ldb_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
dce_call->conn->dce_ctx->lp_ctx,
- system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (samdb_rodc(ldb_ctx, &is_rodc) != LDB_SUCCESS) {
talloc_unlink(mem_ctx, ldb_ctx);
diff --git a/source4/rpc_server/common/server_info.c b/source4/rpc_server/common/server_info.c
index 39c75cc..0aabcda 100644
--- a/source4/rpc_server/common/server_info.c
+++ b/source4/rpc_server/common/server_info.c
@@ -83,7 +83,13 @@ uint32_t dcesrv_common_get_server_type(TALLOC_CTX *mem_ctx, struct tevent_contex
break;
}
/* open main ldb */
- samctx = samdb_connect(tmp_ctx, event_ctx, dce_ctx->lp_ctx, anonymous_session(tmp_ctx, dce_ctx->lp_ctx), 0);
+ samctx = samdb_connect(
+ tmp_ctx,
+ event_ctx,
+ dce_ctx->lp_ctx,
+ anonymous_session(tmp_ctx, dce_ctx->lp_ctx),
+ NULL,
+ 0);
if (samctx == NULL) {
DEBUG(2,("Unable to open samdb in determining server announce flags\n"));
} else {
diff --git a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c
index 5d3cb9e..b9ed3dd 100644
--- a/source4/rpc_server/dnsserver/dcerpc_dnsserver.c
+++ b/source4/rpc_server/dnsserver/dcerpc_dnsserver.c
@@ -119,8 +119,12 @@ static struct dnsserver_state *dnsserver_connect(struct dcesrv_call_state *dce_c
dsstate->lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
/* FIXME: create correct auth_session_info for connecting user */
- dsstate->samdb = samdb_connect(dsstate, dce_call->event_ctx, dsstate->lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ dsstate->samdb = samdb_connect(dsstate,
+ dce_call->event_ctx,
+ dsstate->lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (dsstate->samdb == NULL) {
DEBUG(0,("dnsserver: Failed to open samdb"));
goto failed;
diff --git a/source4/rpc_server/drsuapi/dcesrv_drsuapi.c b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
index 026098d..250b4c7 100644
--- a/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
+++ b/source4/rpc_server/drsuapi/dcesrv_drsuapi.c
@@ -96,8 +96,13 @@ static WERROR dcesrv_drsuapi_DsBind(struct dcesrv_call_state *dce_call, TALLOC_C
/*
* connect to the samdb
*/
- b_state->sam_ctx = samdb_connect(b_state, dce_call->event_ctx,
- dce_call->conn->dce_ctx->lp_ctx, auth_info, 0);
+ b_state->sam_ctx = samdb_connect(
+ b_state,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ auth_info,
+ dce_call->conn->remote_address,
+ 0);
if (!b_state->sam_ctx) {
return WERR_FOOBAR;
}
@@ -110,9 +115,14 @@ static WERROR dcesrv_drsuapi_DsBind(struct dcesrv_call_state *dce_call, TALLOC_C
werr = drs_security_level_check(dce_call, NULL, SECURITY_RO_DOMAIN_CONTROLLER,
samdb_domain_sid(b_state->sam_ctx));
if (W_ERROR_IS_OK(werr)) {
- b_state->sam_ctx_system = samdb_connect(b_state, dce_call->event_ctx,
- dce_call->conn->dce_ctx->lp_ctx,
- system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ b_state->sam_ctx_system
+ = samdb_connect(
+ b_state,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (!b_state->sam_ctx_system) {
return WERR_FOOBAR;
}
diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c
index 9371bee..8c540ab 100644
--- a/source4/rpc_server/lsa/dcesrv_lsa.c
+++ b/source4/rpc_server/lsa/dcesrv_lsa.c
@@ -3180,6 +3180,7 @@ static NTSTATUS dcesrv_lsa_CreateSecret(struct dcesrv_call_state *dce_call, TALL
struct lsa_secret_state *secret_state;
struct dcesrv_handle *handle;
struct ldb_message **msgs, *msg;
+ struct ldb_context *samdb = NULL;
const char *attrs[] = {
NULL
};
@@ -3233,8 +3234,14 @@ static NTSTATUS dcesrv_lsa_CreateSecret(struct dcesrv_call_state *dce_call, TALL
/* We need to connect to the database as system, as this is one
* of the rare RPC calls that must read the secrets (and this
* is denied otherwise) */
- secret_state->sam_ldb = talloc_reference(secret_state,
- samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx), 0));
+ samdb = samdb_connect(
+ mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
+ secret_state->sam_ldb = talloc_reference(secret_state, samdb);
NT_STATUS_HAVE_NO_MEMORY(secret_state->sam_ldb);
/* search for the secret record */
@@ -3337,6 +3344,7 @@ static NTSTATUS dcesrv_lsa_OpenSecret(struct dcesrv_call_state *dce_call, TALLOC
struct lsa_secret_state *secret_state;
struct dcesrv_handle *handle;
struct ldb_message **msgs;
+ struct ldb_context *samdb = NULL;
const char *attrs[] = {
NULL
};
@@ -3372,8 +3380,14 @@ static NTSTATUS dcesrv_lsa_OpenSecret(struct dcesrv_call_state *dce_call, TALLOC
if (strncmp("G$", r->in.name.string, 2) == 0) {
name = &r->in.name.string[2];
/* We need to connect to the database as system, as this is one of the rare RPC calls that must read the secrets (and this is denied otherwise) */
- secret_state->sam_ldb = talloc_reference(secret_state,
- samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx), 0));
+ samdb = samdb_connect(
+ mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
+ secret_state->sam_ldb = talloc_reference(secret_state, samdb);
secret_state->global = true;
if (strlen(name) < 1) {
diff --git a/source4/rpc_server/lsa/lsa_init.c b/source4/rpc_server/lsa/lsa_init.c
index 5628c5b..5602294 100644
--- a/source4/rpc_server/lsa/lsa_init.c
+++ b/source4/rpc_server/lsa/lsa_init.c
@@ -70,7 +70,12 @@ NTSTATUS dcesrv_lsa_get_policy_state(struct dcesrv_call_state *dce_call,
}
/* make sure the sam database is accessible */
- state->sam_ldb = samdb_connect(state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info, 0);
+ state->sam_ldb = samdb_connect(state,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (state->sam_ldb == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
diff --git a/source4/rpc_server/netlogon/dcerpc_netlogon.c b/source4/rpc_server/netlogon/dcerpc_netlogon.c
index 638dfc2..c19d33c 100644
--- a/source4/rpc_server/netlogon/dcerpc_netlogon.c
+++ b/source4/rpc_server/netlogon/dcerpc_netlogon.c
@@ -291,8 +291,12 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3_helper(
return NT_STATUS_INVALID_PARAMETER;
}
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx,
- system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
@@ -699,7 +703,12 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet(struct dcesrv_call_state *dce_call
&creds);
NT_STATUS_NOT_OK_RETURN(nt_status);
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
@@ -759,7 +768,12 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
&creds);
NT_STATUS_NOT_OK_RETURN(nt_status);
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
@@ -1583,9 +1597,12 @@ static WERROR dcesrv_netr_GetDcName(struct dcesrv_call_state *dce_call, TALLOC_C
*/
}
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
dce_call->conn->dce_ctx->lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return WERR_DS_UNAVAILABLE;
}
@@ -1785,8 +1802,13 @@ static WERROR dcesrv_netr_LogonControl_base_call(struct dcesrv_netr_LogonControl
if (!ok) {
struct ldb_context *sam_ctx;
- sam_ctx = samdb_connect(state, state->dce_call->event_ctx,
- lp_ctx, system_session(lp_ctx), 0);
+ sam_ctx = samdb_connect(
+ state,
+ state->dce_call->event_ctx,
+ lp_ctx,
+ system_session(lp_ctx),
+ state->dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return WERR_DS_UNAVAILABLE;
}
@@ -1996,8 +2018,12 @@ static WERROR dcesrv_netr_GetAnyDCName(struct dcesrv_call_state *dce_call, TALLO
r->in.domainname = lpcfg_workgroup(lp_ctx);
}
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return WERR_DS_UNAVAILABLE;
}
@@ -2142,8 +2168,12 @@ static WERROR dcesrv_netr_DsRGetSiteName(struct dcesrv_call_state *dce_call, TAL
struct ldb_context *sam_ctx;
struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return WERR_DS_UNAVAILABLE;
}
@@ -2265,9 +2295,12 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
}
NT_STATUS_NOT_OK_RETURN(status);
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
dce_call->conn->dce_ctx->lp_ctx,
- system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
@@ -2662,9 +2695,12 @@ static NTSTATUS dcesrv_netr_NetrLogonSendToSam(struct dcesrv_call_state *dce_cal
return NT_STATUS_INVALID_PARAMETER;
}
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
dce_call->conn->dce_ctx->lp_ctx,
- system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
@@ -2788,8 +2824,12 @@ static WERROR dcesrv_netr_DsRGetDCName_base_call(struct dcesrv_netr_DsRGetDCName
ZERO_STRUCTP(r->out.info);
- sam_ctx = samdb_connect(state, dce_call->event_ctx, lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ sam_ctx = samdb_connect(state,
+ dce_call->event_ctx,
+ lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return WERR_DS_UNAVAILABLE;
}
@@ -3259,8 +3299,12 @@ static WERROR dcesrv_netr_DsRAddressToSitenamesExW(struct dcesrv_call_state *dce
const char *res;
uint32_t i;
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return WERR_DS_UNAVAILABLE;
}
@@ -3376,8 +3420,12 @@ static WERROR dcesrv_netr_DsrGetDcSiteCoverageW(struct dcesrv_call_state *dce_ca
struct DcSitesCtr *ctr;
struct loadparm_context *lp_ctx = dce_call->conn->dce_ctx->lp_ctx;
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return WERR_DS_UNAVAILABLE;
}
@@ -3544,8 +3592,12 @@ static WERROR dcesrv_netr_DsrEnumerateDomainTrusts(struct dcesrv_call_state *dce
trusts->count = 0;
r->out.trusts = trusts;
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return WERR_GEN_FAILURE;
}
@@ -3677,8 +3729,12 @@ static WERROR dcesrv_netr_DsRGetForestTrustInformation(struct dcesrv_call_state
return WERR_INVALID_FLAGS;
}
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return WERR_GEN_FAILURE;
}
@@ -3829,8 +3885,12 @@ static NTSTATUS dcesrv_netr_GetForestTrustInformation(struct dcesrv_call_state *
return NT_STATUS_NOT_IMPLEMENTED;
}
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INTERNAL_ERROR;
}
@@ -3924,8 +3984,12 @@ static NTSTATUS dcesrv_netr_ServerGetTrustInfo(struct dcesrv_call_state *dce_cal
return NT_STATUS_INVALID_PARAMETER;
}
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
- lp_ctx, system_session(lp_ctx), 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ lp_ctx,
+ system_session(lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
diff --git a/source4/rpc_server/samr/dcesrv_samr.c b/source4/rpc_server/samr/dcesrv_samr.c
index c7d692a..bf086f7 100644
--- a/source4/rpc_server/samr/dcesrv_samr.c
+++ b/source4/rpc_server/samr/dcesrv_samr.c
@@ -166,7 +166,12 @@ static NTSTATUS dcesrv_samr_Connect(struct dcesrv_call_state *dce_call, TALLOC_C
}
/* make sure the sam database is accessible */
- c_state->sam_ctx = samdb_connect(c_state, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info, 0);
+ c_state->sam_ctx = samdb_connect(c_state,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (c_state->sam_ctx == NULL) {
talloc_free(c_state);
return NT_STATUS_INVALID_SYSTEM_SERVICE;
@@ -4180,9 +4185,12 @@ static NTSTATUS dcesrv_samr_GetDomPwInfo(struct dcesrv_call_state *dce_call, TAL
ZERO_STRUCTP(r->out.info);
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
- dce_call->conn->dce_ctx->lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
+ dce_call->conn->dce_ctx->lp_ctx,
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
diff --git a/source4/rpc_server/samr/samr_password.c b/source4/rpc_server/samr/samr_password.c
index 22f456f..d7b5e16 100644
--- a/source4/rpc_server/samr/samr_password.c
+++ b/source4/rpc_server/samr/samr_password.c
@@ -135,9 +135,12 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
/* Connect to a SAMDB with system privileges for fetching the old pw
* hashes. */
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
dce_call->conn->dce_ctx->lp_ctx,
- system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
@@ -212,9 +215,12 @@ NTSTATUS dcesrv_samr_OemChangePasswordUser2(struct dcesrv_call_state *dce_call,
}
/* Connect to a SAMDB with user privileges for the password change */
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
dce_call->conn->dce_ctx->lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
@@ -327,9 +333,12 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
/* Connect to a SAMDB with system privileges for fetching the old pw
* hashes. */
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
dce_call->conn->dce_ctx->lp_ctx,
- system_session(dce_call->conn->dce_ctx->lp_ctx), 0);
+ system_session(dce_call->conn->dce_ctx->lp_ctx),
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
@@ -408,9 +417,12 @@ NTSTATUS dcesrv_samr_ChangePasswordUser3(struct dcesrv_call_state *dce_call,
}
/* Connect to a SAMDB with user privileges for the password change */
- sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx,
+ sam_ctx = samdb_connect(mem_ctx,
+ dce_call->event_ctx,
dce_call->conn->dce_ctx->lp_ctx,
- dce_call->conn->auth_state.session_info, 0);
+ dce_call->conn->auth_state.session_info,
+ dce_call->conn->remote_address,
+ 0);
if (sam_ctx == NULL) {
return NT_STATUS_INVALID_SYSTEM_SERVICE;
}
diff --git a/source4/smb_server/smb/trans2.c b/source4/smb_server/smb/trans2.c
index 89402ba..447095c 100644
--- a/source4/smb_server/smb/trans2.c
+++ b/source4/smb_server/smb/trans2.c
@@ -876,7 +876,12 @@ static NTSTATUS trans2_getdfsreferral(struct smbsrv_request *req,
r = talloc_zero(req, struct dfs_GetDFSReferral);
NT_STATUS_HAVE_NO_MEMORY(r);
- ldb = samdb_connect(r, req->tcon->ntvfs->event_ctx, lp_ctx, system_session(lp_ctx), 0);
+ ldb = samdb_connect(r,
+ req->tcon->ntvfs->event_ctx,
+ lp_ctx,
+ system_session(lp_ctx),
+ NULL,
+ 0);
if (ldb == NULL) {
DEBUG(2,(__location__ ": Failed to open samdb\n"));
talloc_free(r);
diff --git a/source4/smbd/server.c b/source4/smbd/server.c
index beac4ff..ed81c01 100644
--- a/source4/smbd/server.c
+++ b/source4/smbd/server.c
@@ -238,10 +238,11 @@ static void prime_ldb_databases(struct tevent_context *event_ctx)
db_context = talloc_new(event_ctx);
samdb_connect(db_context,
- event_ctx,
- cmdline_lp_ctx,
- system_session(cmdline_lp_ctx),
- 0);
+ event_ctx,
+ cmdline_lp_ctx,
+ system_session(cmdline_lp_ctx),
+ NULL,
+ 0);
privilege_connect(db_context, cmdline_lp_ctx);
/* we deliberately leave these open, which allows them to be
diff --git a/source4/torture/dns/dlz_bind9.c b/source4/torture/dns/dlz_bind9.c
index d948487..ef7220b 100644
--- a/source4/torture/dns/dlz_bind9.c
+++ b/source4/torture/dns/dlz_bind9.c
@@ -88,13 +88,16 @@ static isc_result_t dlz_bind9_writeable_zone_hook(dns_view_t *view,
struct torture_context *tctx = talloc_get_type((void *)view, struct torture_context);
struct ldb_context *samdb = NULL;
char *errstring = NULL;
- int ret = samdb_connect_url(tctx, NULL, tctx->lp_ctx,
- system_session(tctx->lp_ctx),
- 0,
- test_dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"),
- NULL,
- &samdb,
- &errstring);
+ int ret = samdb_connect_url(
+ tctx,
+ NULL,
+ tctx->lp_ctx,
+ system_session(tctx->lp_ctx),
+ 0,
+ test_dlz_bind9_binddns_dir(tctx, "dns/sam.ldb"),
+ NULL,
+ &samdb,
+ &errstring);
struct ldb_message *msg;
const char *attrs[] = {
NULL
diff --git a/source4/torture/gpo/apply.c b/source4/torture/gpo/apply.c
index 88da0b1..248ddbb 100644
--- a/source4/torture/gpo/apply.c
+++ b/source4/torture/gpo/apply.c
@@ -119,8 +119,12 @@ bool torture_gpo_system_access_policies(struct torture_context *tctx)
"Failed to fetch the gpo update command");
/* Open and read the samba db and store the initial password settings */
- samdb = samdb_connect(ctx, tctx->ev, tctx->lp_ctx,
- system_session(tctx->lp_ctx), 0);
+ samdb = samdb_connect(ctx,
+ tctx->ev,
+ tctx->lp_ctx,
+ system_session(tctx->lp_ctx),
+ NULL,
+ 0);
torture_assert(tctx, samdb, "Failed to connect to the samdb");
ret = ldb_search(samdb, ctx, &result, ldb_get_default_basedn(samdb),
diff --git a/source4/torture/libnet/libnet_BecomeDC.c b/source4/torture/libnet/libnet_BecomeDC.c
index ffba17c..c3d66d5 100644
--- a/source4/torture/libnet/libnet_BecomeDC.c
+++ b/source4/torture/libnet/libnet_BecomeDC.c
@@ -148,7 +148,12 @@ bool torture_net_become_dc(struct torture_context *torture)
private_dir = talloc_asprintf(s, "%s/%s", location, "private");
lpcfg_set_cmdline(lp_ctx, "private dir", private_dir);
torture_comment(torture, "Reopen the SAM LDB with system credentials and all replicated data: %s\n", private_dir);
- ldb = samdb_connect(s, torture->ev, lp_ctx, system_session(lp_ctx), 0);
+ ldb = samdb_connect(s,
+ torture->ev,
+ lp_ctx,
+ system_session(lp_ctx),
+ NULL,
+ 0);
torture_assert_goto(torture, ldb != NULL, ret, cleanup,
talloc_asprintf(torture,
"Failed to open '%s/sam.ldb'\n", private_dir));
diff --git a/source4/winbind/idmap.c b/source4/winbind/idmap.c
index edeb724..86fd354 100644
--- a/source4/winbind/idmap.c
+++ b/source4/winbind/idmap.c
@@ -175,7 +175,12 @@ struct idmap_context *idmap_init(TALLOC_CTX *mem_ctx,
goto fail;
}
- idmap_ctx->samdb = samdb_connect(idmap_ctx, ev_ctx, lp_ctx, system_session(lp_ctx), 0);
+ idmap_ctx->samdb = samdb_connect(idmap_ctx,
+ ev_ctx,
+ lp_ctx,
+ system_session(lp_ctx),
+ NULL,
+ 0);
if (idmap_ctx->samdb == NULL) {
DEBUG(0, ("Failed to load sam.ldb in idmap_init\n"));
goto fail;
--
2.7.4
From 060cd8543bcb4c4df1633519e88c6d4dbc939716 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Tue, 10 Apr 2018 11:45:32 +1200
Subject: [PATCH 14/21] auth_log: Use common code from audit_logging
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
auth/auth_log.c | 544 ++++++++++-------------------------------------------
auth/wscript_build | 2 +-
2 files changed, 103 insertions(+), 443 deletions(-)
diff --git a/auth/auth_log.c b/auth/auth_log.c
index 97b6537..19e0cba 100644
--- a/auth/auth_log.c
+++ b/auth/auth_log.c
@@ -57,48 +57,7 @@
#include "lib/util/server_id_db.h"
#include "lib/param/param.h"
#include "librpc/ndr/libndr.h"
-
-/*
- * Get a human readable timestamp.
- *
- * Returns the current time formatted as
- * "Tue, 14 Mar 2017 08:38:42.209028 NZDT"
- *
- * The returned string is allocated by talloc in the supplied context.
- * It is the callers responsibility to free it.
- *
- */
-static const char* get_timestamp(TALLOC_CTX *frame)
-{
- char buffer[40]; /* formatted time less usec and timezone */
- char tz[10]; /* formatted time zone */
- struct tm* tm_info; /* current local time */
- struct timeval tv; /* current system time */
- int r; /* response code from gettimeofday */
- const char * ts; /* formatted time stamp */
-
- r = gettimeofday(&tv, NULL);
- if (r) {
- DBG_ERR("Unable to get time of day: (%d) %s\n",
- errno,
- strerror(errno));
- return NULL;
- }
-
- tm_info = localtime(&tv.tv_sec);
- if (tm_info == NULL) {
- DBG_ERR("Unable to determine local time\n");
- return NULL;
- }
-
- strftime(buffer, sizeof(buffer)-1, "%a, %d %b %Y %H:%M:%S", tm_info);
- strftime(tz, sizeof(tz)-1, "%Z", tm_info);
- ts = talloc_asprintf(frame, "%s.%06ld %s", buffer, tv.tv_usec, tz);
- if (ts == NULL) {
- DBG_ERR("Out of memory formatting time stamp\n");
- }
- return ts;
-}
+#include "lib/audit_logging/audit_logging.h"
/*
* Determine the type of the password supplied for the
@@ -109,349 +68,26 @@ static const char* get_password_type(const struct auth_usersupplied_info *ui);
#ifdef HAVE_JANSSON
-#include <jansson.h>
-#include "system/time.h"
-
-/*
- * Context required by the JSON generation
- * routines
- *
- */
-struct json_context {
- json_t *root;
- bool error;
-};
-
-static NTSTATUS get_auth_event_server(struct imessaging_context *msg_ctx,
- struct server_id *auth_event_server)
-{
- NTSTATUS status;
- TALLOC_CTX *frame = talloc_stackframe();
- unsigned num_servers, i;
- struct server_id *servers;
-
- status = irpc_servers_byname(msg_ctx, frame,
- AUTH_EVENT_NAME,
- &num_servers, &servers);
-
- if (!NT_STATUS_IS_OK(status)) {
- DBG_NOTICE("Failed to find 'auth_event' registered on the "
- "message bus to send JSON authentication events to: %s\n",
- nt_errstr(status));
- TALLOC_FREE(frame);
- return status;
- }
-
- /*
- * Select the first server that is listening, because
- * we get connection refused as
- * NT_STATUS_OBJECT_NAME_NOT_FOUND without waiting
- */
- for (i = 0; i < num_servers; i++) {
- status = imessaging_send(msg_ctx, servers[i], MSG_PING,
- &data_blob_null);
- if (NT_STATUS_IS_OK(status)) {
- *auth_event_server = servers[i];
- TALLOC_FREE(frame);
- return NT_STATUS_OK;
- }
- }
- DBG_NOTICE("Failed to find a running 'auth_event' server "
- "registered on the message bus to send JSON "
- "authentication events to\n");
- TALLOC_FREE(frame);
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-}
-
-static void auth_message_send(struct imessaging_context *msg_ctx,
- const char *json)
-{
- struct server_id auth_event_server;
- NTSTATUS status;
- DATA_BLOB json_blob = data_blob_string_const(json);
- if (msg_ctx == NULL) {
- return;
- }
-
- /* Need to refetch the address each time as the destination server may
- * have disconnected and reconnected in the interim, in which case
- * messages may get lost, manifests in the auth_log tests
- */
- status = get_auth_event_server(msg_ctx, &auth_event_server);
- if (!NT_STATUS_IS_OK(status)) {
- return;
- }
-
- status = imessaging_send(msg_ctx, auth_event_server, MSG_AUTH_LOG,
- &json_blob);
-
- /* If the server crashed, try to find it again */
- if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
- status = get_auth_event_server(msg_ctx, &auth_event_server);
- if (!NT_STATUS_IS_OK(status)) {
- return;
- }
- imessaging_send(msg_ctx, auth_event_server, MSG_AUTH_LOG,
- &json_blob);
-
- }
-}
-
/*
* Write the json object to the debug logs.
*
*/
static void log_json(struct imessaging_context *msg_ctx,
struct loadparm_context *lp_ctx,
- struct json_context *context,
+ struct json_object *context,
const char *type, int debug_class, int debug_level)
{
- char* json = NULL;
-
- if (context->error) {
- return;
- }
-
- json = json_dumps(context->root, 0);
- if (json == NULL) {
- DBG_ERR("Unable to convert JSON object to string\n");
- context->error = true;
- return;
- }
- DEBUGC(debug_class, debug_level, ("JSON %s: %s\n", type, json));
+ audit_log_json(type, context, debug_class, debug_level);
if (msg_ctx && lp_ctx && lpcfg_auth_event_notification(lp_ctx)) {
- auth_message_send(msg_ctx, json);
- }
-
- if (json) {
- free(json);
- }
-
-}
-
-/*
- * Create a new json logging context.
- *
- * Free with a call to free_json_context
- *
- */
-static struct json_context get_json_context(void) {
-
- struct json_context context;
- context.error = false;
-
- context.root = json_object();
- if (context.root == NULL) {
- context.error = true;
- DBG_ERR("Unable to create json_object\n");
- }
- return context;
-}
-
-/*
- * free a previously created json_context
- *
- */
-static void free_json_context(struct json_context *context)
-{
- if (context->root) {
- json_decref(context->root);
+ audit_message_send(msg_ctx,
+ AUTH_EVENT_NAME,
+ MSG_AUTH_LOG,
+ context);
}
}
/*
- * Output a JSON pair with name name and integer value value
- *
- */
-static void add_int(struct json_context *context,
- const char* name,
- const int value)
-{
- int rc = 0;
-
- if (context->error) {
- return;
- }
-
- rc = json_object_set_new(context->root, name, json_integer(value));
- if (rc) {
- DBG_ERR("Unable to set name [%s] value [%d]\n", name, value);
- context->error = true;
- }
-
-}
-
-/*
- * Output a JSON pair with name name and string value value
- *
- */
-static void add_string(struct json_context *context,
- const char* name,
- const char* value)
-{
- int rc = 0;
-
- if (context->error) {
- return;
- }
-
- if (value) {
- rc = json_object_set_new(context->root, name, json_string(value));
- } else {
- rc = json_object_set_new(context->root, name, json_null());
- }
- if (rc) {
- DBG_ERR("Unable to set name [%s] value [%s]\n", name, value);
- context->error = true;
- }
-}
-
-
-/*
- * Output a JSON pair with name name and object value
- *
- */
-static void add_object(struct json_context *context,
- const char* name,
- struct json_context *value)
-{
- int rc = 0;
-
- if (value->error) {
- context->error = true;
- }
- if (context->error) {
- return;
- }
- rc = json_object_set_new(context->root, name, value->root);
- if (rc) {
- DBG_ERR("Unable to add object [%s]\n", name);
- context->error = true;
- }
-}
-
-/*
- * Output a version object
- *
- * "version":{"major":1,"minor":0}
- *
- */
-static void add_version(struct json_context *context, int major, int minor)
-{
- struct json_context version = get_json_context();
- add_int(&version, "major", major);
- add_int(&version, "minor", minor);
- add_object(context, "version", &version);
-}
-
-/*
- * Output the current date and time as a timestamp in ISO 8601 format
- *
- * "timestamp":"2017-03-06T17:18:04.455081+1300"
- *
- */
-static void add_timestamp(struct json_context *context)
-{
- char buffer[40]; /* formatted time less usec and timezone */
- char timestamp[50]; /* the formatted ISO 8601 time stamp */
- char tz[10]; /* formatted time zone */
- struct tm* tm_info; /* current local time */
- struct timeval tv; /* current system time */
- int r; /* response code from gettimeofday */
-
- if (context->error) {
- return;
- }
-
- r = gettimeofday(&tv, NULL);
- if (r) {
- DBG_ERR("Unable to get time of day: (%d) %s\n",
- errno,
- strerror(errno));
- context->error = true;
- return;
- }
-
- tm_info = localtime(&tv.tv_sec);
- if (tm_info == NULL) {
- DBG_ERR("Unable to determine local time\n");
- context->error = true;
- return;
- }
-
- strftime(buffer, sizeof(buffer)-1, "%Y-%m-%dT%T", tm_info);
- strftime(tz, sizeof(tz)-1, "%z", tm_info);
- snprintf(timestamp, sizeof(timestamp),"%s.%06ld%s",
- buffer, tv.tv_usec, tz);
- add_string(context,"timestamp", timestamp);
-}
-
-
-/*
- * Output an address pair, with name name.
- *
- * "localAddress":"ipv6::::0"
- *
- */
-static void add_address(struct json_context *context,
- const char *name,
- const struct tsocket_address *address)
-{
- char *s = NULL;
- TALLOC_CTX *frame = talloc_stackframe();
-
- if (context->error) {
- return;
- }
-
- s = tsocket_address_string(address, frame);
- add_string(context, name, s);
- talloc_free(frame);
-
-}
-
-/*
- * Output a SID with name name
- *
- * "sid":"S-1-5-18"
- *
- */
-static void add_sid(struct json_context *context,
- const char *name,
- const struct dom_sid *sid)
-{
- char sid_buf[DOM_SID_STR_BUFLEN];
-
- if (context->error) {
- return;
- }
-
- dom_sid_string_buf(sid, sid_buf, sizeof(sid_buf));
- add_string(context, name, sid_buf);
-}
-
-/*
- * Add a formatted string representation of a GUID to a json object.
- *
- */
-static void add_guid(struct json_context *context,
- const char *name,
- struct GUID *guid)
-{
-
- char *guid_str;
- struct GUID_txt_buf guid_buff;
-
- if (context->error) {
- return;
- }
-
- guid_str = GUID_buf_string(guid, &guid_buff);
- add_string(context, name, guid_str);
-}
-
-/*
* Write a machine parsable json formatted authentication log entry.
*
* IF removing or changing the format/meaning of a field please update the
@@ -482,57 +118,72 @@ static void log_authentication_event_json(
struct dom_sid *sid,
int debug_level)
{
- struct json_context context = get_json_context();
- struct json_context authentication;
+ struct json_object context = json_new_object();
+ struct json_object authentication;
char negotiate_flags[11];
- add_timestamp(&context);
- add_string(&context, "type", AUTH_JSON_TYPE);
-
- authentication = get_json_context();
- add_version(&authentication, AUTH_MAJOR, AUTH_MINOR);
- add_string(&authentication, "status", nt_errstr(status));
- add_address(&authentication, "localAddress", ui->local_host);
- add_address(&authentication, "remoteAddress", ui->remote_host);
- add_string(&authentication,
- "serviceDescription",
- ui->service_description);
- add_string(&authentication, "authDescription", ui->auth_description);
- add_string(&authentication, "clientDomain", ui->client.domain_name);
- add_string(&authentication, "clientAccount", ui->client.account_name);
- add_string(&authentication, "workstation", ui->workstation_name);
- add_string(&authentication, "becameAccount", account_name);
- add_string(&authentication, "becameDomain", domain_name);
- add_sid(&authentication, "becameSid", sid);
- add_string(&authentication, "mappedAccount", ui->mapped.account_name);
- add_string(&authentication, "mappedDomain", ui->mapped.domain_name);
- add_string(&authentication,
- "netlogonComputer",
- ui->netlogon_trust_account.computer_name);
- add_string(&authentication,
- "netlogonTrustAccount",
- ui->netlogon_trust_account.account_name);
+ json_add_timestamp(&context);
+ json_add_string(&context, "type", AUTH_JSON_TYPE);
+
+ authentication = json_new_object();
+ json_add_version(&authentication, AUTH_MAJOR, AUTH_MINOR);
+ json_add_string(&authentication, "status", nt_errstr(status));
+ json_add_address(&authentication, "localAddress", ui->local_host);
+ json_add_address(&authentication, "remoteAddress", ui->remote_host);
+ json_add_string(&authentication,
+ "serviceDescription",
+ ui->service_description);
+ json_add_string(&authentication,
+ "authDescription",
+ ui->auth_description);
+ json_add_string(&authentication,
+ "clientDomain",
+ ui->client.domain_name);
+ json_add_string(&authentication,
+ "clientAccount",
+ ui->client.account_name);
+ json_add_string(&authentication,
+ "workstation",
+ ui->workstation_name);
+ json_add_string(&authentication, "becameAccount", account_name);
+ json_add_string(&authentication, "becameDomain", domain_name);
+ json_add_sid(&authentication, "becameSid", sid);
+ json_add_string(&authentication,
+ "mappedAccount",
+ ui->mapped.account_name);
+ json_add_string(&authentication,
+ "mappedDomain",
+ ui->mapped.domain_name);
+ json_add_string(&authentication,
+ "netlogonComputer",
+ ui->netlogon_trust_account.computer_name);
+ json_add_string(&authentication,
+ "netlogonTrustAccount",
+ ui->netlogon_trust_account.account_name);
snprintf(negotiate_flags,
sizeof( negotiate_flags),
"0x%08X",
ui->netlogon_trust_account.negotiate_flags);
- add_string(&authentication, "netlogonNegotiateFlags", negotiate_flags);
- add_int(&authentication,
- "netlogonSecureChannelType",
- ui->netlogon_trust_account.secure_channel_type);
- add_sid(&authentication,
- "netlogonTrustAccountSid",
- ui->netlogon_trust_account.sid);
- add_string(&authentication, "passwordType", get_password_type(ui));
- add_object(&context,AUTH_JSON_TYPE, &authentication);
-
- log_json(msg_ctx,
- lp_ctx,
- &context,
- AUTH_JSON_TYPE,
- DBGC_AUTH_AUDIT,
- debug_level);
- free_json_context(&context);
+ json_add_string(&authentication,
+ "netlogonNegotiateFlags",
+ negotiate_flags);
+ json_add_int(&authentication,
+ "netlogonSecureChannelType",
+ ui->netlogon_trust_account.secure_channel_type);
+ json_add_sid(&authentication,
+ "netlogonTrustAccountSid",
+ ui->netlogon_trust_account.sid);
+ json_add_string(&authentication, "passwordType", get_password_type(ui));
+ json_add_object(&context,AUTH_JSON_TYPE, &authentication);
+
+ log_json(
+ msg_ctx,
+ lp_ctx,
+ &context,
+ AUTH_JSON_TYPE,
+ DBGC_AUTH_AUDIT,
+ debug_level);
+ json_free(&context);
}
/*
@@ -566,36 +217,45 @@ static void log_successful_authz_event_json(
struct auth_session_info *session_info,
int debug_level)
{
- struct json_context context = get_json_context();
- struct json_context authorization;
+ struct json_object context = json_new_object();
+ struct json_object authorization;
char account_flags[11];
- //start_object(&context, NULL);
- add_timestamp(&context);
- add_string(&context, "type", AUTHZ_JSON_TYPE);
- authorization = get_json_context();
- add_version(&authorization, AUTHZ_MAJOR, AUTHZ_MINOR);
- add_address(&authorization, "localAddress", local);
- add_address(&authorization, "remoteAddress", remote);
- add_string(&authorization, "serviceDescription", service_description);
- add_string(&authorization, "authType", auth_type);
- add_string(&authorization, "domain", session_info->info->domain_name);
- add_string(&authorization, "account", session_info->info->account_name);
- add_sid(&authorization, "sid", &session_info->security_token->sids[0]);
- add_guid(&authorization,
- "sessionId",
- &session_info->unique_session_token);
- add_string(&authorization,
- "logonServer",
- session_info->info->logon_server);
- add_string(&authorization, "transportProtection", transport_protection);
+ json_add_timestamp(&context);
+ json_add_string(&context, "type", AUTHZ_JSON_TYPE);
+ authorization = json_new_object();
+ json_add_version(&authorization, AUTHZ_MAJOR, AUTHZ_MINOR);
+ json_add_address(&authorization, "localAddress", local);
+ json_add_address(&authorization, "remoteAddress", remote);
+ json_add_string(&authorization,
+ "serviceDescription",
+ service_description);
+ json_add_string(&authorization, "authType", auth_type);
+ json_add_string(&authorization,
+ "domain",
+ session_info->info->domain_name);
+ json_add_string(&authorization,
+ "account",
+ session_info->info->account_name);
+ json_add_sid(&authorization,
+ "sid",
+ &session_info->security_token->sids[0]);
+ json_add_guid(&authorization,
+ "sessionId",
+ &session_info->unique_session_token);
+ json_add_string(&authorization,
+ "logonServer",
+ session_info->info->logon_server);
+ json_add_string(&authorization,
+ "transportProtection",
+ transport_protection);
snprintf(account_flags,
sizeof(account_flags),
"0x%08X",
session_info->info->acct_flags);
- add_string(&authorization, "accountFlags", account_flags);
- add_object(&context,AUTHZ_JSON_TYPE, &authorization);
+ json_add_string(&authorization, "accountFlags", account_flags);
+ json_add_object(&context, AUTHZ_JSON_TYPE, &authorization);
log_json(msg_ctx,
lp_ctx,
@@ -603,7 +263,7 @@ static void log_successful_authz_event_json(
AUTHZ_JSON_TYPE,
DBGC_AUTH_AUDIT,
debug_level);
- free_json_context(&context);
+ json_free(&context);
}
#else
@@ -738,7 +398,7 @@ static void log_authentication_event_human_readable(
password_type = get_password_type(ui);
/* Get the current time */
- ts = get_timestamp(frame);
+ ts = audit_get_timestamp(frame);
/* Only log the NETLOGON details if they are present */
if (ui->netlogon_trust_account.computer_name ||
@@ -869,7 +529,7 @@ static void log_successful_authz_event_human_readable(
frame = talloc_stackframe();
/* Get the current time */
- ts = get_timestamp(frame);
+ ts = audit_get_timestamp(frame);
remote_str = tsocket_address_string(remote, frame);
local_str = tsocket_address_string(local, frame);
diff --git a/auth/wscript_build b/auth/wscript_build
index 88e9a03..e2e3d21 100644
--- a/auth/wscript_build
+++ b/auth/wscript_build
@@ -2,7 +2,7 @@
bld.SAMBA_LIBRARY('common_auth',
source='auth_sam_reply.c wbc_auth_util.c auth_log.c',
- deps='talloc samba-security samba-util util_str_escape LIBTSOCKET jansson MESSAGING_SEND server_id_db ',
+ deps='talloc samba-security samba-util util_str_escape LIBTSOCKET audit_logging jansson MESSAGING_SEND server_id_db ',
private_library=True)
bld.RECURSE('gensec')
--
2.7.4
From 2dfa39de6f08b75b15c1b83a2dbe4f553aaf0d74 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Tue, 10 Apr 2018 11:57:41 +1200
Subject: [PATCH 15/21] auth_log: tidy up code formatting
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
auth/auth_log.c | 176 +++++++++++++++++++++++++++++---------------------------
1 file changed, 91 insertions(+), 85 deletions(-)
diff --git a/auth/auth_log.c b/auth/auth_log.c
index 19e0cba..69e28bf 100644
--- a/auth/auth_log.c
+++ b/auth/auth_log.c
@@ -74,16 +74,18 @@ static const char* get_password_type(const struct auth_usersupplied_info *ui);
*/
static void log_json(struct imessaging_context *msg_ctx,
struct loadparm_context *lp_ctx,
- struct json_object *context,
- const char *type, int debug_class, int debug_level)
+ struct json_object *object,
+ const char *type,
+ int debug_class,
+ int debug_level)
{
- audit_log_json(type, context, debug_class, debug_level);
+ audit_log_json(type, object, debug_class, debug_level);
if (msg_ctx && lp_ctx && lpcfg_auth_event_notification(lp_ctx)) {
audit_message_send(msg_ctx,
AUTH_EVENT_NAME,
MSG_AUTH_LOG,
- context);
+ object);
}
}
@@ -108,22 +110,22 @@ static void log_json(struct imessaging_context *msg_ctx,
* \t\(.Authentication.localAddress)"'
*/
static void log_authentication_event_json(
- struct imessaging_context *msg_ctx,
- struct loadparm_context *lp_ctx,
- const struct auth_usersupplied_info *ui,
- NTSTATUS status,
- const char *domain_name,
- const char *account_name,
- const char *unix_username,
- struct dom_sid *sid,
- int debug_level)
+ struct imessaging_context *msg_ctx,
+ struct loadparm_context *lp_ctx,
+ const struct auth_usersupplied_info *ui,
+ NTSTATUS status,
+ const char *domain_name,
+ const char *account_name,
+ const char *unix_username,
+ struct dom_sid *sid,
+ int debug_level)
{
- struct json_object context = json_new_object();
+ struct json_object wrapper = json_new_object();
struct json_object authentication;
char negotiate_flags[11];
- json_add_timestamp(&context);
- json_add_string(&context, "type", AUTH_JSON_TYPE);
+ json_add_timestamp(&wrapper);
+ json_add_string(&wrapper, "type", AUTH_JSON_TYPE);
authentication = json_new_object();
json_add_version(&authentication, AUTH_MAJOR, AUTH_MINOR);
@@ -174,16 +176,16 @@ static void log_authentication_event_json(
"netlogonTrustAccountSid",
ui->netlogon_trust_account.sid);
json_add_string(&authentication, "passwordType", get_password_type(ui));
- json_add_object(&context,AUTH_JSON_TYPE, &authentication);
+ json_add_object(&wrapper, AUTH_JSON_TYPE, &authentication);
log_json(
msg_ctx,
lp_ctx,
- &context,
+ &wrapper,
AUTH_JSON_TYPE,
DBGC_AUTH_AUDIT,
debug_level);
- json_free(&context);
+ json_free(&wrapper);
}
/*
@@ -207,22 +209,22 @@ static void log_authentication_event_json(
*
*/
static void log_successful_authz_event_json(
- struct imessaging_context *msg_ctx,
- struct loadparm_context *lp_ctx,
- const struct tsocket_address *remote,
- const struct tsocket_address *local,
- const char *service_description,
- const char *auth_type,
- const char *transport_protection,
- struct auth_session_info *session_info,
- int debug_level)
+ struct imessaging_context *msg_ctx,
+ struct loadparm_context *lp_ctx,
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ const char *service_description,
+ const char *auth_type,
+ const char *transport_protection,
+ struct auth_session_info *session_info,
+ int debug_level)
{
- struct json_object context = json_new_object();
+ struct json_object wrapper = json_new_object();
struct json_object authorization;
char account_flags[11];
- json_add_timestamp(&context);
- json_add_string(&context, "type", AUTHZ_JSON_TYPE);
+ json_add_timestamp(&wrapper);
+ json_add_string(&wrapper, "type", AUTHZ_JSON_TYPE);
authorization = json_new_object();
json_add_version(&authorization, AUTHZ_MAJOR, AUTHZ_MINOR);
json_add_address(&authorization, "localAddress", local);
@@ -255,15 +257,15 @@ static void log_successful_authz_event_json(
"0x%08X",
session_info->info->acct_flags);
json_add_string(&authorization, "accountFlags", account_flags);
- json_add_object(&context, AUTHZ_JSON_TYPE, &authorization);
+ json_add_object(&wrapper, AUTHZ_JSON_TYPE, &authorization);
log_json(msg_ctx,
lp_ctx,
- &context,
+ &wrapper,
AUTHZ_JSON_TYPE,
DBGC_AUTH_AUDIT,
debug_level);
- json_free(&context);
+ json_free(&wrapper);
}
#else
@@ -275,13 +277,15 @@ static void log_no_json(struct imessaging_context *msg_ctx,
static bool auth_event_logged = false;
if (auth_event_logged == false) {
auth_event_logged = true;
- DBG_ERR("auth event notification = true but Samba was not compiled with jansson\n");
+ DBG_ERR("auth event notification = true but Samba was "
+ "not compiled with jansson\n");
}
} else {
static bool json_logged = false;
if (json_logged == false) {
json_logged = true;
- DBG_NOTICE("JSON auth logs not available unless compiled with jansson\n");
+ DBG_NOTICE("JSON auth logs not available unless "
+ "compiled with jansson\n");
}
}
@@ -289,30 +293,30 @@ static void log_no_json(struct imessaging_context *msg_ctx,
}
static void log_authentication_event_json(
- struct imessaging_context *msg_ctx,
- struct loadparm_context *lp_ctx,
- const struct auth_usersupplied_info *ui,
- NTSTATUS status,
- const char *domain_name,
- const char *account_name,
- const char *unix_username,
- struct dom_sid *sid,
- int debug_level)
+ struct imessaging_context *msg_ctx,
+ struct loadparm_context *lp_ctx,
+ const struct auth_usersupplied_info *ui,
+ NTSTATUS status,
+ const char *domain_name,
+ const char *account_name,
+ const char *unix_username,
+ struct dom_sid *sid,
+ int debug_level)
{
log_no_json(msg_ctx, lp_ctx);
return;
}
static void log_successful_authz_event_json(
- struct imessaging_context *msg_ctx,
- struct loadparm_context *lp_ctx,
- const struct tsocket_address *remote,
- const struct tsocket_address *local,
- const char *service_description,
- const char *auth_type,
- const char *transport_protection,
- struct auth_session_info *session_info,
- int debug_level)
+ struct imessaging_context *msg_ctx,
+ struct loadparm_context *lp_ctx,
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ const char *service_description,
+ const char *auth_type,
+ const char *transport_protection,
+ struct auth_session_info *session_info,
+ int debug_level)
{
log_no_json(msg_ctx, lp_ctx);
return;
@@ -375,13 +379,13 @@ static const char* get_password_type(const struct auth_usersupplied_info *ui)
*
*/
static void log_authentication_event_human_readable(
- const struct auth_usersupplied_info *ui,
- NTSTATUS status,
- const char *domain_name,
- const char *account_name,
- const char *unix_username,
- struct dom_sid *sid,
- int debug_level)
+ const struct auth_usersupplied_info *ui,
+ NTSTATUS status,
+ const char *domain_name,
+ const char *account_name,
+ const char *unix_username,
+ struct dom_sid *sid,
+ int debug_level)
{
TALLOC_CTX *frame = NULL;
@@ -450,7 +454,7 @@ static void log_authentication_event_human_readable(
logon_line,
local,
nl ? nl : ""
- ));
+ ));
talloc_free(frame);
}
@@ -462,14 +466,15 @@ static void log_authentication_event_human_readable(
* NOTE: msg_ctx and lp_ctx is optional, but when supplied allows streaming the
* authentication events over the message bus.
*/
-void log_authentication_event(struct imessaging_context *msg_ctx,
- struct loadparm_context *lp_ctx,
- const struct auth_usersupplied_info *ui,
- NTSTATUS status,
- const char *domain_name,
- const char *account_name,
- const char *unix_username,
- struct dom_sid *sid)
+void log_authentication_event(
+ struct imessaging_context *msg_ctx,
+ struct loadparm_context *lp_ctx,
+ const struct auth_usersupplied_info *ui,
+ NTSTATUS status,
+ const char *domain_name,
+ const char *account_name,
+ const char *unix_username,
+ struct dom_sid *sid)
{
/* set the log level */
int debug_level = AUTH_FAILURE_LEVEL;
@@ -511,13 +516,13 @@ void log_authentication_event(struct imessaging_context *msg_ctx,
*
*/
static void log_successful_authz_event_human_readable(
- const struct tsocket_address *remote,
- const struct tsocket_address *local,
- const char *service_description,
- const char *auth_type,
- const char *transport_protection,
- struct auth_session_info *session_info,
- int debug_level)
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ const char *service_description,
+ const char *auth_type,
+ const char *transport_protection,
+ struct auth_session_info *session_info,
+ int debug_level)
{
TALLOC_CTX *frame = NULL;
@@ -567,14 +572,15 @@ static void log_successful_authz_event_human_readable(
* NOTE: msg_ctx and lp_ctx is optional, but when supplied allows streaming the
* authentication events over the message bus.
*/
-void log_successful_authz_event(struct imessaging_context *msg_ctx,
- struct loadparm_context *lp_ctx,
- const struct tsocket_address *remote,
- const struct tsocket_address *local,
- const char *service_description,
- const char *auth_type,
- const char *transport_protection,
- struct auth_session_info *session_info)
+void log_successful_authz_event(
+ struct imessaging_context *msg_ctx,
+ struct loadparm_context *lp_ctx,
+ const struct tsocket_address *remote,
+ const struct tsocket_address *local,
+ const char *service_description,
+ const char *auth_type,
+ const char *transport_protection,
+ struct auth_session_info *session_info)
{
int debug_level = AUTHZ_SUCCESS_LEVEL;
--
2.7.4
From 9fbaada8aa5d9a608c25a1e008a127720a0c3cc2 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 16 Apr 2018 07:59:43 +1200
Subject: [PATCH 16/21] samdb: Add transaction id control
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
source4/dsdb/samdb/samdb.h | 9 +++++++++
source4/libcli/ldap/ldap_controls.c | 1 +
source4/setup/schema_samba4.ldif | 1 +
3 files changed, 11 insertions(+)
diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h
index d2686af..65d22ea 100644
--- a/source4/dsdb/samdb/samdb.h
+++ b/source4/dsdb/samdb/samdb.h
@@ -204,6 +204,15 @@ struct dsdb_control_password_acl_validation {
bool pwd_reset;
};
+/*
+ * Used to pass the current transaction identifier from the audit_log
+ * module to group membership auditing module
+ */
+#define DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID "1.3.6.1.4.1.7165.4.3.34"
+struct dsdb_control_transaction_identifier {
+ struct GUID transaction_guid;
+};
+
#define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1"
struct dsdb_extended_replicated_object {
struct ldb_message *msg;
diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c
index 7ecc908..716ca14 100644
--- a/source4/libcli/ldap/ldap_controls.c
+++ b/source4/libcli/ldap/ldap_controls.c
@@ -1272,6 +1272,7 @@ static const struct ldap_control_handler ldap_known_controls[] = {
{ DSDB_EXTENDED_ALLOCATE_RID_POOL, NULL, NULL },
{ DSDB_CONTROL_NO_GLOBAL_CATALOG, NULL, NULL },
{ DSDB_EXTENDED_SCHEMA_UPGRADE_IN_PROGRESS_OID, NULL, NULL },
+ { DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID, NULL, NULL},
{ NULL, NULL, NULL }
};
diff --git a/source4/setup/schema_samba4.ldif b/source4/setup/schema_samba4.ldif
index 6aafc9e..5b26dc0 100644
--- a/source4/setup/schema_samba4.ldif
+++ b/source4/setup/schema_samba4.ldif
@@ -227,6 +227,7 @@
#Allocated: DSDB_CONTROL_FORCE_RODC_LOCAL_CHANGE 1.3.6.1.4.1.7165.4.3.31
#Allocated: DSDB_CONTROL_INVALID_NOT_IMPLEMENTED 1.3.6.1.4.1.7165.4.3.32
#Allocated: DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID 1.3.6.1.4.1.7165.4.3.33
+#Allocated: DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID 1.3.6.1.4.1.7165.4.3.34
# Extended 1.3.6.1.4.1.7165.4.4.x
--
2.7.4
From f587c2a9927e32c1cd4145ab80345bf422e3e436 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 16 Apr 2018 14:01:13 +1200
Subject: [PATCH 17/21] SamDb audit logging: Add transaction id control to
requests
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
source4/dsdb/samdb/ldb_modules/audit_log.c | 37 ++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/source4/dsdb/samdb/ldb_modules/audit_log.c b/source4/dsdb/samdb/ldb_modules/audit_log.c
index 67182d2..b0c8e13 100644
--- a/source4/dsdb/samdb/ldb_modules/audit_log.c
+++ b/source4/dsdb/samdb/ldb_modules/audit_log.c
@@ -725,6 +725,31 @@ static int audit_callback(struct ldb_request *req, struct ldb_reply *ares)
}
}
+static int add_transaction_id(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+ struct dsdb_control_transaction_identifier *transaction_id;
+
+ transaction_id = talloc_zero(
+ req,
+ struct dsdb_control_transaction_identifier);
+ if (transaction_id == NULL) {
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ return ldb_oom(ldb);
+ }
+ transaction_id->transaction_guid = ac->transaction_guid;
+ ldb_request_add_control(req,
+ DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID,
+ false,
+ transaction_id);
+ return LDB_SUCCESS;
+
+}
+
static int log_add(
struct ldb_module *module,
struct ldb_request *req)
@@ -758,6 +783,10 @@ static int log_add(
if (ret != LDB_SUCCESS) {
return ret;
}
+ ret = add_transaction_id(module, new_req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
return ldb_next_request(module, new_req);
}
@@ -794,6 +823,10 @@ static int log_delete(
if (ret != LDB_SUCCESS) {
return ret;
}
+ ret = add_transaction_id(module, new_req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
return ldb_next_request(module, new_req);
}
@@ -830,6 +863,10 @@ static int log_modify(
if (ret != LDB_SUCCESS) {
return ret;
}
+ ret = add_transaction_id(module, new_req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
return ldb_next_request(module, new_req);
}
--
2.7.4
From 6eb91f54793bd5d8c091a74d7e5f6602403b9762 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 23 Apr 2018 08:49:26 +1200
Subject: [PATCH 18/21] messaging idl add group membersip events
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
librpc/idl/messaging.idl | 2 ++
1 file changed, 2 insertions(+)
diff --git a/librpc/idl/messaging.idl b/librpc/idl/messaging.idl
index 73cceff..b0f0ef0 100644
--- a/librpc/idl/messaging.idl
+++ b/librpc/idl/messaging.idl
@@ -145,6 +145,7 @@ interface messaging
MSG_AUTH_LOG = 0x0800,
MSG_SAMDB_LOG = 0x0801,
MSG_PWD_LOG = 0x0802,
+ MSG_GROUP_LOG = 0x0803,
/* dbwrap messages 4001-4999 (0x0FA0 - 0x1387) */
/* MSG_DBWRAP_TDB2_CHANGES = 4001, */
@@ -184,4 +185,5 @@ interface messaging
const string AUTH_EVENT_NAME = "auth_event";
const string SAMDB_EVENT_NAME = "samdb_event";
const string PWD_EVENT_NAME = "password_event";
+ const string GROUP_EVENT_NAME = "group_event";
}
--
2.7.4
From e82a16a1e1a2ffd6830e6cba07c005be283b9a29 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 23 Apr 2018 09:00:54 +1200
Subject: [PATCH 19/21] smb.conf: Add group change notification parameter
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
.../smbdotconf/misc/groupchangenotification.xml | 27 ++++++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 docs-xml/smbdotconf/misc/groupchangenotification.xml
diff --git a/docs-xml/smbdotconf/misc/groupchangenotification.xml b/docs-xml/smbdotconf/misc/groupchangenotification.xml
new file mode 100644
index 0000000..09278cc
--- /dev/null
+++ b/docs-xml/smbdotconf/misc/groupchangenotification.xml
@@ -0,0 +1,27 @@
+<samba:parameter name="group change notification"
+ context="G"
+ type="boolean"
+ xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+ <para>When enabled, this option causes Samba (acting as an
+ Active Directory Domain Controller) to stream group membership change
+ events across the internal message bus. Scripts built using
+ Samba's python bindings can listen to these events by
+ registering as the service
+ <filename moreinfo="none">group_event</filename>.</para>
+
+ <para>This should be considered a developer option (it assists
+ in the Samba testsuite) rather than a facility for external
+ auditing, as message delivery is not guaranteed (a feature
+ that the testsuite works around). Additionally Samba must be
+ compiled with the jansson support for this option to be
+ effective.</para>
+
+ <para>The group events are also logged via the normal
+ logging methods when the <smbconfoption name="log level"/> is
+ set appropriately.</para>
+
+</description>
+
+<value type="default">no</value>
+</samba:parameter>
--
2.7.4
From 24d409c4706e6986eacf85567e1f4ae1d8e3f5a6 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 23 Apr 2018 12:24:34 +1200
Subject: [PATCH 20/21] debug: Add group logging classes
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
lib/util/debug.c | 2 ++
lib/util/debug.h | 2 ++
2 files changed, 4 insertions(+)
diff --git a/lib/util/debug.c b/lib/util/debug.c
index db4f71a..9e6b5d8 100644
--- a/lib/util/debug.c
+++ b/lib/util/debug.c
@@ -549,6 +549,8 @@ static const char *default_classname_table[] = {
[DBGC_PWD_AUDIT_JSON] = "password_json_audit",
[DBGC_TRN_AUDIT] = "transaction_audit",
[DBGC_TRN_AUDIT_JSON] = "transaction_json_audit",
+ [DBGC_GROUP_AUDIT] = "group_audit",
+ [DBGC_GROUP_AUDIT_JSON] = "group_json_audit",
};
/*
diff --git a/lib/util/debug.h b/lib/util/debug.h
index 4e425a0..67b737e 100644
--- a/lib/util/debug.h
+++ b/lib/util/debug.h
@@ -101,6 +101,8 @@ bool dbghdr( int level, const char *location, const char *func);
#define DBGC_PWD_AUDIT_JSON 33
#define DBGC_TRN_AUDIT 34
#define DBGC_TRN_AUDIT_JSON 35
+#define DBGC_GROUP_AUDIT 36
+#define DBGC_GROUP_AUDIT_JSON 37
/* So you can define DBGC_CLASS before including debug.h */
#ifndef DBGC_CLASS
--
2.7.4
From 64a695f35f124b8c95971bde1a197d11c99857b7 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 16 Apr 2018 14:03:14 +1200
Subject: [PATCH 21/21] SamDb: Audit group membership changes.:w
Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
---
source4/dsdb/samdb/ldb_modules/group_audit.c | 936 +++++++++++++++++++++
source4/dsdb/samdb/ldb_modules/samba_dsdb.c | 1 +
.../dsdb/samdb/ldb_modules/wscript_build_server | 16 +
3 files changed, 953 insertions(+)
create mode 100644 source4/dsdb/samdb/ldb_modules/group_audit.c
diff --git a/source4/dsdb/samdb/ldb_modules/group_audit.c b/source4/dsdb/samdb/ldb_modules/group_audit.c
new file mode 100644
index 0000000..019a829
--- /dev/null
+++ b/source4/dsdb/samdb/ldb_modules/group_audit.c
@@ -0,0 +1,936 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett <abartlet at samba.org> 2018
+
+ 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/>.
+*/
+
+/*
+ * Provide an audit log of changes made to group memberships
+ *
+ */
+
+#include "includes.h"
+#include "ldb_module.h"
+#include "lib/audit_logging/audit_logging.h"
+
+#include "dsdb/samdb/samdb.h"
+#include "dsdb/samdb/ldb_modules/util.h"
+#include "libcli/security/dom_sid.h"
+#include "auth/common_auth.h"
+#include "param/param.h"
+
+#define AUDIT_JSON_TYPE "groupChange"
+#define AUDIT_HR_TAG "Group Change"
+#define AUDIT_MAJOR 1
+#define AUDIT_MINOR 0
+#define GROUP_LOG_LVL 5
+
+static const char * const member_attr[] = {"member", NULL};
+static const char * const primary_group_attr[] = {"primaryGroupID",
+ "objectSID",
+ NULL};
+
+struct audit_context {
+ bool send_events;
+ struct imessaging_context *msg_ctx;
+};
+
+struct audit_callback_context {
+ struct ldb_request *request;
+ struct ldb_module *module;
+ struct ldb_message_element *members;
+ uint32_t primary_group;
+ void (*log_changes)(struct audit_callback_context *acc,
+ const int status);
+};
+
+static struct GUID *get_transaction_id(
+ const struct ldb_request *request)
+{
+ struct ldb_control *control;
+ struct dsdb_control_transaction_identifier *transaction_id;
+
+ control = ldb_request_get_control(
+ discard_const(request),
+ DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID);
+ if (control == NULL) {
+ return NULL;
+ }
+ transaction_id = talloc_get_type(
+ control->data,
+ struct dsdb_control_transaction_identifier);
+ if (transaction_id == NULL) {
+ return NULL;
+ }
+ return &transaction_id->transaction_guid;
+}
+
+static struct json_object audit_group_json(
+ const struct ldb_module *module,
+ const struct ldb_request *request,
+ const char *action,
+ const char *user,
+ const char *group,
+ const int status)
+{
+ struct ldb_context *ldb = NULL;
+ const struct dom_sid *sid = NULL;
+ struct json_object wrapper;
+ struct json_object audit;
+ const struct tsocket_address *remote = NULL;
+ const struct GUID *unique_session_token = NULL;
+ struct GUID *transaction_id = NULL;
+
+ ldb = ldb_module_get_ctx(discard_const(module));
+
+ remote = get_remote_address(ldb);
+ sid = get_user_sid(module);
+ unique_session_token = get_unique_session_token(module);
+ transaction_id = get_transaction_id(request);
+
+ audit = json_new_object();
+ json_add_version(&audit, AUDIT_MAJOR, AUDIT_MINOR);
+ json_add_int(&audit, "statusCode", status);
+ json_add_string(&audit, "status", ldb_strerror(status));
+ json_add_string(&audit, "action", action);
+ json_add_address(&audit, "remoteAddress", remote);
+ json_add_sid(&audit, "userSid", sid);
+ json_add_string(&audit, "group", group);
+ json_add_guid(&audit, "transactionId", transaction_id);
+ json_add_guid(&audit, "sessionId", unique_session_token);
+ json_add_string(&audit, "user", user);
+
+ wrapper = json_new_object();
+ json_add_timestamp(&wrapper);
+ json_add_string(&wrapper, "type", AUDIT_JSON_TYPE);
+ json_add_object(&wrapper, AUDIT_JSON_TYPE, &audit);
+
+ return wrapper;
+}
+
+static char *audit_group_human_readable(
+ TALLOC_CTX *mem_ctx,
+ const struct ldb_module *module,
+ const struct ldb_request *request,
+ const char *action,
+ const char *user,
+ const char * group,
+ const int status)
+{
+ struct ldb_context *ldb = NULL;
+ const char *remote_host = NULL;
+ const struct dom_sid *sid = NULL;
+ const char *user_sid = NULL;
+ const char *timestamp = NULL;
+ char *log_entry = NULL;
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ ldb = ldb_module_get_ctx(discard_const(module));
+
+ remote_host = get_remote_host(ldb, frame);
+ sid = get_user_sid(module);
+ user_sid = dom_sid_string(frame, sid);
+ timestamp = audit_get_timestamp(frame);
+
+ log_entry = talloc_asprintf(
+ mem_ctx,
+ "[%s] at [%s] status [%s] "
+ "Remote host [%s] SID [%s] Group [%s] User [%s]",
+ action,
+ timestamp,
+ ldb_strerror(status),
+ remote_host,
+ user_sid,
+ group,
+ user);
+ TALLOC_FREE(frame);
+ return log_entry;
+}
+
+/*
+ * Get an array of 'struct parsed_dns' without the parsing.
+ * The parsed_dns are parsed only when needed to avoid the expense of parsing.
+ *
+ * This procedure assumes that the dn's are sorted in GUID order and contains
+ * no duplicates. This should be valid as the module sits below repl_meta_data
+ * which ensures this.
+ */
+static struct parsed_dn *get_parsed_dns(
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *el)
+{
+ struct parsed_dn *pdn = NULL;
+
+ int i;
+
+ if (el == NULL || el->num_values == 0) {
+ return NULL;
+ }
+
+ pdn = talloc_zero_array(mem_ctx, struct parsed_dn, el->num_values);
+ if (pdn == NULL) {
+ // TODO error handling/logging
+ return NULL;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ pdn[i].v = &el->values[i];
+ }
+ return pdn;
+
+}
+
+static int dn_compare(
+ TALLOC_CTX *mem_ctx,
+ struct ldb_context *ldb,
+ struct parsed_dn *old_val,
+ struct parsed_dn *new_val) {
+
+ /*
+ * Do a binary compare first to avoid unnecessary parsing
+ */
+ if (data_blob_cmp(old_val->v, new_val->v) == 0) {
+ /*
+ * Values are equal at a binary level so no need
+ * for further processing
+ */
+ return 0;
+ }
+ /*
+ * Values not equal at the binary level, so lets
+ * do a GUID ordering compare. To do this we will need to ensure
+ * that the dn's have been parsed.
+ */
+ really_parse_trusted_dn(mem_ctx,
+ ldb,
+ old_val,
+ LDB_SYNTAX_DN);
+ really_parse_trusted_dn(mem_ctx,
+ ldb,
+ new_val,
+ LDB_SYNTAX_DN);
+
+
+ return ndr_guid_compare(&new_val->guid, &old_val->guid);
+}
+
+static const char *get_primary_group_dn(
+ TALLOC_CTX *mem_ctx,
+ struct ldb_module *module,
+ struct dom_sid *account_sid,
+ uint32_t primary_group_rid)
+{
+ NTSTATUS status;
+
+ struct ldb_context *ldb = NULL;
+ struct dom_sid *domain_sid = NULL;
+ struct dom_sid *primary_group_sid = NULL;
+ char *sid = NULL;
+ struct ldb_dn *dn = NULL;
+ struct ldb_message *msg = NULL;
+ int rc;
+
+ ldb = ldb_module_get_ctx(module);
+
+ status = dom_sid_split_rid(mem_ctx, account_sid, &domain_sid, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ primary_group_sid = dom_sid_add_rid(
+ mem_ctx,
+ domain_sid,
+ primary_group_rid);
+ if (!primary_group_sid) {
+ return NULL;
+ }
+
+ sid = dom_sid_string(mem_ctx, primary_group_sid);
+ if (sid == NULL) {
+ return NULL;
+ }
+
+ dn = ldb_dn_new_fmt(mem_ctx, ldb, "<SID=%s>", sid);
+ if(dn == NULL) {
+ return sid;
+ }
+ rc = dsdb_search_one(
+ ldb,
+ mem_ctx,
+ &msg,
+ dn,
+ LDB_SCOPE_BASE,
+ NULL,
+ 0,
+ NULL);
+ if (rc != LDB_SUCCESS) {
+ return NULL;
+ }
+
+ return ldb_dn_get_linearized(msg->dn);
+}
+
+static void log_primary_group_change(
+ struct ldb_module *module,
+ const struct ldb_request *request,
+ const char *action,
+ const char *group,
+ const int status)
+{
+ const char *user = NULL;
+
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ user = get_primary_dn(request);
+ if (CHECK_DEBUGLVLC(DBGC_GROUP_AUDIT, GROUP_LOG_LVL)) {
+ char *message = NULL;
+ message = audit_group_human_readable(
+ frame,
+ module,
+ request,
+ action,
+ user,
+ group,
+ status);
+ audit_log_hr(
+ AUDIT_HR_TAG,
+ message,
+ DBGC_GROUP_AUDIT,
+ GROUP_LOG_LVL);
+ TALLOC_FREE(message);
+ }
+
+#ifdef HAVE_JANSSON
+ if (CHECK_DEBUGLVLC(DBGC_GROUP_AUDIT_JSON, GROUP_LOG_LVL)) {
+ struct json_object json;
+ json = audit_group_json(
+ module,
+ request,
+ action,
+ user,
+ group,
+ status);
+ audit_log_json(
+ AUDIT_JSON_TYPE,
+ &json,
+ DBGC_GROUP_AUDIT_JSON,
+ GROUP_LOG_LVL);
+ if (ac->send_events) {
+ audit_message_send(
+ ac->msg_ctx,
+ GROUP_EVENT_NAME,
+ MSG_GROUP_LOG,
+ &json);
+ }
+ json_free(&json);
+ }
+#endif
+ TALLOC_FREE(frame);
+}
+
+static void log_membership_change(
+ struct ldb_module *module,
+ const struct ldb_request *request,
+ const char *action,
+ const char *user,
+ const int status)
+{
+ const char *group = NULL;
+ struct audit_context *ac =
+ talloc_get_type(ldb_module_get_private(module),
+ struct audit_context);
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ group = get_primary_dn(request);
+ if (CHECK_DEBUGLVLC(DBGC_GROUP_AUDIT, GROUP_LOG_LVL)) {
+ char *message = NULL;
+ message = audit_group_human_readable(frame,
+ module,
+ request,
+ action,
+ user,
+ group,
+ status);
+ audit_log_hr(
+ AUDIT_HR_TAG,
+ message,
+ DBGC_GROUP_AUDIT,
+ GROUP_LOG_LVL);
+ TALLOC_FREE(message);
+ }
+
+#ifdef HAVE_JANSSON
+ if (CHECK_DEBUGLVLC(DBGC_GROUP_AUDIT_JSON, GROUP_LOG_LVL)) {
+ struct json_object json;
+ json = audit_group_json(
+ module,
+ request,
+ action,
+ user,
+ group,
+ status);
+ audit_log_json(
+ AUDIT_JSON_TYPE,
+ &json,
+ DBGC_GROUP_AUDIT_JSON,
+ GROUP_LOG_LVL);
+ if (ac->send_events) {
+ audit_message_send(
+ ac->msg_ctx,
+ GROUP_EVENT_NAME,
+ MSG_GROUP_LOG,
+ &json);
+ }
+ json_free(&json);
+ }
+#endif
+ TALLOC_FREE(frame);
+}
+
+static void log_membership_changes(
+ struct ldb_module *module,
+ const struct ldb_request *request,
+ struct ldb_message_element *el,
+ struct ldb_message_element *old_el,
+ int status)
+{
+ unsigned int i, old_i, new_i;
+ unsigned int old_num_values;
+ unsigned int max_num_values;
+ unsigned int new_num_values;
+ struct parsed_dn *old_val = NULL;
+ struct parsed_dn *new_val = NULL;
+ struct parsed_dn *new_values = NULL;
+ struct parsed_dn *old_values = NULL;
+ struct ldb_context *ldb = NULL;
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ old_num_values = old_el ? old_el->num_values : 0;
+ new_num_values = el ? el->num_values : 0;
+ max_num_values = old_num_values + new_num_values;
+
+ if (max_num_values == 0) {
+ /* There is nothing to do! */
+ }
+
+ old_values = get_parsed_dns(frame, old_el);
+ new_values = get_parsed_dns(frame, el);
+ ldb = ldb_module_get_ctx(module);
+
+ old_i = 0;
+ new_i = 0;
+ for (i = 0; i < max_num_values; i++) {
+ int cmp;
+ if (old_i < old_num_values && new_i < new_num_values) {
+ old_val = &old_values[old_i];
+ new_val = &new_values[new_i];
+ // samba_start_debugger();
+ cmp = dn_compare(frame, ldb, old_val, new_val);
+ } else if (old_i < old_num_values) {
+ /* the new list is empty, read the old list */
+ old_val = &old_values[old_i];
+ new_val = NULL;
+ cmp = -1;
+ } else if (new_i < new_num_values) {
+ /* the old list is empty, read new list */
+ old_val = NULL;
+ new_val = &new_values[new_i];
+ cmp = 1;
+ } else {
+ break;
+ }
+
+ if (cmp < 0) {
+ /*
+ * Have an entry in the original record that is not in
+ * the new record. So it's been deleted
+ */
+ const char *user = NULL;
+ really_parse_trusted_dn(frame,
+ ldb,
+ old_val,
+ LDB_SYNTAX_DN);
+ user = ldb_dn_get_linearized(old_val->dsdb_dn->dn);
+ log_membership_change(module,
+ request,
+ "Removed",
+ user,
+ status);
+ old_i++;
+ } else if (cmp == 0) {
+ /*
+ * Value is unchanged so need to log it,
+ * Note: This currently ignores the setting of the
+ * deleted flags. i.e. a change is not logged
+ * if the group is deleted or undeleted.
+ */
+ old_i++;
+ new_i++;
+ } else {
+ /*
+ * Member in the updated record that's not in the
+ * original, so it must have been added.
+ */
+ const char *user = NULL;
+ really_parse_trusted_dn(frame,
+ ldb,
+ new_val,
+ LDB_SYNTAX_DN);
+ user = ldb_dn_get_linearized(new_val->dsdb_dn->dn);
+ log_membership_change(module,
+ request,
+ "Added",
+ user,
+ status);
+ new_i++;
+ }
+ }
+
+ TALLOC_FREE(frame);
+}
+
+
+static void log_user(
+ struct audit_callback_context *acc,
+ const int status)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ uint32_t new_rid;
+ struct dom_sid *account_sid = NULL;
+ int ret;
+ const struct ldb_message *msg = get_message(acc->request);
+ if (status == LDB_SUCCESS && msg != NULL) {
+ struct ldb_result *res = NULL;
+ ret = dsdb_module_search_dn(acc->module,
+ frame,
+ &res,
+ msg->dn,
+ primary_group_attr,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_REVEAL_INTERNALS |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
+ NULL);
+ if (ret == LDB_SUCCESS) {
+ new_rid = ldb_msg_find_attr_as_uint(msg,
+ "primaryGroupID",
+ ~0);
+ account_sid = samdb_result_dom_sid(frame,
+ res->msgs[0],
+ "objectSid");
+ }
+ }
+ /*
+ * If we don't have a new value then the user has been deleted
+ * which we currently do not log.
+ * Otherwise only log if the primary group has actually changed.
+ */
+ if (account_sid != NULL &&
+ new_rid != ~0 &&
+ acc->primary_group!= new_rid) {
+ const char* group = get_primary_group_dn(frame,
+ acc->module,
+ account_sid,
+ new_rid);
+ log_primary_group_change(acc->module,
+ acc->request,
+ "PrimaryGroup",
+ group,
+ status);
+ }
+ TALLOC_FREE(frame);
+}
+
+static void log_group(
+ struct audit_callback_context *acc,
+ const int status)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct ldb_message_element *new_val = NULL;
+ int ret;
+ const struct ldb_message *msg = get_message(acc->request);
+ if (status == LDB_SUCCESS && msg != NULL) {
+ struct ldb_result *res = NULL;
+ ret = dsdb_module_search_dn(acc->module,
+ frame,
+ &res,
+ msg->dn,
+ member_attr,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_REVEAL_INTERNALS |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
+ NULL);
+ if (ret == LDB_SUCCESS) {
+ new_val = ldb_msg_find_element(res->msgs[0], "member");
+ }
+ }
+ log_membership_changes(acc->module,
+ acc->request,
+ new_val,
+ acc->members,
+ status);
+ TALLOC_FREE(frame);
+}
+
+static int group_audit_callback(
+ struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct audit_callback_context *ac = NULL;
+
+ ac = talloc_get_type(req->context,
+ struct audit_callback_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->request, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* pass on to the callback */
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ return ldb_module_send_entry(ac->request,
+ ares->message,
+ ares->controls);
+
+ case LDB_REPLY_REFERRAL:
+ return ldb_module_send_referral(ac->request,
+ ares->referral);
+
+ case LDB_REPLY_DONE:
+ /*
+ * Log on DONE now we have a result code
+ */
+ ac->log_changes(ac, ares->error);
+ return ldb_module_done(ac->request,
+ ares->controls,
+ ares->response,
+ ares->error);
+ break;
+
+ default:
+ /* Can't happen */
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+}
+
+static bool has_primary_group_id(struct ldb_request *req)
+{
+ struct ldb_message_element *el = NULL;
+ const struct ldb_message *msg = NULL;
+
+ msg = get_message(req);
+ el = ldb_msg_find_element(msg, "primaryGroupID");
+
+ return (el != NULL);
+}
+
+static bool has_group_membership_changes(struct ldb_request *req)
+{
+ struct ldb_message_element *el = NULL;
+ const struct ldb_message *msg = NULL;
+
+ msg = get_message(req);
+ el = ldb_msg_find_element(msg, "member");
+
+ return (el != NULL);
+}
+
+
+static int set_group_add_callback(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct audit_callback_context *context = NULL;
+ struct ldb_request *new_req = NULL;
+ struct ldb_context *ldb = NULL;
+ int ret;
+ /*
+ * Adding group memberships so will need to log the changes.
+ */
+ ldb = ldb_module_get_ctx(module);
+ context = talloc_zero(req, struct audit_callback_context);
+
+ if (context == NULL) {
+ return ldb_oom(ldb);
+ }
+ context->request = req;
+ context->module = module;
+ context->log_changes = log_group;
+ /*
+ * We want to log the return code status, so we need to register
+ * a callback function to get the actual result.
+ * We need to take a new copy so that we don't alter the callers copy
+ */
+ ret = ldb_build_add_req(&new_req,
+ ldb,
+ req,
+ req->op.add.message,
+ req->controls,
+ context,
+ group_audit_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return ldb_next_request(module, new_req);
+}
+
+
+static int set_user_modify_callback(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct audit_callback_context *context = NULL;
+ struct ldb_request *new_req = NULL;
+ struct ldb_context *ldb = NULL;
+ const struct ldb_message *msg = NULL;
+ struct ldb_result *res = NULL;
+ int ret;
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ ldb = ldb_module_get_ctx(module);
+
+ context = talloc_zero(req, struct audit_callback_context);
+ if (context == NULL) {
+ ret = ldb_oom(ldb);
+ goto exit;
+ }
+ context->request = req;
+ context->module = module;
+ context->log_changes = log_user;
+
+ msg = get_message(req);
+ ret = dsdb_module_search_dn(module,
+ frame,
+ &res,
+ msg->dn,
+ primary_group_attr,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_REVEAL_INTERNALS |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
+ NULL);
+ if (ret == LDB_SUCCESS) {
+ uint32_t pg;
+ pg = ldb_msg_find_attr_as_uint(res->msgs[0],
+ "primaryGroupID",
+ ~0);
+ context->primary_group = pg;
+ }
+ /*
+ * We want to log the return code status, so we need to register
+ * a callback function to get the actual result.
+ * We need to take a new copy so that we don't alter the callers copy
+ */
+ ret = ldb_build_mod_req(&new_req,
+ ldb,
+ req,
+ req->op.add.message,
+ req->controls,
+ context,
+ group_audit_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ goto exit;
+ }
+ ret = ldb_next_request(module, new_req);
+exit:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+static int set_user_add_callback(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct audit_callback_context *context = NULL;
+ struct ldb_request *new_req = NULL;
+ struct ldb_context *ldb = NULL;
+ int ret;
+ /*
+ * Adding a user with a primary group.
+ */
+ ldb = ldb_module_get_ctx(module);
+ context = talloc_zero(req, struct audit_callback_context);
+
+ if (context == NULL) {
+ return ldb_oom(ldb);
+ }
+ context->request = req;
+ context->module = module;
+ context->log_changes = log_user;
+ /*
+ * We want to log the return code status, so we need to register
+ * a callback function to get the actual result.
+ * We need to take a new copy so that we don't alter the callers copy
+ */
+ ret = ldb_build_add_req(&new_req,
+ ldb,
+ req,
+ req->op.add.message,
+ req->controls,
+ context,
+ group_audit_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return ldb_next_request(module, new_req);
+}
+
+static int group_add(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+
+ if (CHECK_DEBUGLVLC(DBGC_GROUP_AUDIT, GROUP_LOG_LVL) ||
+ CHECK_DEBUGLVLC(DBGC_GROUP_AUDIT_JSON, GROUP_LOG_LVL)) {
+ /*
+ * Avoid the overheads of logging unless it has been
+ * enabled
+ */
+ if (has_group_membership_changes(req)) {
+ return set_group_add_callback(module, req);
+ }
+ if (has_primary_group_id(req)) {
+ return set_user_add_callback(module, req);
+ }
+ }
+ return ldb_next_request(module, req);
+}
+
+static int group_delete(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+static int set_group_modify_callback(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+ struct audit_callback_context *context = NULL;
+ struct ldb_request *new_req = NULL;
+ struct ldb_context *ldb = NULL;
+ struct ldb_result *res = NULL;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+ context = talloc_zero(req, struct audit_callback_context);
+
+ if (context == NULL) {
+ return ldb_oom(ldb);
+ }
+ context->request = req;
+ context->module = module;
+ context->log_changes = log_group;
+
+ /*
+ * About to change the group memberships need to read
+ * the current state from the database.
+ */
+ ret = dsdb_module_search_dn(module,
+ context,
+ &res,
+ req->op.add.message->dn,
+ member_attr,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_REVEAL_INTERNALS |
+ DSDB_SEARCH_SHOW_DN_IN_STORAGE_FORMAT,
+ NULL);
+ if (ret == LDB_SUCCESS) {
+ context->members = ldb_msg_find_element(res->msgs[0], "member");
+ }
+
+ ret = ldb_build_mod_req(&new_req,
+ ldb,
+ req,
+ req->op.mod.message,
+ req->controls,
+ context,
+ group_audit_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return ldb_next_request(module, new_req);
+}
+
+static int group_modify(
+ struct ldb_module *module,
+ struct ldb_request *req)
+{
+
+ if (CHECK_DEBUGLVLC(DBGC_GROUP_AUDIT, GROUP_LOG_LVL) ||
+ CHECK_DEBUGLVLC(DBGC_GROUP_AUDIT_JSON, GROUP_LOG_LVL)) {
+ /*
+ * Avoid the overheads of logging unless it has been
+ * enabled
+ */
+ if (has_group_membership_changes(req)) {
+ return set_group_modify_callback(module, req);
+ }
+ if (has_primary_group_id(req)) {
+ return set_user_modify_callback(module, req);
+ }
+ }
+ return ldb_next_request(module, req);
+}
+
+static int group_init(struct ldb_module *module)
+{
+
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct audit_context *context = NULL;
+ struct loadparm_context *lp_ctx
+ = talloc_get_type_abort(ldb_get_opaque(ldb, "loadparm"),
+ struct loadparm_context);
+ struct tevent_context *ec = ldb_get_event_context(ldb);
+
+ context = talloc_zero(module, struct audit_context);
+ if (context == NULL) {
+ return ldb_module_oom(module);
+ }
+
+ if (lp_ctx && lpcfg_group_change_notification(lp_ctx)) {
+ context->send_events = true;
+ context->msg_ctx = imessaging_client_init(ec, lp_ctx, ec);
+ }
+
+ ldb_module_set_private(module, context);
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ldb_group_audit_log_module_ops = {
+ .name = "group_audit_log",
+ .add = group_add,
+ .modify = group_modify,
+ .del = group_delete,
+ .init_context = group_init,
+};
+
+int ldb_group_audit_log_module_init(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_group_audit_log_module_ops);
+}
diff --git a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
index baa30f9..fa58f19 100644
--- a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
+++ b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c
@@ -313,6 +313,7 @@ static int samba_dsdb_init(struct ldb_module *module)
"rdn_name",
"subtree_delete",
"repl_meta_data",
+ "group_audit_log",
"encrypted_secrets",
"operational",
"unique_object_sids",
diff --git a/source4/dsdb/samdb/ldb_modules/wscript_build_server b/source4/dsdb/samdb/ldb_modules/wscript_build_server
index 6c821fb..e5c5032 100644
--- a/source4/dsdb/samdb/ldb_modules/wscript_build_server
+++ b/source4/dsdb/samdb/ldb_modules/wscript_build_server
@@ -441,3 +441,19 @@ bld.SAMBA_MODULE('ldb_audit_log',
samdb
'''
)
+
+bld.SAMBA_MODULE('ldb_group_audit_log',
+ source='group_audit.c',
+ subsystem='ldb',
+ init_function='ldb_group_audit_log_module_init',
+ module_init_name='ldb_init_module',
+ internal_module=False,
+ deps='''
+ audit_logging
+ talloc
+ samba-util
+ samdb-common
+ DSDB_MODULE_HELPERS
+ samdb
+ '''
+ )
--
2.7.4
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20180507/70fd2314/signature-0001.sig>
More information about the samba-technical
mailing list