summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2021-08-05 11:26:16 +0200
committerJule Anger <janger@samba.org>2021-11-08 10:52:13 +0100
commit5337dc5eaeb7abba3aeb73cc6ef0471ffc9e7bc8 (patch)
tree2d8e142eeda991a6c1443292bda53ab7f9773b2b
parentec1ea05e8f1dcca14ed7a92a0645da9ff73f33d3 (diff)
downloadsamba-5337dc5eaeb7abba3aeb73cc6ef0471ffc9e7bc8.tar.gz
CVE-2021-3738 s4:torture/drsuapi: DsBindAssocGroup* tests
This adds a reproducer for an invalid memory access, when using the context handle from DsBind across multiple connections within an association group. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14468 Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
-rw-r--r--selftest/knownfail.d/drsuapi.DsBindAssocGroupWS1
-rw-r--r--source4/torture/rpc/drsuapi.c172
2 files changed, 173 insertions, 0 deletions
diff --git a/selftest/knownfail.d/drsuapi.DsBindAssocGroupWS b/selftest/knownfail.d/drsuapi.DsBindAssocGroupWS
new file mode 100644
index 00000000000..9af5a904fdd
--- /dev/null
+++ b/selftest/knownfail.d/drsuapi.DsBindAssocGroupWS
@@ -0,0 +1 @@
+^samba4.rpc.drsuapi.*drsuapi.DsBindAssocGroupWS
diff --git a/source4/torture/rpc/drsuapi.c b/source4/torture/rpc/drsuapi.c
index 3e8105af07b..1cd8f77db9c 100644
--- a/source4/torture/rpc/drsuapi.c
+++ b/source4/torture/rpc/drsuapi.c
@@ -25,6 +25,7 @@
#include "lib/cmdline/popt_common.h"
#include "librpc/gen_ndr/ndr_drsuapi_c.h"
#include "torture/rpc/torture_rpc.h"
+#include "libcli/security/dom_sid.h"
#include "param/param.h"
#define TEST_MACHINE_NAME "torturetest"
@@ -845,6 +846,173 @@ static bool torture_drsuapi_tcase_teardown(struct torture_context *tctx, void *d
return ret;
}
+static bool __test_DsBind_assoc_group(struct torture_context *tctx,
+ const char *testname,
+ struct DsPrivate *priv,
+ struct cli_credentials *creds)
+{
+ NTSTATUS status;
+ const char *err_msg;
+ struct drsuapi_DsCrackNames r;
+ union drsuapi_DsNameRequest req;
+ uint32_t level_out;
+ union drsuapi_DsNameCtr ctr;
+ struct drsuapi_DsNameString names[1];
+ const char *dom_sid = NULL;
+ struct dcerpc_pipe *p1 = NULL;
+ struct dcerpc_pipe *p2 = NULL;
+ TALLOC_CTX *mem_ctx = priv;
+ struct dcerpc_binding *binding = NULL;
+ struct policy_handle ds_bind_handle = { .handle_type = 0, };
+
+ torture_comment(tctx, "%s: starting...\n", testname);
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_binding(tctx, &binding),
+ "torture_rpc_binding");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx,
+ &p1,
+ binding,
+ &ndr_table_drsuapi,
+ creds,
+ tctx->ev,
+ tctx->lp_ctx),
+ "connect p1");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx,
+ &p2,
+ p1->binding,
+ &ndr_table_drsuapi,
+ creds,
+ tctx->ev,
+ tctx->lp_ctx),
+ "connect p2");
+
+ torture_assert(tctx, test_DsBind(p1, tctx, &ds_bind_handle, NULL), "DsBind");
+
+ ZERO_STRUCT(r);
+ r.in.bind_handle = &ds_bind_handle;
+ r.in.level = 1;
+ r.in.req = &req;
+ r.in.req->req1.codepage = 1252; /* german */
+ r.in.req->req1.language = 0x00000407; /* german */
+ r.in.req->req1.count = 1;
+ r.in.req->req1.names = names;
+ r.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
+
+ r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
+ r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+
+ r.out.level_out = &level_out;
+ r.out.ctr = &ctr;
+
+ dom_sid = dom_sid_string(mem_ctx, torture_join_sid(priv->join));
+
+ names[0].str = dom_sid;
+
+ torture_comment(tctx, "Testing DsCrackNames on p1 with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+ status = dcerpc_drsuapi_DsCrackNames_r(p1->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ torture_comment(tctx, "Testing DsCrackNames on p2 with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+ status = dcerpc_drsuapi_DsCrackNames_r(p2->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ TALLOC_FREE(p1);
+
+ torture_comment(tctx, "Testing DsCrackNames on p2 (with p1 closed) with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+ status = dcerpc_drsuapi_DsCrackNames_r(p2->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ torture_comment(tctx, "%s: ... finished\n", testname);
+ return true;
+}
+
+static bool test_DsBindAssocGroupAdmin(struct torture_context *tctx,
+ struct DsPrivate *priv,
+ struct cli_credentials *creds)
+{
+ return __test_DsBind_assoc_group(tctx, __func__, priv,
+ priv->admin_credentials);
+}
+
+static bool test_DsBindAssocGroupDC(struct torture_context *tctx,
+ struct DsPrivate *priv,
+ struct cli_credentials *creds)
+{
+ return __test_DsBind_assoc_group(tctx, __func__, priv,
+ priv->dc_credentials);
+}
+
+static bool test_DsBindAssocGroupWS(struct torture_context *tctx,
+ struct DsPrivate *priv,
+ struct cli_credentials *creds)
+{
+ struct test_join *wks_join = NULL;
+ struct cli_credentials *wks_credentials = NULL;
+ int rnd = rand() % 1000;
+ char *wks_name = talloc_asprintf(tctx, "WKS%s%d", TEST_MACHINE_NAME, rnd);
+ bool ret;
+
+ torture_comment(tctx, "%s: About to join workstation with name %s\n",
+ __func__, wks_name);
+ wks_join = torture_join_domain(tctx, wks_name, ACB_WSTRUST,
+ &wks_credentials);
+ torture_assert(tctx, wks_join, "Failed to join as WORKSTATION");
+ ret = __test_DsBind_assoc_group(tctx, __func__, priv,
+ wks_credentials);
+ torture_leave_domain(tctx, wks_join);
+ return ret;
+}
+
/**
* DRSUAPI test case implementation
*/
@@ -874,4 +1042,8 @@ void torture_rpc_drsuapi_tcase(struct torture_suite *suite)
torture_tcase_add_simple_test(tcase, "DsReplicaUpdateRefs", (run_func)test_DsReplicaUpdateRefs);
torture_tcase_add_simple_test(tcase, "DsGetNCChanges", (run_func)test_DsGetNCChanges);
+
+ torture_tcase_add_simple_test(tcase, "DsBindAssocGroupAdmin", (run_func)test_DsBindAssocGroupAdmin);
+ torture_tcase_add_simple_test(tcase, "DsBindAssocGroupDC", (run_func)test_DsBindAssocGroupDC);
+ torture_tcase_add_simple_test(tcase, "DsBindAssocGroupWS", (run_func)test_DsBindAssocGroupWS);
}