CVE-2021-3738 s4:torture/drsuapi: DsBindAssocGroup* tests
authorStefan Metzmacher <metze@samba.org>
Thu, 5 Aug 2021 09:26:16 +0000 (11:26 +0200)
committerJule Anger <janger@samba.org>
Mon, 8 Nov 2021 09:46:45 +0000 (10:46 +0100)
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>
selftest/knownfail.d/drsuapi.DsBindAssocGroupWS [new file with mode: 0644]
source4/torture/rpc/drsuapi.c

diff --git a/selftest/knownfail.d/drsuapi.DsBindAssocGroupWS b/selftest/knownfail.d/drsuapi.DsBindAssocGroupWS
new file mode 100644 (file)
index 0000000..9af5a90
--- /dev/null
@@ -0,0 +1 @@
+^samba4.rpc.drsuapi.*drsuapi.DsBindAssocGroupWS
index 3e8105af07b615f20e3b78cda42ce789f36bd9b5..1cd8f77db9c81531479c275956fcb90376046dbf 100644 (file)
@@ -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);
 }