summaryrefslogtreecommitdiff
path: root/source4/torture/rpc
diff options
context:
space:
mode:
Diffstat (limited to 'source4/torture/rpc')
-rw-r--r--source4/torture/rpc/alter_context.c86
-rw-r--r--source4/torture/rpc/async_bind.c90
-rw-r--r--source4/torture/rpc/atsvc.c139
-rw-r--r--source4/torture/rpc/autoidl.c277
-rw-r--r--source4/torture/rpc/bench.c122
-rw-r--r--source4/torture/rpc/bind.c82
-rw-r--r--source4/torture/rpc/countcalls.c129
-rw-r--r--source4/torture/rpc/dfs.c665
-rw-r--r--source4/torture/rpc/drsuapi.c783
-rw-r--r--source4/torture/rpc/drsuapi.h36
-rw-r--r--source4/torture/rpc/drsuapi_cracknames.c997
-rw-r--r--source4/torture/rpc/dssetup.c64
-rw-r--r--source4/torture/rpc/dssync.c893
-rw-r--r--source4/torture/rpc/echo.c452
-rw-r--r--source4/torture/rpc/epmapper.c277
-rw-r--r--source4/torture/rpc/eventlog.c260
-rw-r--r--source4/torture/rpc/frsapi.c275
-rw-r--r--source4/torture/rpc/handles.c580
-rw-r--r--source4/torture/rpc/initshutdown.c114
-rw-r--r--source4/torture/rpc/join.c82
-rw-r--r--source4/torture/rpc/lsa.c2558
-rw-r--r--source4/torture/rpc/lsa_lookup.c319
-rw-r--r--source4/torture/rpc/mgmt.c270
-rw-r--r--source4/torture/rpc/netlogon.c1612
-rw-r--r--source4/torture/rpc/netlogon.h6
-rw-r--r--source4/torture/rpc/oxidresolve.c237
-rw-r--r--source4/torture/rpc/remact.c89
-rw-r--r--source4/torture/rpc/remote_pac.c380
-rw-r--r--source4/torture/rpc/rpc.c446
-rw-r--r--source4/torture/rpc/rpc.h89
-rw-r--r--source4/torture/rpc/samba3rpc.c3400
-rw-r--r--source4/torture/rpc/samlogon.c1855
-rw-r--r--source4/torture/rpc/samr.c4648
-rw-r--r--source4/torture/rpc/samr_accessmask.c658
-rw-r--r--source4/torture/rpc/samsync.c1639
-rw-r--r--source4/torture/rpc/scanner.c151
-rw-r--r--source4/torture/rpc/schannel.c835
-rw-r--r--source4/torture/rpc/session_key.c233
-rw-r--r--source4/torture/rpc/spoolss.c1769
-rw-r--r--source4/torture/rpc/spoolss_notify.c300
-rw-r--r--source4/torture/rpc/spoolss_win.c578
-rw-r--r--source4/torture/rpc/srvsvc.c998
-rw-r--r--source4/torture/rpc/svcctl.c131
-rw-r--r--source4/torture/rpc/testjoin.c691
-rw-r--r--source4/torture/rpc/unixinfo.c144
-rw-r--r--source4/torture/rpc/winreg.c1908
-rw-r--r--source4/torture/rpc/wkssvc.c1452
47 files changed, 33799 insertions, 0 deletions
diff --git a/source4/torture/rpc/alter_context.c b/source4/torture/rpc/alter_context.c
new file mode 100644
index 0000000000..7843713074
--- /dev/null
+++ b/source4/torture/rpc/alter_context.c
@@ -0,0 +1,86 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for dcerpc alter_context operations
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_dssetup.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+
+bool torture_rpc_alter_context(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p, *p2, *p3;
+ struct policy_handle *handle;
+ struct ndr_interface_table tmptbl;
+ struct ndr_syntax_id syntax;
+ struct ndr_syntax_id transfer_syntax;
+ bool ret = true;
+
+ torture_comment(torture, "opening LSA connection\n");
+ status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(torture, status, "connecting");
+
+ if (!test_lsa_OpenPolicy2(p, torture, &handle)) {
+ ret = false;
+ }
+
+ torture_comment(torture, "Opening secondary DSSETUP context\n");
+ status = dcerpc_secondary_context(p, &p2, &ndr_table_dssetup);
+ torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed");
+
+ tmptbl = ndr_table_dssetup;
+ tmptbl.syntax_id.if_version += 100;
+ torture_comment(torture, "Opening bad secondary connection\n");
+ status = dcerpc_secondary_context(p, &p3, &tmptbl);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX,
+ "dcerpc_alter_context with wrong version should fail");
+
+ torture_comment(torture, "testing DSSETUP pipe operations\n");
+ ret &= test_DsRoleGetPrimaryDomainInformation(torture, p2);
+
+ if (handle) {
+ ret &= test_lsa_Close(p, torture, handle);
+ }
+
+ syntax = p->syntax;
+ transfer_syntax = p->transfer_syntax;
+
+ torture_comment(torture, "Testing change of primary context\n");
+ status = dcerpc_alter_context(p, torture, &p2->syntax, &p2->transfer_syntax);
+ torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed");
+
+ torture_comment(torture, "testing DSSETUP pipe operations - should fault\n");
+ ret &= test_DsRoleGetPrimaryDomainInformation_ext(torture, p, NT_STATUS_NET_WRITE_FAULT);
+
+ ret &= test_lsa_OpenPolicy2(p, torture, &handle);
+
+ if (handle) {
+ ret &= test_lsa_Close(p, torture, handle);
+ }
+
+ torture_comment(torture, "testing DSSETUP pipe operations\n");
+
+ ret &= test_DsRoleGetPrimaryDomainInformation(torture, p2);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/async_bind.c b/source4/torture/rpc/async_bind.c
new file mode 100644
index 0000000000..0ebbef1ce6
--- /dev/null
+++ b/source4/torture/rpc/async_bind.c
@@ -0,0 +1,90 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc torture tests
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Rafal Szczesniak 2006
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "lib/events/events.h"
+#include "libcli/composite/composite.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+
+/*
+ This test initiates multiple rpc bind requests and verifies
+ whether all of them are served.
+*/
+
+
+bool torture_async_bind(struct torture_context *torture)
+{
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ int i;
+ const char *binding_string;
+ struct cli_credentials *creds;
+ extern int torture_numasync;
+
+ struct composite_context **bind_req;
+ struct dcerpc_pipe **pipe;
+ const struct ndr_interface_table **table;
+
+ if (!torture_setting_bool(torture, "async", false)) {
+ printf("async bind test disabled - enable async tests to use\n");
+ return true;
+ }
+
+ binding_string = torture_setting_string(torture, "binding", NULL);
+
+ /* talloc context */
+ mem_ctx = talloc_init("torture_async_bind");
+ if (mem_ctx == NULL) return false;
+
+ bind_req = talloc_array(torture, struct composite_context*, torture_numasync);
+ if (bind_req == NULL) return false;
+ pipe = talloc_array(torture, struct dcerpc_pipe*, torture_numasync);
+ if (pipe == NULL) return false;
+ table = talloc_array(torture, const struct ndr_interface_table*, torture_numasync);
+ if (table == NULL) return false;
+
+ /* credentials */
+ creds = cmdline_credentials;
+
+ /* send bind requests */
+ for (i = 0; i < torture_numasync; i++) {
+ table[i] = &ndr_table_lsarpc;
+ bind_req[i] = dcerpc_pipe_connect_send(mem_ctx, binding_string,
+ table[i], creds, torture->ev, torture->lp_ctx);
+ }
+
+ /* recv bind requests */
+ for (i = 0; i < torture_numasync; i++) {
+ status = dcerpc_pipe_connect_recv(bind_req[i], mem_ctx, &pipe[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("async rpc connection failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return true;
+}
diff --git a/source4/torture/rpc/atsvc.c b/source4/torture/rpc/atsvc.c
new file mode 100644
index 0000000000..23d76ae502
--- /dev/null
+++ b/source4/torture/rpc/atsvc.c
@@ -0,0 +1,139 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for atsvc rpc operations
+
+ Copyright (C) Tim Potter 2003
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_atsvc_c.h"
+#include "torture/rpc/rpc.h"
+
+static bool test_JobGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t job_id)
+{
+ NTSTATUS status;
+ struct atsvc_JobGetInfo r;
+ struct atsvc_JobInfo *info = talloc(tctx, struct atsvc_JobInfo);
+ if (!info) {
+ return false;
+ }
+
+ r.in.servername = dcerpc_server_name(p);
+ r.in.job_id = job_id;
+ r.out.job_info = &info;
+
+ status = dcerpc_atsvc_JobGetInfo(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "JobGetInfo failed");
+
+ return true;
+}
+
+static bool test_JobDel(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t min_job_id,
+ uint32_t max_job_id)
+{
+ NTSTATUS status;
+ struct atsvc_JobDel r;
+
+ r.in.servername = dcerpc_server_name(p);
+ r.in.min_job_id = min_job_id;
+ r.in.max_job_id = max_job_id;
+
+ status = dcerpc_atsvc_JobDel(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "JobDel failed");
+
+ return true;
+}
+
+static bool test_JobEnum(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct atsvc_JobEnum r;
+ struct atsvc_enum_ctr ctr;
+ uint32_t resume_handle = 0, i, total_entries = 0;
+
+ bool ret = true;
+
+ r.in.servername = dcerpc_server_name(p);
+ ctr.entries_read = 0;
+ ctr.first_entry = NULL;
+ r.in.ctr = r.out.ctr = &ctr;
+ r.in.preferred_max_len = 0xffffffff;
+ r.in.resume_handle = r.out.resume_handle = &resume_handle;
+ r.out.total_entries = &total_entries;
+
+ status = dcerpc_atsvc_JobEnum(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "JobEnum failed");
+
+ for (i = 0; i < r.out.ctr->entries_read; i++) {
+ if (!test_JobGetInfo(p, tctx, r.out.ctr->first_entry[i].job_id)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_JobAdd(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct atsvc_JobAdd r;
+ struct atsvc_JobInfo info;
+
+ r.in.servername = dcerpc_server_name(p);
+ info.job_time = 0x050ae4c0; /* 11:30pm */
+ info.days_of_month = 0; /* n/a */
+ info.days_of_week = 0x02; /* Tuesday */
+ info.flags = 0x11; /* periodic, non-interactive */
+ info.command = "foo.exe";
+ r.in.job_info = &info;
+
+ status = dcerpc_atsvc_JobAdd(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "JobAdd failed");
+
+ /* Run EnumJobs again in case there were no jobs to begin with */
+
+ if (!test_JobEnum(tctx, p)) {
+ return false;
+ }
+
+ if (!test_JobGetInfo(p, tctx, *r.out.job_id)) {
+ return false;
+ }
+
+ if (!test_JobDel(p, tctx, *r.out.job_id, *r.out.job_id)) {
+ return false;
+ }
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_atsvc(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "ATSVC");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "atsvc", &ndr_table_atsvc);
+
+ torture_rpc_tcase_add_test(tcase, "JobEnum", test_JobEnum);
+ torture_rpc_tcase_add_test(tcase, "JobAdd", test_JobAdd);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/autoidl.c b/source4/torture/rpc/autoidl.c
new file mode 100644
index 0000000000..5ae0201855
--- /dev/null
+++ b/source4/torture/rpc/autoidl.c
@@ -0,0 +1,277 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ auto-idl scanner
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "librpc/gen_ndr/ndr_misc.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+
+
+#if 1
+/*
+ get a DRSUAPI policy handle
+*/
+static bool get_policy_handle(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct drsuapi_DsBind r;
+
+ ZERO_STRUCT(r);
+ r.out.bind_handle = handle;
+
+ status = dcerpc_drsuapi_DsBind(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("drsuapi_DsBind failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+#else
+/*
+ get a SAMR handle
+*/
+static bool get_policy_handle(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_Connect r;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = handle;
+
+ status = dcerpc_samr_Connect(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("samr_Connect failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+static void fill_blob_handle(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ DATA_BLOB b2;
+
+ if (blob->length < 20) {
+ return;
+ }
+
+ ndr_push_struct_blob(&b2, mem_ctx, NULL, handle, (ndr_push_flags_fn_t)ndr_push_policy_handle);
+
+ memcpy(blob->data, b2.data, 20);
+}
+
+static void reopen(struct torture_context *tctx,
+ struct dcerpc_pipe **p,
+ const struct ndr_interface_table *iface)
+{
+ NTSTATUS status;
+
+ talloc_free(*p);
+
+ status = torture_rpc_connection(tctx, p, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to reopen '%s' - %s\n", iface->name, nt_errstr(status));
+ exit(1);
+ }
+}
+
+static void print_depth(int depth)
+{
+ int i;
+ for (i=0;i<depth;i++) {
+ printf(" ");
+ }
+}
+
+static void test_ptr_scan(struct torture_context *tctx, const struct ndr_interface_table *iface,
+ int opnum, DATA_BLOB *base_in, int min_ofs, int max_ofs, int depth);
+
+static void try_expand(struct torture_context *tctx, const struct ndr_interface_table *iface,
+ int opnum, DATA_BLOB *base_in, int insert_ofs, int depth)
+{
+ DATA_BLOB stub_in, stub_out;
+ int n;
+ NTSTATUS status;
+ struct dcerpc_pipe *p = NULL;
+
+ reopen(tctx, &p, iface);
+
+ /* work out how much to expand to get a non fault */
+ for (n=0;n<2000;n++) {
+ stub_in = data_blob(NULL, base_in->length + n);
+ data_blob_clear(&stub_in);
+ memcpy(stub_in.data, base_in->data, insert_ofs);
+ memcpy(stub_in.data+insert_ofs+n, base_in->data+insert_ofs, base_in->length-insert_ofs);
+
+ status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ print_depth(depth);
+ printf("expand by %d gives %s\n", n, nt_errstr(status));
+ if (n >= 4) {
+ test_ptr_scan(tctx, iface, opnum, &stub_in,
+ insert_ofs, insert_ofs+n, depth+1);
+ }
+ return;
+ } else {
+#if 0
+ print_depth(depth);
+ printf("expand by %d gives fault %s\n", n, dcerpc_errstr(tctx, p->last_fault_code));
+#endif
+ }
+ if (p->last_fault_code == 5) {
+ reopen(tctx, &p, iface);
+ }
+ }
+
+ talloc_free(p);
+}
+
+
+static void test_ptr_scan(struct torture_context *tctx, const struct ndr_interface_table *iface,
+ int opnum, DATA_BLOB *base_in, int min_ofs, int max_ofs, int depth)
+{
+ DATA_BLOB stub_in, stub_out;
+ int ofs;
+ NTSTATUS status;
+ struct dcerpc_pipe *p = NULL;
+
+ reopen(tctx, &p, iface);
+
+ stub_in = data_blob(NULL, base_in->length);
+ memcpy(stub_in.data, base_in->data, base_in->length);
+
+ /* work out which elements are pointers */
+ for (ofs=min_ofs;ofs<=max_ofs-4;ofs+=4) {
+ SIVAL(stub_in.data, ofs, 1);
+ status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ print_depth(depth);
+ printf("possible ptr at ofs %d - fault %s\n",
+ ofs-min_ofs, dcerpc_errstr(tctx, p->last_fault_code));
+ if (p->last_fault_code == 5) {
+ reopen(tctx, &p, iface);
+ }
+ if (depth == 0) {
+ try_expand(tctx, iface, opnum, &stub_in, ofs+4, depth+1);
+ } else {
+ try_expand(tctx, iface, opnum, &stub_in, max_ofs, depth+1);
+ }
+ SIVAL(stub_in.data, ofs, 0);
+ continue;
+ }
+ SIVAL(stub_in.data, ofs, 0);
+ }
+
+ talloc_free(p);
+}
+
+
+static void test_scan_call(struct torture_context *tctx, const struct ndr_interface_table *iface, int opnum)
+{
+ DATA_BLOB stub_in, stub_out;
+ int i;
+ NTSTATUS status;
+ struct dcerpc_pipe *p = NULL;
+ struct policy_handle handle;
+
+ reopen(tctx, &p, iface);
+
+ get_policy_handle(p, tctx, &handle);
+
+ /* work out the minimum amount of input data */
+ for (i=0;i<2000;i++) {
+ stub_in = data_blob(NULL, i);
+ data_blob_clear(&stub_in);
+
+
+ status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("opnum %d min_input %d - output %d\n",
+ opnum, (int)stub_in.length, (int)stub_out.length);
+ dump_data(0, stub_out.data, stub_out.length);
+ talloc_free(p);
+ test_ptr_scan(tctx, iface, opnum, &stub_in, 0, stub_in.length, 0);
+ return;
+ }
+
+ fill_blob_handle(&stub_in, tctx, &handle);
+
+ status = dcerpc_request(p, NULL, opnum, false, tctx, &stub_in, &stub_out);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("opnum %d min_input %d - output %d (with handle)\n",
+ opnum, (int)stub_in.length, (int)stub_out.length);
+ dump_data(0, stub_out.data, stub_out.length);
+ talloc_free(p);
+ test_ptr_scan(tctx, iface, opnum, &stub_in, 0, stub_in.length, 0);
+ return;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ printf("opnum %d size %d fault %s\n", opnum, i, dcerpc_errstr(tctx, p->last_fault_code));
+ if (p->last_fault_code == 5) {
+ reopen(tctx, &p, iface);
+ }
+ continue;
+ }
+
+ printf("opnum %d size %d error %s\n", opnum, i, nt_errstr(status));
+ }
+
+ printf("opnum %d minimum not found!?\n", opnum);
+ talloc_free(p);
+}
+
+
+static void test_auto_scan(struct torture_context *tctx, const struct ndr_interface_table *iface)
+{
+ test_scan_call(tctx, iface, 2);
+}
+
+bool torture_rpc_autoidl(struct torture_context *torture)
+{
+ const struct ndr_interface_table *iface;
+
+ iface = ndr_table_by_name("drsuapi");
+ if (!iface) {
+ printf("Unknown interface!\n");
+ return false;
+ }
+
+ printf("\nProbing pipe '%s'\n", iface->name);
+
+ test_auto_scan(torture, iface);
+
+ return true;
+}
diff --git a/source4/torture/rpc/bench.c b/source4/torture/rpc/bench.c
new file mode 100644
index 0000000000..2ae92332f8
--- /dev/null
+++ b/source4/torture/rpc/bench.c
@@ -0,0 +1,122 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ simple RPC benchmark
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+
+/**************************/
+/* srvsvc_NetShare */
+/**************************/
+static bool test_NetShareEnumAll(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareEnumAll r;
+ struct srvsvc_NetShareCtr0 c0;
+ uint32_t levels[] = {0, 1, 2, 501, 502};
+ int i;
+ bool ret = true;
+ uint32_t resume_handle;
+
+ ZERO_STRUCT(c0);
+
+ r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.ctr.ctr0 = &c0;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = &resume_handle;
+ r.out.resume_handle = &resume_handle;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ resume_handle = 0;
+ r.in.level = levels[i];
+ status = dcerpc_srvsvc_NetShareEnumAll(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("NetShareEnumAll level %u failed - %s\n", r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("NetShareEnumAll level %u failed - %s\n", r.in.level, win_errstr(r.out.result));
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ benchmark srvsvc netshareenumall queries
+*/
+static bool bench_NetShareEnumAll(struct torture_context *tctx, struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
+{
+ struct timeval tv = timeval_current();
+ bool ret = true;
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+ int count=0;
+
+ printf("Running for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ if (!test_NetShareEnumAll(p, tmp_ctx)) break;
+ talloc_free(tmp_ctx);
+ count++;
+ if (count % 50 == 0) {
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("%.1f queries per second \r",
+ count / timeval_elapsed(&tv));
+ }
+ }
+ }
+
+ printf("%.1f queries per second \n", count / timeval_elapsed(&tv));
+
+ return ret;
+}
+
+
+bool torture_bench_rpc(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+
+ mem_ctx = talloc_init("torture_rpc_srvsvc");
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_srvsvc);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (!bench_NetShareEnumAll(torture, p, mem_ctx)) {
+ ret = false;
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/bind.c b/source4/torture/rpc/bind.c
new file mode 100644
index 0000000000..55c9d3686d
--- /dev/null
+++ b/source4/torture/rpc/bind.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc torture tests
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org 2004
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+#include "libcli/libcli.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+
+/*
+ This test is 'bogus' in that it doesn't actually perform to the
+ spec. We need to deal with other things inside the DCERPC layer,
+ before we could have multiple binds.
+
+ We should never pass this test, until such details are fixed in our
+ client, and it looks like multible binds are never used anyway.
+
+*/
+
+bool torture_multi_bind(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding *binding;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret;
+
+ mem_ctx = talloc_init("torture_multi_bind");
+
+ status = torture_rpc_binding(torture, &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = dcerpc_pipe_auth(mem_ctx, &p, binding, &ndr_table_lsarpc, cmdline_credentials,
+ torture->lp_ctx);
+
+ if (NT_STATUS_IS_OK(status)) {
+ printf("(incorrectly) allowed re-bind to uuid %s - %s\n",
+ GUID_string(mem_ctx, &ndr_table_lsarpc.syntax_id.uuid), nt_errstr(status));
+ ret = false;
+ } else {
+ printf("\n");
+ ret = true;
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/countcalls.c b/source4/torture/rpc/countcalls.c
new file mode 100644
index 0000000000..205ee1ec86
--- /dev/null
+++ b/source4/torture/rpc/countcalls.c
@@ -0,0 +1,129 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ count number of calls on an interface
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/ndr/libndr.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc_proto.h"
+
+
+
+bool count_calls(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ const struct ndr_interface_table *iface,
+ bool all)
+{
+ struct dcerpc_pipe *p;
+ DATA_BLOB stub_in, stub_out;
+ int i;
+ NTSTATUS status = torture_rpc_connection(tctx, &p, iface);
+ if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)
+ || NT_STATUS_EQUAL(NT_STATUS_NET_WRITE_FAULT, status)
+ || NT_STATUS_EQUAL(NT_STATUS_PORT_UNREACHABLE, status)
+ || NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
+ if (all) {
+ /* Not fatal if looking for all pipes */
+ return true;
+ } else {
+ printf("Failed to open '%s' to count calls - %s\n", iface->name, nt_errstr(status));
+ return false;
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to open '%s' to count calls - %s\n", iface->name, nt_errstr(status));
+ return false;
+ }
+
+ stub_in = data_blob_talloc(p, mem_ctx, 0);
+
+ printf("\nScanning pipe '%s'\n", iface->name);
+
+ for (i=0;i<500;i++) {
+ status = dcerpc_request(p, NULL, i, false, p, &stub_in, &stub_out);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT) &&
+ p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+ i--;
+ break;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT) &&
+ p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+ i--;
+ break;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) {
+ i--;
+ break;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
+ i--;
+ break;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ i--;
+ break;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_LOGON_FAILURE)) {
+ i--;
+ break;
+ }
+ }
+
+ if (i==500) {
+ talloc_free(p);
+ printf("no limit on calls: %s!?\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("Found %d calls\n", i);
+
+ talloc_free(p);
+
+ return true;
+
+}
+
+bool torture_rpc_countcalls(struct torture_context *torture)
+{
+ const struct ndr_interface_table *iface;
+ const char *iface_name;
+ bool ret = true;
+ const struct ndr_interface_list *l;
+ iface_name = lp_parm_string(torture->lp_ctx, NULL, "countcalls", "interface");
+ if (iface_name != NULL) {
+ iface = ndr_table_by_name(iface_name);
+ if (!iface) {
+ printf("Unknown interface '%s'\n", iface_name);
+ return false;
+ }
+ return count_calls(torture, torture, iface, false);
+ }
+
+ for (l=ndr_table_list();l;l=l->next) {
+ TALLOC_CTX *loop_ctx;
+ loop_ctx = talloc_named(torture, 0, "torture_rpc_councalls loop context");
+ ret &= count_calls(torture, loop_ctx, l->table, true);
+ talloc_free(loop_ctx);
+ }
+ return ret;
+}
diff --git a/source4/torture/rpc/dfs.c b/source4/torture/rpc/dfs.c
new file mode 100644
index 0000000000..1c81766ebe
--- /dev/null
+++ b/source4/torture/rpc/dfs.c
@@ -0,0 +1,665 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for rpc dfs operations
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_dfs_c.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "libnet/libnet.h"
+#include "libcli/raw/libcliraw.h"
+#include "torture/util.h"
+#include "libcli/libcli.h"
+#include "lib/cmdline/popt_common.h"
+
+#define SMBTORTURE_DFS_SHARENAME "smbtorture_dfs_share"
+#define SMBTORTURE_DFS_DIRNAME "\\smbtorture_dfs_dir"
+#define SMBTORTURE_DFS_PATHNAME "C:"SMBTORTURE_DFS_DIRNAME
+
+#define IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(x,y)\
+ if (x == DFS_MANAGER_VERSION_W2K3) {\
+ if (!W_ERROR_EQUAL(y,WERR_NOT_SUPPORTED)) {\
+ printf("expected WERR_NOT_SUPPORTED\n");\
+ return false;\
+ }\
+ return true;\
+ }\
+
+static bool test_NetShareAdd(TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ const char *host,
+ const char *sharename,
+ const char *dir)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareInfo2 i;
+ struct libnet_context* libnetctx;
+ struct libnet_AddShare r;
+
+ printf("Creating share %s\n", sharename);
+
+ if (!(libnetctx = libnet_context_init(tctx->ev, tctx->lp_ctx))) {
+ return false;
+ }
+
+ libnetctx->cred = cmdline_credentials;
+
+ i.name = sharename;
+ i.type = STYPE_DISKTREE;
+ i.path = dir;
+ i.max_users = (uint32_t) -1;
+ i.comment = "created by smbtorture";
+ i.password = NULL;
+ i.permissions = 0x0;
+ i.current_users = 0x0;
+
+ r.level = 2;
+ r.in.server_name = host;
+ r.in.share = i;
+
+ status = libnet_AddShare(libnetctx, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Failed to add new share: %s (%s)\n",
+ nt_errstr(status), r.out.error_string);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_NetShareDel(TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ const char *host,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct libnet_context* libnetctx;
+ struct libnet_DelShare r;
+
+ printf("Deleting share %s\n", sharename);
+
+ if (!(libnetctx = libnet_context_init(tctx->ev, tctx->lp_ctx))) {
+ return false;
+ }
+
+ libnetctx->cred = cmdline_credentials;
+
+ r.in.share_name = sharename;
+ r.in.server_name = host;
+
+ status = libnet_DelShare(libnetctx, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Failed to delete share: %s (%s)\n",
+ nt_errstr(status), r.out.error_string);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_CreateDir(TALLOC_CTX *mem_ctx,
+ struct smbcli_state **cli,
+ struct torture_context *tctx,
+ const char *host,
+ const char *share,
+ const char *dir)
+{
+ printf("Creating directory %s\n", dir);
+
+ if (!torture_open_connection_share(mem_ctx, cli, tctx, host, share, tctx->ev)) {
+ return false;
+ }
+
+ if (!torture_setup_dir(*cli, dir)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DeleteDir(struct smbcli_state *cli,
+ const char *dir)
+{
+ printf("Deleting directory %s\n", dir);
+
+ if (smbcli_deltree(cli->tree, dir) == -1) {
+ printf("Unable to delete dir %s - %s\n", dir,
+ smbcli_errstr(cli->tree));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_GetManagerVersion(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ enum dfs_ManagerVersion *version)
+{
+ NTSTATUS status;
+ struct dfs_GetManagerVersion r;
+
+ r.out.version = version;
+
+ status = dcerpc_dfs_GetManagerVersion(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetManagerVersion failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_ManagerInitialize(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ NTSTATUS status;
+ enum dfs_ManagerVersion version;
+ struct dfs_ManagerInitialize r;
+
+ printf("Testing ManagerInitialize\n");
+
+ if (!test_GetManagerVersion(p, mem_ctx, &version)) {
+ return false;
+ }
+
+ r.in.servername = host;
+ r.in.flags = 0;
+
+ status = dcerpc_dfs_ManagerInitialize(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ManagerInitialize failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_ManagerInitialize failed - %s\n",
+ win_errstr(r.out.result));
+ IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(version, r.out.result);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_GetInfoLevel(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ uint16_t level,
+ const char *root)
+{
+ NTSTATUS status;
+ struct dfs_GetInfo r;
+
+ printf("Testing GetInfo level %u on '%s'\n", level, root);
+
+ r.in.dfs_entry_path = talloc_strdup(mem_ctx, root);
+ r.in.servername = NULL;
+ r.in.sharename = NULL;
+ r.in.level = level;
+
+ status = dcerpc_dfs_GetInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetInfo failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result) &&
+ !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) {
+ printf("dfs_GetInfo failed - %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_GetInfo(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *root)
+{
+ bool ret = true;
+ /* 103, 104, 105, 106 is only available on Set */
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 100, 101, 102, 103, 104, 105, 106};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (!test_GetInfoLevel(p, mem_ctx, levels[i], root)) {
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+static bool test_EnumLevelEx(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ uint16_t level,
+ const char *dfs_name)
+{
+ NTSTATUS status;
+ struct dfs_EnumEx rex;
+ uint32_t total=0;
+ struct dfs_EnumStruct e;
+ struct dfs_Info1 s;
+ struct dfs_EnumArray1 e1;
+ bool ret = true;
+
+ rex.in.level = level;
+ rex.in.bufsize = (uint32_t)-1;
+ rex.in.total = &total;
+ rex.in.info = &e;
+ rex.in.dfs_name = dfs_name;
+
+ e.level = rex.in.level;
+ e.e.info1 = &e1;
+ e.e.info1->count = 0;
+ e.e.info1->s = &s;
+ s.path = NULL;
+
+ printf("Testing EnumEx level %u on '%s'\n", level, dfs_name);
+
+ status = dcerpc_dfs_EnumEx(p, mem_ctx, &rex);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumEx failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (level == 1 && rex.out.total) {
+ int i;
+ for (i=0;i<*rex.out.total;i++) {
+ const char *root = talloc_strdup(mem_ctx,
+ rex.out.info->e.info1->s[i].path);
+ if (!test_GetInfo(p, mem_ctx, root)) {
+ ret = false;
+ }
+ }
+ }
+
+ if (level == 300 && rex.out.total) {
+ int i,k;
+ for (i=0;i<*rex.out.total;i++) {
+ uint16_t levels[] = {1, 2, 3, 4, 200}; /* 300 */
+ const char *root = talloc_strdup(mem_ctx,
+ rex.out.info->e.info300->s[i].dom_root);
+ for (k=0;k<ARRAY_SIZE(levels);k++) {
+ if (!test_EnumLevelEx(p, mem_ctx,
+ levels[k], root))
+ {
+ ret = false;
+ }
+ }
+ if (!test_GetInfo(p, mem_ctx, root)) {
+ ret = false;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_EnumLevel(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ uint16_t level)
+{
+ NTSTATUS status;
+ struct dfs_Enum r;
+ uint32_t total=0;
+ struct dfs_EnumStruct e;
+ struct dfs_Info1 s;
+ struct dfs_EnumArray1 e1;
+ bool ret = true;
+
+ r.in.level = level;
+ r.in.bufsize = (uint32_t)-1;
+ r.in.total = &total;
+ r.in.info = &e;
+
+ e.level = r.in.level;
+ e.e.info1 = &e1;
+ e.e.info1->count = 0;
+ e.e.info1->s = &s;
+ s.path = NULL;
+
+ printf("Testing Enum level %u\n", level);
+
+ status = dcerpc_dfs_Enum(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Enum failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result) &&
+ !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) {
+ printf("dfs_Enum failed - %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ if (level == 1 && r.out.total) {
+ int i;
+ for (i=0;i<*r.out.total;i++) {
+ const char *root = r.out.info->e.info1->s[i].path;
+ if (!test_GetInfo(p, mem_ctx, root)) {
+ ret = false;
+ }
+ }
+
+ }
+
+ return ret;
+}
+
+
+static bool test_Enum(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
+{
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 200, 300};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (!test_EnumLevel(p, mem_ctx, levels[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_EnumEx(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 200, 300};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (!test_EnumLevelEx(p, mem_ctx, levels[i], host)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_RemoveStdRoot(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char *sharename)
+{
+ struct dfs_RemoveStdRoot r;
+ NTSTATUS status;
+
+ printf("Testing RemoveStdRoot\n");
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+ r.in.flags = 0;
+
+ status = dcerpc_dfs_RemoveStdRoot(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("RemoveStdRoot failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_RemoveStdRoot failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_AddStdRoot(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct dfs_AddStdRoot r;
+
+ printf("Testing AddStdRoot\n");
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+ r.in.comment = "standard dfs standalone DFS root created by smbtorture (dfs_AddStdRoot)";
+ r.in.flags = 0;
+
+ status = dcerpc_dfs_AddStdRoot(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddStdRoot failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_AddStdRoot failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_AddStdRootForced(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct dfs_AddStdRootForced r;
+ enum dfs_ManagerVersion version;
+
+ printf("Testing AddStdRootForced\n");
+
+ if (!test_GetManagerVersion(p, mem_ctx, &version)) {
+ return false;
+ }
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+ r.in.comment = "standard dfs forced standalone DFS root created by smbtorture (dfs_AddStdRootForced)";
+ r.in.store = SMBTORTURE_DFS_PATHNAME;
+
+ status = dcerpc_dfs_AddStdRootForced(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddStdRootForced failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_AddStdRootForced failed - %s\n",
+ win_errstr(r.out.result));
+ IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(version, r.out.result);
+ return false;
+ }
+
+ return test_RemoveStdRoot(p, mem_ctx, host, sharename);
+}
+
+static void test_cleanup_stdroot(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ const char *host,
+ const char *sharename,
+ const char *dir)
+{
+ struct smbcli_state *cli;
+
+ printf("Cleaning up StdRoot\n");
+
+ test_RemoveStdRoot(p, mem_ctx, host, sharename);
+ test_NetShareDel(mem_ctx, tctx, host, sharename);
+ torture_open_connection_share(mem_ctx, &cli, tctx, host, "C$", tctx->ev);
+ test_DeleteDir(cli, dir);
+ torture_close_connection(cli);
+}
+
+static bool test_StdRoot(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ const char *host)
+{
+ const char *sharename = SMBTORTURE_DFS_SHARENAME;
+ const char *dir = SMBTORTURE_DFS_DIRNAME;
+ const char *path = SMBTORTURE_DFS_PATHNAME;
+ struct smbcli_state *cli;
+ bool ret = true;
+
+ printf("Testing StdRoot\n");
+
+ test_cleanup_stdroot(p, mem_ctx, tctx, host, sharename, dir);
+
+ ret &= test_CreateDir(mem_ctx, &cli, tctx, host, "C$", dir);
+ ret &= test_NetShareAdd(mem_ctx, tctx, host, sharename, path);
+ ret &= test_AddStdRoot(p, mem_ctx, host, sharename);
+ ret &= test_RemoveStdRoot(p, mem_ctx, host, sharename);
+ ret &= test_AddStdRootForced(p, mem_ctx, host, sharename);
+ ret &= test_NetShareDel(mem_ctx, tctx, host, sharename);
+ ret &= test_DeleteDir(cli, dir);
+
+ torture_close_connection(cli);
+
+ return ret;
+}
+
+static bool test_GetDcAddress(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ NTSTATUS status;
+ struct dfs_GetDcAddress r;
+ uint8_t is_root = 0;
+ uint32_t ttl = 0;
+ const char *ptr;
+
+ printf("Testing GetDcAddress\n");
+
+ ptr = host;
+
+ r.in.servername = host;
+ r.in.server_fullname = r.out.server_fullname = &ptr;
+ r.in.is_root = r.out.is_root = &is_root;
+ r.in.ttl = r.out.ttl = &ttl;
+
+ status = dcerpc_dfs_GetDcAddress(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetDcAddress failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_GetDcAddress failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_SetDcAddress(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ NTSTATUS status;
+ struct dfs_SetDcAddress r;
+
+ printf("Testing SetDcAddress\n");
+
+ r.in.servername = host;
+ r.in.server_fullname = host;
+ r.in.flags = 0;
+ r.in.ttl = 1000;
+
+ status = dcerpc_dfs_SetDcAddress(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetDcAddress failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_SetDcAddress failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DcAddress(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ if (!test_GetDcAddress(p, mem_ctx, host)) {
+ return false;
+ }
+
+ if (!test_SetDcAddress(p, mem_ctx, host)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_FlushFtTable(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct dfs_FlushFtTable r;
+ enum dfs_ManagerVersion version;
+
+ printf("Testing FlushFtTable\n");
+
+ if (!test_GetManagerVersion(p, mem_ctx, &version)) {
+ return false;
+ }
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+
+ status = dcerpc_dfs_FlushFtTable(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("FlushFtTable failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("dfs_FlushFtTable failed - %s\n",
+ win_errstr(r.out.result));
+ IS_DFS_VERSION_UNSUPPORTED_CALL_W2K3(version, r.out.result);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_FtRoot(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *host)
+{
+ const char *sharename = SMBTORTURE_DFS_SHARENAME;
+
+ return test_FlushFtTable(p, mem_ctx, host, sharename);
+}
+
+bool torture_rpc_dfs(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ enum dfs_ManagerVersion version;
+ const char *host = torture_setting_string(torture, "host", NULL);
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_netdfs);
+ torture_assert_ntstatus_ok(torture, status, "Unable to connect");
+
+ ret &= test_GetManagerVersion(p, torture, &version);
+ ret &= test_ManagerInitialize(p, torture, host);
+ ret &= test_Enum(p, torture);
+ ret &= test_EnumEx(p, torture, host);
+ ret &= test_StdRoot(p, torture, torture, host);
+ ret &= test_FtRoot(p, torture, host);
+ ret &= test_DcAddress(p, torture, host);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/drsuapi.c b/source4/torture/rpc/drsuapi.c
new file mode 100644
index 0000000000..e5cb3d7ddf
--- /dev/null
+++ b/source4/torture/rpc/drsuapi.c
@@ -0,0 +1,783 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DRSUapi tests
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+
+#define TEST_MACHINE_NAME "torturetest"
+
+bool test_DsBind(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsBind r;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &priv->bind_guid);
+
+ r.in.bind_guid = &priv->bind_guid;
+ r.in.bind_info = NULL;
+ r.out.bind_handle = &priv->bind_handle;
+
+ torture_comment(tctx, "testing DsBind\n");
+
+ status = dcerpc_drsuapi_DsBind(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ torture_fail(tctx, "dcerpc_drsuapi_DsBind failed");
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_fail(tctx, "DsBind failed");
+ }
+
+ return true;
+}
+
+static bool test_DsGetDomainControllerInfo(struct dcerpc_pipe *p, struct torture_context *torture,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsGetDomainControllerInfo r;
+ bool found = false;
+ int i, j, k;
+
+ struct {
+ const char *name;
+ WERROR expected;
+ } names[] = {
+ {
+ .name = torture_join_dom_netbios_name(priv->join),
+ .expected = WERR_OK
+ },
+ {
+ .name = torture_join_dom_dns_name(priv->join),
+ .expected = WERR_OK
+ },
+ {
+ .name = "__UNKNOWN_DOMAIN__",
+ .expected = WERR_DS_OBJ_NOT_FOUND
+ },
+ {
+ .name = "unknown.domain.samba.example.com",
+ .expected = WERR_DS_OBJ_NOT_FOUND
+ },
+ };
+ int levels[] = {1, 2};
+ int level;
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+ for (j=0; j < ARRAY_SIZE(names); j++) {
+ level = levels[i];
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+
+ r.in.req.req1.domain_name = names[j].name;
+ r.in.req.req1.level = level;
+
+ torture_comment(torture,
+ "testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
+ r.in.req.req1.level, r.in.req.req1.domain_name);
+
+ status = dcerpc_drsuapi_DsGetDomainControllerInfo(p, torture, &r);
+ torture_assert_ntstatus_ok(torture, status,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
+ torture_assert_werr_equal(torture,
+ r.out.result, names[j].expected,
+ "DsGetDomainControllerInfo level with dns domain failed");
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ /* If this was an error, we can't read the result structure */
+ continue;
+ }
+
+ torture_assert_int_equal(torture,
+ r.in.req.req1.level, r.out.level_out,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo level");
+
+ switch (level) {
+ case 1:
+ for (k=0; k < r.out.ctr.ctr1.count; k++) {
+ if (strcasecmp_m(r.out.ctr.ctr1.array[k].netbios_name,
+ torture_join_netbios_name(priv->join)) == 0) {
+ found = true;
+ break;
+ }
+ }
+ break;
+ case 2:
+ for (k=0; k < r.out.ctr.ctr2.count; k++) {
+ if (strcasecmp_m(r.out.ctr.ctr2.array[k].netbios_name,
+ torture_join_netbios_name(priv->join)) == 0) {
+ found = true;
+ priv->dcinfo = r.out.ctr.ctr2.array[k];
+ break;
+ }
+ }
+ break;
+ }
+ torture_assert(torture, found,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo: Failed to find the domain controller we just created during the join");
+ }
+ }
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+
+ r.in.req.req1.domain_name = "__UNKNOWN_DOMAIN__"; /* This is clearly ignored for this level */
+ r.in.req.req1.level = -1;
+
+ printf("testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
+ r.in.req.req1.level, r.in.req.req1.domain_name);
+
+ status = dcerpc_drsuapi_DsGetDomainControllerInfo(p, torture, &r);
+
+ torture_assert_ntstatus_ok(torture, status,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
+ torture_assert_werr_ok(torture, r.out.result,
+ "DsGetDomainControllerInfo with dns domain failed");
+
+ {
+ const char *dc_account = talloc_asprintf(torture, "%s\\%s$",
+ torture_join_dom_netbios_name(priv->join),
+ priv->dcinfo.netbios_name);
+ for (k=0; k < r.out.ctr.ctr01.count; k++) {
+ if (strcasecmp_m(r.out.ctr.ctr01.array[k].client_account,
+ dc_account)) {
+ found = true;
+ break;
+ }
+ }
+ torture_assert(torture, found,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo level: Failed to find the domain controller in last logon records");
+ }
+
+
+ return true;
+}
+
+static bool test_DsWriteAccountSpn(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsWriteAccountSpn r;
+ struct drsuapi_DsNameString names[2];
+ bool ret = true;
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+
+ printf("testing DsWriteAccountSpn\n");
+
+ r.in.req.req1.operation = DRSUAPI_DS_SPN_OPERATION_ADD;
+ r.in.req.req1.unknown1 = 0;
+ r.in.req.req1.object_dn = priv->dcinfo.computer_dn;
+ r.in.req.req1.count = 2;
+ r.in.req.req1.spn_names = names;
+ names[0].str = talloc_asprintf(mem_ctx, "smbtortureSPN/%s",priv->dcinfo.netbios_name);
+ names[1].str = talloc_asprintf(mem_ctx, "smbtortureSPN/%s",priv->dcinfo.dns_name);
+
+ status = dcerpc_drsuapi_DsWriteAccountSpn(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsWriteAccountSpn failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsWriteAccountSpn failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ r.in.req.req1.operation = DRSUAPI_DS_SPN_OPERATION_DELETE;
+ r.in.req.req1.unknown1 = 0;
+
+ status = dcerpc_drsuapi_DsWriteAccountSpn(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsWriteAccountSpn failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsWriteAccountSpn failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_DsReplicaGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsReplicaGetInfo r;
+ bool ret = true;
+ int i;
+ struct {
+ int32_t level;
+ int32_t infotype;
+ const char *obj_dn;
+ } array[] = {
+ {
+ DRSUAPI_DS_REPLICA_GET_INFO,
+ DRSUAPI_DS_REPLICA_INFO_NEIGHBORS,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO,
+ DRSUAPI_DS_REPLICA_INFO_CURSORS,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO,
+ DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO,
+ DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO,
+ DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO,
+ DRSUAPI_DS_REPLICA_INFO_PENDING_OPS,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_CURSORS2,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_CURSORS3,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_NEIGHBORS02,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_CONNECTIONS04,
+ "__IGNORED__"
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_CURSORS05,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_06,
+ NULL
+ }
+ };
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping DsReplicaGetInfo test against Samba4\n");
+ return true;
+ }
+
+ r.in.bind_handle = &priv->bind_handle;
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ const char *object_dn;
+
+ printf("testing DsReplicaGetInfo level %d infotype %d\n",
+ array[i].level, array[i].infotype);
+
+ object_dn = (array[i].obj_dn ? array[i].obj_dn : priv->domain_obj_dn);
+
+ r.in.level = array[i].level;
+ switch(r.in.level) {
+ case DRSUAPI_DS_REPLICA_GET_INFO:
+ r.in.req.req1.info_type = array[i].infotype;
+ r.in.req.req1.object_dn = object_dn;
+ ZERO_STRUCT(r.in.req.req1.guid1);
+ break;
+ case DRSUAPI_DS_REPLICA_GET_INFO2:
+ r.in.req.req2.info_type = array[i].infotype;
+ r.in.req.req2.object_dn = object_dn;
+ ZERO_STRUCT(r.in.req.req1.guid1);
+ r.in.req.req2.unknown1 = 0;
+ r.in.req.req2.string1 = NULL;
+ r.in.req.req2.string2 = NULL;
+ r.in.req.req2.unknown2 = 0;
+ break;
+ }
+
+ status = dcerpc_drsuapi_DsReplicaGetInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ if (p->last_fault_code != DCERPC_FAULT_INVALID_TAG) {
+ printf("dcerpc_drsuapi_DsReplicaGetInfo failed - %s\n", errstr);
+ ret = false;
+ } else {
+ printf("DsReplicaGetInfo level %d and/or infotype %d not supported by server\n",
+ array[i].level, array[i].infotype);
+ }
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsReplicaGetInfo failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_DsReplicaSync(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ bool ret = true;
+ int i;
+ struct drsuapi_DsReplicaSync r;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ struct {
+ int32_t level;
+ } array[] = {
+ {
+ 1
+ }
+ };
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ printf("DsReplicaSync disabled - enable dangerous tests to use\n");
+ return true;
+ }
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping DsReplicaSync test against Samba4\n");
+ return true;
+ }
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+
+ r.in.bind_handle = &priv->bind_handle;
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ printf("testing DsReplicaSync level %d\n",
+ array[i].level);
+
+ r.in.level = array[i].level;
+ switch(r.in.level) {
+ case 1:
+ nc.guid = null_guid;
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:"";
+
+ r.in.req.req1.naming_context = &nc;
+ r.in.req.req1.source_dsa_guid = priv->dcinfo.ntds_guid;
+ r.in.req.req1.other_info = NULL;
+ r.in.req.req1.options = 16;
+ break;
+ }
+
+ status = dcerpc_drsuapi_DsReplicaSync(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsReplicaSync failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsReplicaSync failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_DsReplicaUpdateRefs(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ bool ret = true;
+ int i;
+ struct drsuapi_DsReplicaUpdateRefs r;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ struct {
+ int32_t level;
+ } array[] = {
+ {
+ 1
+ }
+ };
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping DsReplicaUpdateRefs test against Samba4\n");
+ return true;
+ }
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+
+ r.in.bind_handle = &priv->bind_handle;
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ printf("testing DsReplicaUpdateRefs level %d\n",
+ array[i].level);
+
+ r.in.level = array[i].level;
+ switch(r.in.level) {
+ case 1:
+ nc.guid = null_guid;
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:"";
+
+ r.in.req.req1.naming_context = &nc;
+ r.in.req.req1.dest_dsa_dns_name = talloc_asprintf(tctx, "__some_dest_dsa_guid_string._msdn.%s",
+ priv->domain_dns_name);
+ r.in.req.req1.dest_dsa_guid = null_guid;
+ r.in.req.req1.options = 0;
+ break;
+ }
+
+ status = dcerpc_drsuapi_DsReplicaUpdateRefs(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsReplicaUpdateRefs failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsReplicaUpdateRefs failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_DsGetNCChanges(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ bool ret = true;
+ int i;
+ struct drsuapi_DsGetNCChanges r;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ struct {
+ int32_t level;
+ } array[] = {
+ {
+ 5
+ },
+ {
+ 8
+ }
+ };
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping DsGetNCChanges test against Samba4\n");
+ return true;
+ }
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ printf("testing DsGetNCChanges level %d\n",
+ array[i].level);
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = &array[i].level;
+
+ switch (*r.in.level) {
+ case 5:
+ nc.guid = null_guid;
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:"";
+
+ r.in.req.req5.destination_dsa_guid = GUID_random();
+ r.in.req.req5.source_dsa_invocation_id = null_guid;
+ r.in.req.req5.naming_context = &nc;
+ r.in.req.req5.highwatermark.tmp_highest_usn = 0;
+ r.in.req.req5.highwatermark.reserved_usn = 0;
+ r.in.req.req5.highwatermark.highest_usn = 0;
+ r.in.req.req5.uptodateness_vector = NULL;
+ r.in.req.req5.replica_flags = 0;
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "drsuapi","compression", false)) {
+ r.in.req.req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+ }
+ r.in.req.req5.max_object_count = 0;
+ r.in.req.req5.max_ndr_size = 0;
+ r.in.req.req5.extended_op = DRSUAPI_EXOP_NONE;
+ r.in.req.req5.fsmo_info = 0;
+
+ break;
+ case 8:
+ nc.guid = null_guid;
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:"";
+
+ r.in.req.req8.destination_dsa_guid = GUID_random();
+ r.in.req.req8.source_dsa_invocation_id = null_guid;
+ r.in.req.req8.naming_context = &nc;
+ r.in.req.req8.highwatermark.tmp_highest_usn = 0;
+ r.in.req.req8.highwatermark.reserved_usn = 0;
+ r.in.req.req8.highwatermark.highest_usn = 0;
+ r.in.req.req8.uptodateness_vector = NULL;
+ r.in.req.req8.replica_flags = 0;
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "compression", false)) {
+ r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+ }
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "neighbour_writeable", true)) {
+ r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE;
+ }
+ r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
+ ;
+ r.in.req.req8.max_object_count = 402;
+ r.in.req.req8.max_ndr_size = 402116;
+ r.in.req.req8.extended_op = DRSUAPI_EXOP_NONE;
+ r.in.req.req8.fsmo_info = 0;
+ r.in.req.req8.partial_attribute_set = NULL;
+ r.in.req.req8.partial_attribute_set_ex = NULL;
+ r.in.req.req8.mapping_ctr.num_mappings = 0;
+ r.in.req.req8.mapping_ctr.mappings = NULL;
+
+ break;
+ }
+
+ status = dcerpc_drsuapi_DsGetNCChanges(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(tctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsGetNCChanges failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsGetNCChanges failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+bool test_QuerySitesByCost(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_QuerySitesByCost r;
+ bool ret = true;
+
+ const char *my_site = "Default-First-Site-Name";
+ const char *remote_site1 = "smbtorture-nonexisting-site1";
+ const char *remote_site2 = "smbtorture-nonexisting-site2";
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+ r.in.req.req1.site_from = talloc_strdup(mem_ctx, my_site);
+ r.in.req.req1.num_req = 2;
+ r.in.req.req1.site_to = talloc_zero_array(mem_ctx, const char *, r.in.req.req1.num_req);
+ r.in.req.req1.site_to[0] = talloc_strdup(mem_ctx, remote_site1);
+ r.in.req.req1.site_to[1] = talloc_strdup(mem_ctx, remote_site2);
+ r.in.req.req1.flags = 0;
+
+ status = dcerpc_drsuapi_QuerySitesByCost(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("drsuapi_QuerySitesByCost - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("QuerySitesByCost failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+
+ if (!W_ERROR_EQUAL(r.out.ctr.ctr1.info[0].error_code, WERR_DS_OBJ_NOT_FOUND) ||
+ !W_ERROR_EQUAL(r.out.ctr.ctr1.info[1].error_code, WERR_DS_OBJ_NOT_FOUND)) {
+ printf("expected error_code WERR_DS_OBJ_NOT_FOUND, got %s\n",
+ win_errstr(r.out.ctr.ctr1.info[0].error_code));
+ ret = false;
+ }
+
+ if ((r.out.ctr.ctr1.info[0].site_cost != (uint32_t) -1) ||
+ (r.out.ctr.ctr1.info[1].site_cost != (uint32_t) -1)) {
+ printf("expected site_cost %d, got %d\n",
+ (uint32_t) -1, r.out.ctr.ctr1.info[0].site_cost);
+ ret = false;
+ }
+ }
+
+ return ret;
+
+
+}
+
+bool test_DsUnbind(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsUnbind r;
+ bool ret = true;
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.out.bind_handle = &priv->bind_handle;
+
+ printf("testing DsUnbind\n");
+
+ status = dcerpc_drsuapi_DsUnbind(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsUnbind failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsBind failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool torture_rpc_drsuapi(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct DsPrivate priv;
+ struct cli_credentials *machine_credentials;
+
+ ZERO_STRUCT(priv);
+
+ priv.join = torture_join_domain(torture, TEST_MACHINE_NAME, ACB_SVRTRUST,
+ &machine_credentials);
+ if (!priv.join) {
+ torture_fail(torture, "Failed to join as BDC");
+ }
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_drsuapi);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_leave_domain(torture, priv.join);
+ torture_fail(torture, "Unable to connect to DRSUAPI pipe");
+ }
+
+ ret &= test_DsBind(p, torture, &priv);
+#if 0
+ ret &= test_QuerySitesByCost(p, torture, &priv);
+#endif
+ ret &= test_DsGetDomainControllerInfo(p, torture, &priv);
+
+ ret &= test_DsCrackNames(torture, p, torture, &priv);
+
+ ret &= test_DsWriteAccountSpn(p, torture, &priv);
+
+ ret &= test_DsReplicaGetInfo(p, torture, &priv);
+
+ ret &= test_DsReplicaSync(p, torture, &priv);
+
+ ret &= test_DsReplicaUpdateRefs(p, torture, &priv);
+
+ ret &= test_DsGetNCChanges(p, torture, &priv);
+
+ ret &= test_DsUnbind(p, torture, &priv);
+
+ torture_leave_domain(torture, priv.join);
+
+ return ret;
+}
+
+
+bool torture_rpc_drsuapi_cracknames(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct DsPrivate priv;
+ struct cli_credentials *machine_credentials;
+
+ torture_comment(torture, "Connected to DRSUAPI pipe\n");
+
+ ZERO_STRUCT(priv);
+
+ priv.join = torture_join_domain(torture, TEST_MACHINE_NAME, ACB_SVRTRUST,
+ &machine_credentials);
+ if (!priv.join) {
+ torture_fail(torture, "Failed to join as BDC\n");
+ }
+
+ status = torture_rpc_connection(torture,
+ &p,
+ &ndr_table_drsuapi);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_leave_domain(torture, priv.join);
+ torture_fail(torture, "Unable to connect to DRSUAPI pipe");
+ }
+
+ ret &= test_DsBind(p, torture, &priv);
+
+ if (ret) {
+ /* We don't care if this fails, we just need some info from it */
+ test_DsGetDomainControllerInfo(p, torture, &priv);
+
+ ret &= test_DsCrackNames(torture, p, torture, &priv);
+
+ ret &= test_DsUnbind(p, torture, &priv);
+ }
+
+ torture_leave_domain(torture, priv.join);
+
+ return ret;
+}
+
diff --git a/source4/torture/rpc/drsuapi.h b/source4/torture/rpc/drsuapi.h
new file mode 100644
index 0000000000..98438cc9b7
--- /dev/null
+++ b/source4/torture/rpc/drsuapi.h
@@ -0,0 +1,36 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DRSUapi tests
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ 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 "librpc/gen_ndr/drsuapi.h"
+
+struct DsPrivate {
+ struct policy_handle bind_handle;
+ struct GUID bind_guid;
+ const char *domain_obj_dn;
+ const char *domain_guid_str;
+ const char *domain_dns_name;
+ struct GUID domain_guid;
+ struct drsuapi_DsGetDCInfo2 dcinfo;
+ struct test_join *join;
+};
+
diff --git a/source4/torture/rpc/drsuapi_cracknames.c b/source4/torture/rpc/drsuapi_cracknames.c
new file mode 100644
index 0000000000..fbda69df57
--- /dev/null
+++ b/source4/torture/rpc/drsuapi_cracknames.c
@@ -0,0 +1,997 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DRSUapi tests
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "torture/rpc/rpc.h"
+#include "ldb/include/ldb.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+
+static bool test_DsCrackNamesMatrix(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv, const char *dn,
+ const char *user_principal_name, const char *service_principal_name)
+{
+
+
+ NTSTATUS status;
+ bool ret = true;
+ struct drsuapi_DsCrackNames r;
+ enum drsuapi_DsNameFormat formats[] = {
+ DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ DRSUAPI_DS_NAME_FORMAT_DISPLAY,
+ DRSUAPI_DS_NAME_FORMAT_GUID,
+ DRSUAPI_DS_NAME_FORMAT_CANONICAL,
+ DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
+ DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN
+ };
+ struct drsuapi_DsNameString names[ARRAY_SIZE(formats)];
+ int i, j;
+
+ const char *n_matrix[ARRAY_SIZE(formats)][ARRAY_SIZE(formats)];
+ const char *n_from[ARRAY_SIZE(formats)];
+
+ ZERO_STRUCT(r);
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+ 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;
+
+ n_matrix[0][0] = dn;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ r.in.req.req1.format_desired = formats[i];
+ names[0].str = dn;
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d ",
+ names[0].str, r.in.req.req1.format_offered, r.in.req.req1.format_desired);
+
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("testing DsCrackNames (matrix prep) with name '%s' from format: %d desired format:%d ",
+ names[0].str, r.in.req.req1.format_offered, r.in.req.req1.format_desired);
+
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+ switch (formats[i]) {
+ case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
+ if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE) {
+ printf(__location__ ": Unexpected error (%d): This name lookup should fail\n",
+ r.out.ctr.ctr1->array[0].status);
+ return false;
+ }
+ printf ("(expected) error\n");
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
+ if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NO_MAPPING) {
+ printf(__location__ ": Unexpected error (%d): This name lookup should fail\n",
+ r.out.ctr.ctr1->array[0].status);
+ return false;
+ }
+ printf ("(expected) error\n");
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN:
+ case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY:
+ if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR) {
+ printf(__location__ ": Unexpected error (%d): This name lookup should fail\n",
+ r.out.ctr.ctr1->array[0].status);
+ return false;
+ }
+ printf ("(expected) error\n");
+ break;
+ default:
+ if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("Error: %d\n", r.out.ctr.ctr1->array[0].status);
+ return false;
+ }
+ }
+
+ switch (formats[i]) {
+ case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
+ n_from[i] = user_principal_name;
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
+ n_from[i] = service_principal_name;
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY:
+ case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN:
+ n_from[i] = NULL;
+ break;
+ default:
+ n_from[i] = r.out.ctr.ctr1->array[0].result_name;
+ printf("%s\n", n_from[i]);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ for (j = 0; j < ARRAY_SIZE(formats); j++) {
+ r.in.req.req1.format_offered = formats[i];
+ r.in.req.req1.format_desired = formats[j];
+ if (!n_from[i]) {
+ n_matrix[i][j] = NULL;
+ continue;
+ }
+ names[0].str = n_from[i];
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("testing DsCrackNames (matrix) with name '%s' from format: %d desired format:%d failed - %s",
+ names[0].str, r.in.req.req1.format_offered, r.in.req.req1.format_desired, errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("testing DsCrackNames (matrix) with name '%s' from format: %d desired format:%d failed - %s",
+ names[0].str, r.in.req.req1.format_offered, r.in.req.req1.format_desired,
+ win_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+ if (r.out.ctr.ctr1->array[0].status == DRSUAPI_DS_NAME_STATUS_OK) {
+ n_matrix[i][j] = r.out.ctr.ctr1->array[0].result_name;
+ } else {
+ n_matrix[i][j] = NULL;
+ }
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ for (j = 0; j < ARRAY_SIZE(formats); j++) {
+ if (n_matrix[i][j] == n_from[j]) {
+
+ /* We don't have a from name for these yet (and we can't map to them to find it out) */
+ } else if (n_matrix[i][j] == NULL && n_from[i] == NULL) {
+
+ /* we can't map to these two */
+ } else if (n_matrix[i][j] == NULL && formats[j] == DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL) {
+ } else if (n_matrix[i][j] == NULL && formats[j] == DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL) {
+ } else if (n_matrix[i][j] == NULL && n_from[j] != NULL) {
+ printf("dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s\n", formats[i], formats[j], n_matrix[i][j], n_from[j]);
+ ret = false;
+ } else if (n_matrix[i][j] != NULL && n_from[j] == NULL) {
+ printf("dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s\n", formats[i], formats[j], n_matrix[i][j], n_from[j]);
+ ret = false;
+ } else if (strcmp(n_matrix[i][j], n_from[j]) != 0) {
+ printf("dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s\n", formats[i], formats[j], n_matrix[i][j], n_from[j]);
+ ret = false;
+ }
+ }
+ }
+ return ret;
+}
+
+bool test_DsCrackNames(struct torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsCrackNames r;
+ struct drsuapi_DsNameString names[1];
+ bool ret = true;
+ const char *dns_domain;
+ const char *nt4_domain;
+ const char *FQDN_1779_name;
+ struct ldb_context *ldb;
+ struct ldb_dn *FQDN_1779_dn;
+ struct ldb_dn *realm_dn;
+ const char *realm_dn_str;
+ const char *realm_canonical;
+ const char *realm_canonical_ex;
+ const char *user_principal_name;
+ char *user_principal_name_short;
+ const char *service_principal_name;
+ const char *canonical_name;
+ const char *canonical_ex_name;
+ const char *dom_sid;
+ const char *test_dc = torture_join_netbios_name(priv->join);
+
+ ZERO_STRUCT(r);
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+ 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;
+
+ dom_sid = dom_sid_string(mem_ctx, torture_join_sid(priv->join));
+
+ names[0].str = dom_sid;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req.req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ dns_domain = r.out.ctr.ctr1->array[0].dns_domain_name;
+ nt4_domain = r.out.ctr.ctr1->array[0].result_name;
+
+ r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_GUID;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req.req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ priv->domain_dns_name = r.out.ctr.ctr1->array[0].dns_domain_name;
+ priv->domain_guid_str = r.out.ctr.ctr1->array[0].result_name;
+ GUID_from_string(priv->domain_guid_str, &priv->domain_guid);
+
+ r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req.req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ ldb = ldb_init(mem_ctx, tctx->ev);
+
+ realm_dn_str = r.out.ctr.ctr1->array[0].result_name;
+ realm_dn = ldb_dn_new(mem_ctx, ldb, realm_dn_str);
+ realm_canonical = ldb_dn_canonical_string(mem_ctx, realm_dn);
+
+ if (strcmp(realm_canonical,
+ talloc_asprintf(mem_ctx, "%s/", dns_domain))!= 0) {
+ printf("local Round trip on canonical name failed: %s != %s!\n",
+ realm_canonical,
+ talloc_asprintf(mem_ctx, "%s/", dns_domain));
+ return false;
+ };
+
+ realm_canonical_ex = ldb_dn_canonical_ex_string(mem_ctx, realm_dn);
+
+ if (strcmp(realm_canonical_ex,
+ talloc_asprintf(mem_ctx, "%s\n", dns_domain))!= 0) {
+ printf("local Round trip on canonical ex name failed: %s != %s!\n",
+ realm_canonical,
+ talloc_asprintf(mem_ctx, "%s\n", dns_domain));
+ return false;
+ };
+
+ r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+ r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = nt4_domain;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req.req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ priv->domain_obj_dn = r.out.ctr.ctr1->array[0].result_name;
+
+ r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+ r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = talloc_asprintf(mem_ctx, "%s%s$", nt4_domain, test_dc);
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req.req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ FQDN_1779_name = r.out.ctr.ctr1->array[0].result_name;
+
+ r.in.req.req1.format_offered = DRSUAPI_DS_NAME_FORMAT_GUID;
+ r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = priv->domain_guid_str;
+
+ printf("testing DsCrackNames with name '%s' desired format:%d\n",
+ names[0].str, r.in.req.req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr.ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ printf("DsCrackNames failed on name - %d\n", r.out.ctr.ctr1->array[0].status);
+ ret = false;
+ }
+
+ if (!ret) {
+ return ret;
+ }
+
+ if (strcmp(priv->domain_dns_name, r.out.ctr.ctr1->array[0].dns_domain_name) != 0) {
+ printf("DsCrackNames failed to return same DNS name - expected %s got %s\n", priv->domain_dns_name, r.out.ctr.ctr1->array[0].dns_domain_name);
+ return false;
+ }
+
+ FQDN_1779_dn = ldb_dn_new(mem_ctx, ldb, FQDN_1779_name);
+
+ canonical_name = ldb_dn_canonical_string(mem_ctx, FQDN_1779_dn);
+ canonical_ex_name = ldb_dn_canonical_ex_string(mem_ctx, FQDN_1779_dn);
+
+ user_principal_name = talloc_asprintf(mem_ctx, "%s$@%s", test_dc, dns_domain);
+
+ /* form up a user@DOMAIN */
+ user_principal_name_short = talloc_asprintf(mem_ctx, "%s$@%s", test_dc, nt4_domain);
+ /* variable nt4_domain includs a trailing \ */
+ user_principal_name_short[strlen(user_principal_name_short) - 1] = '\0';
+
+ service_principal_name = talloc_asprintf(mem_ctx, "HOST/%s", test_dc);
+ {
+
+ struct {
+ enum drsuapi_DsNameFormat format_offered;
+ enum drsuapi_DsNameFormat format_desired;
+ const char *comment;
+ const char *str;
+ const char *expected_str;
+ const char *expected_dns;
+ enum drsuapi_DsNameStatus status;
+ enum drsuapi_DsNameStatus alternate_status;
+ enum drsuapi_DsNameFlags flags;
+ bool skip;
+ } crack[] = {
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = user_principal_name,
+ .expected_str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = user_principal_name_short,
+ .expected_str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ .str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = service_principal_name,
+ .expected_str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "cifs/%s.%s", test_dc, dns_domain),
+ .comment = "ServicePrincipal Name",
+ .expected_str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL,
+ .str = FQDN_1779_name,
+ .expected_str = canonical_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = canonical_name,
+ .expected_str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
+ .str = FQDN_1779_name,
+ .expected_str = canonical_ex_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = canonical_ex_name,
+ .expected_str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL,
+ .str = FQDN_1779_name,
+ .comment = "DN to cannoical syntactial only",
+ .status = DRSUAPI_DS_NAME_STATUS_OK,
+ .expected_str = canonical_name,
+ .flags = DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
+ .str = FQDN_1779_name,
+ .comment = "DN to cannoical EX syntactial only",
+ .status = DRSUAPI_DS_NAME_STATUS_OK,
+ .expected_str = canonical_ex_name,
+ .flags = DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_DISPLAY,
+ .str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .str = priv->domain_guid_str,
+ .comment = "Domain GUID to NT4 ACCOUNT",
+ .expected_str = nt4_domain,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL,
+ .str = priv->domain_guid_str,
+ .comment = "Domain GUID to Canonical",
+ .expected_str = talloc_asprintf(mem_ctx, "%s/", dns_domain),
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
+ .str = priv->domain_guid_str,
+ .comment = "Domain GUID to Canonical EX",
+ .expected_str = talloc_asprintf(mem_ctx, "%s\n", dns_domain),
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_DISPLAY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = "CN=Microsoft Corporation,L=Redmond,S=Washington,C=US",
+ .comment = "display name for Microsoft Support Account",
+ .status = DRSUAPI_DS_NAME_STATUS_OK,
+ .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE,
+ .skip = torture_setting_bool(tctx, "samba4", false)
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = GUID_string2(mem_ctx, torture_join_user_guid(priv->join)),
+ .comment = "Account GUID -> DN",
+ .expected_str = FQDN_1779_name,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .str = GUID_string2(mem_ctx, torture_join_user_guid(priv->join)),
+ .comment = "Account GUID -> NT4 Account",
+ .expected_str = talloc_asprintf(mem_ctx, "%s%s$", nt4_domain, test_dc),
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = GUID_string2(mem_ctx, &priv->dcinfo.site_guid),
+ .comment = "Site GUID",
+ .expected_str = priv->dcinfo.site_dn,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = GUID_string2(mem_ctx, &priv->dcinfo.computer_guid),
+ .comment = "Computer GUID",
+ .expected_str = priv->dcinfo.computer_dn,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .str = GUID_string2(mem_ctx, &priv->dcinfo.computer_guid),
+ .comment = "Computer GUID -> NT4 Account",
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = GUID_string2(mem_ctx, &priv->dcinfo.server_guid),
+ .comment = "Server GUID",
+ .expected_str = priv->dcinfo.server_dn,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = GUID_string2(mem_ctx, &priv->dcinfo.ntds_guid),
+ .comment = "NTDS GUID",
+ .expected_str = priv->dcinfo.ntds_dn,
+ .status = DRSUAPI_DS_NAME_STATUS_OK,
+ .skip = GUID_all_zero(&priv->dcinfo.ntds_guid)
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_DISPLAY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = test_dc,
+ .comment = "DISLPAY NAME search for DC short name",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "krbtgt/%s", dns_domain),
+ .comment = "Looking for KRBTGT as a serivce principal",
+ .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY,
+ .expected_dns = dns_domain
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "bogus/%s", dns_domain),
+ .comment = "Looking for bogus serivce principal",
+ .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY,
+ .expected_dns = dns_domain
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "bogus/%s.%s", test_dc, dns_domain),
+ .comment = "Looking for bogus serivce on test DC",
+ .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY,
+ .expected_dns = talloc_asprintf(mem_ctx, "%s.%s", test_dc, dns_domain)
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "krbtgt"),
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "Looking for the kadmin/changepw service as a serivce principal",
+ .str = talloc_asprintf(mem_ctx, "kadmin/changepw"),
+ .status = DRSUAPI_DS_NAME_STATUS_OK,
+ .expected_str = talloc_asprintf(mem_ctx, "CN=krbtgt,CN=Users,%s", realm_dn_str),
+ .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "cifs/%s.%s@%s",
+ test_dc, dns_domain,
+ dns_domain),
+ .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "cifs/%s.%s@%s",
+ test_dc, dns_domain,
+ "BOGUS"),
+ .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY,
+ .expected_dns = "BOGUS"
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "cifs/%s.%s@%s",
+ test_dc, "REALLY",
+ "BOGUS"),
+ .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY,
+ .expected_dns = "BOGUS"
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "cifs/%s.%s",
+ test_dc, dns_domain),
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = talloc_asprintf(mem_ctx, "cifs/%s",
+ test_dc),
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = "NOT A GUID",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = "NOT A SID",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = "NOT AN NT4 NAME",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .comment = "Unparsable DN",
+ .str = "NOT A DN",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "Unparsable user principal",
+ .str = "NOT A PRINCIPAL",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "Unparsable service principal",
+ .str = "NOT A SERVICE PRINCIPAL",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_GUID,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "BIND GUID (ie, not in the directory)",
+ .str = GUID_string2(mem_ctx, &priv->bind_guid),
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "Unqualified Machine account as user principal",
+ .str = talloc_asprintf(mem_ctx, "%s$", test_dc),
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "Machine account as service principal",
+ .str = talloc_asprintf(mem_ctx, "%s$", test_dc),
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "Full Machine account as service principal",
+ .str = user_principal_name,
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "Realm as an NT4 domain lookup",
+ .str = talloc_asprintf(mem_ctx, "%s\\", dns_domain),
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "BUILTIN\\ -> DN",
+ .str = "BUILTIN\\",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "NT AUTHORITY\\ -> DN",
+ .str = "NT AUTHORITY\\",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "NT AUTHORITY\\ANONYMOUS LOGON -> DN",
+ .str = "NT AUTHORITY\\ANONYMOUS LOGON",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "NT AUTHORITY\\SYSTEM -> DN",
+ .str = "NT AUTHORITY\\SYSTEM",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .comment = "BUITIN SID -> NT4 account",
+ .str = SID_BUILTIN,
+ .status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING,
+ .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = SID_BUILTIN,
+ .comment = "Builtin Domain SID -> DN",
+ .status = DRSUAPI_DS_NAME_STATUS_OK,
+ .expected_str = talloc_asprintf(mem_ctx, "CN=Builtin,%s", realm_dn_str),
+ .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .str = SID_BUILTIN_ADMINISTRATORS,
+ .comment = "Builtin Administrors SID -> DN",
+ .status = DRSUAPI_DS_NAME_STATUS_OK,
+ .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .str = SID_BUILTIN_ADMINISTRATORS,
+ .comment = "Builtin Administrors SID -> NT4 Account",
+ .status = DRSUAPI_DS_NAME_STATUS_OK,
+ .alternate_status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .str = SID_NT_ANONYMOUS,
+ .comment = "NT Anonymous SID -> NT4 Account",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .str = SID_NT_SYSTEM,
+ .comment = "NT SYSTEM SID -> NT4 Account",
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "Domain SID -> DN",
+ .str = dom_sid,
+ .expected_str = realm_dn_str,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
+ .comment = "Domain SID -> NT4 account",
+ .str = dom_sid,
+ .expected_str = nt4_domain,
+ .status = DRSUAPI_DS_NAME_STATUS_OK
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "invalid user principal name",
+ .str = "foo@bar",
+ .status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY,
+ .expected_dns = "bar"
+ },
+ {
+ .format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
+ .format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
+ .comment = "invalid user principal name in valid domain",
+ .str = talloc_asprintf(mem_ctx, "invalidusername@%s", dns_domain),
+ .status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND
+ }
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(crack); i++) {
+ const char *comment;
+ r.in.req.req1.format_flags = crack[i].flags;
+ r.in.req.req1.format_offered = crack[i].format_offered;
+ r.in.req.req1.format_desired = crack[i].format_desired;
+ names[0].str = crack[i].str;
+
+ if (crack[i].comment) {
+ comment = talloc_asprintf(mem_ctx, "'%s' with name '%s' desired format:%d\n",
+ crack[i].comment, names[0].str, r.in.req.req1.format_desired);
+ } else {
+ comment = talloc_asprintf(mem_ctx, "'%s' desired format:%d\n",
+ names[0].str, r.in.req.req1.format_desired);
+ }
+ if (crack[i].skip) {
+ printf("skipping: %s", comment);
+ continue;
+ }
+ status = dcerpc_drsuapi_DsCrackNames(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(mem_ctx, p->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed on %s - %s\n", comment, errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.ctr.ctr1->array[0].status != crack[i].status) {
+ if (crack[i].alternate_status) {
+ if (r.out.ctr.ctr1->array[0].status != crack[i].alternate_status) {
+ printf("DsCrackNames unexpected status %d, wanted %d or %d on: %s\n",
+ r.out.ctr.ctr1->array[0].status,
+ crack[i].status,
+ crack[i].alternate_status,
+ comment);
+ ret = false;
+ }
+ } else {
+ printf("DsCrackNames unexpected status %d, wanted %d on: %s\n",
+ r.out.ctr.ctr1->array[0].status,
+ crack[i].status,
+ comment);
+ ret = false;
+ }
+ } else if (crack[i].expected_str
+ && (strcmp(r.out.ctr.ctr1->array[0].result_name,
+ crack[i].expected_str) != 0)) {
+ if (strcasecmp(r.out.ctr.ctr1->array[0].result_name,
+ crack[i].expected_str) != 0) {
+ printf("DsCrackNames failed - got %s, expected %s on %s\n",
+ r.out.ctr.ctr1->array[0].result_name,
+ crack[i].expected_str, comment);
+ ret = false;
+ } else {
+ printf("(warning) DsCrackNames returned different case - got %s, expected %s on %s\n",
+ r.out.ctr.ctr1->array[0].result_name,
+ crack[i].expected_str, comment);
+ }
+ } else if (crack[i].expected_dns
+ && (strcmp(r.out.ctr.ctr1->array[0].dns_domain_name,
+ crack[i].expected_dns) != 0)) {
+ printf("DsCrackNames failed - got DNS name %s, expected %s on %s\n",
+ r.out.ctr.ctr1->array[0].result_name,
+ crack[i].expected_str, comment);
+ ret = false;
+ }
+ }
+ }
+
+ if (!test_DsCrackNamesMatrix(p, mem_ctx, priv, FQDN_1779_name,
+ user_principal_name, service_principal_name)) {
+ ret = false;
+ }
+
+ return ret;
+}
diff --git a/source4/torture/rpc/dssetup.c b/source4/torture/rpc/dssetup.c
new file mode 100644
index 0000000000..aa5f12eb92
--- /dev/null
+++ b/source4/torture/rpc/dssetup.c
@@ -0,0 +1,64 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for dssetup rpc operations
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_dssetup_c.h"
+#include "torture/rpc/rpc.h"
+
+
+bool test_DsRoleGetPrimaryDomainInformation_ext(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ NTSTATUS ext_status)
+{
+ struct dssetup_DsRoleGetPrimaryDomainInformation r;
+ NTSTATUS status;
+ int i;
+
+ for (i=DS_ROLE_BASIC_INFORMATION; i <= DS_ROLE_OP_STATUS; i++) {
+ r.in.level = i;
+ torture_comment(tctx, "dcerpc_dssetup_DsRoleGetPrimaryDomainInformation level %d\n", i);
+
+ status = dcerpc_dssetup_DsRoleGetPrimaryDomainInformation(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, ext_status, status, "DsRoleGetPrimaryDomainInformation failed");
+ if (NT_STATUS_IS_OK(ext_status)) {
+ torture_assert_werr_ok(tctx, r.out.result, "DsRoleGetPrimaryDomainInformation failed");
+ }
+ }
+
+ return true;
+}
+
+bool test_DsRoleGetPrimaryDomainInformation(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_DsRoleGetPrimaryDomainInformation_ext(tctx, p, NT_STATUS_OK);
+}
+
+struct torture_suite *torture_rpc_dssetup(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "DSSETUP");
+ struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, "dssetup", &ndr_table_dssetup);
+
+ torture_rpc_tcase_add_test(tcase, "DsRoleGetPrimaryDomainInformation", test_DsRoleGetPrimaryDomainInformation);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/dssync.c b/source4/torture/rpc/dssync.c
new file mode 100644
index 0000000000..e9346f9605
--- /dev/null
+++ b/source4/torture/rpc/dssync.c
@@ -0,0 +1,893 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DsGetNCChanges replication test
+
+ Copyright (C) Stefan (metze) Metzmacher 2005
+ Copyright (C) Brad Henry 2005
+
+ 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 "includes.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "libcli/cldap/cldap.h"
+#include "libcli/ldap/ldap_client.h"
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "lib/crypto/crypto.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/auth/libcli_auth.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+#include "dsdb/samdb/samdb.h"
+
+struct DsSyncBindInfo {
+ struct dcerpc_pipe *pipe;
+ struct drsuapi_DsBind req;
+ struct GUID bind_guid;
+ struct drsuapi_DsBindInfoCtr our_bind_info_ctr;
+ struct drsuapi_DsBindInfo28 our_bind_info28;
+ struct drsuapi_DsBindInfo28 peer_bind_info28;
+ struct policy_handle bind_handle;
+};
+
+struct DsSyncLDAPInfo {
+ struct ldap_connection *conn;
+};
+
+struct DsSyncTest {
+ struct dcerpc_binding *drsuapi_binding;
+
+ const char *ldap_url;
+ const char *site_name;
+
+ const char *domain_dn;
+
+ /* what we need to do as 'Administrator' */
+ struct {
+ struct cli_credentials *credentials;
+ struct DsSyncBindInfo drsuapi;
+ struct DsSyncLDAPInfo ldap;
+ } admin;
+
+ /* what we need to do as the new dc machine account */
+ struct {
+ struct cli_credentials *credentials;
+ struct DsSyncBindInfo drsuapi;
+ struct drsuapi_DsGetDCInfo2 dc_info2;
+ struct GUID invocation_id;
+ struct GUID object_guid;
+ } new_dc;
+
+ /* info about the old dc */
+ struct {
+ struct drsuapi_DsGetDomainControllerInfo dc_info;
+ } old_dc;
+};
+
+static struct DsSyncTest *test_create_context(struct torture_context *tctx)
+{
+ NTSTATUS status;
+ struct DsSyncTest *ctx;
+ struct drsuapi_DsBindInfo28 *our_bind_info28;
+ struct drsuapi_DsBindInfoCtr *our_bind_info_ctr;
+ const char *binding = torture_setting_string(tctx, "binding", NULL);
+ ctx = talloc_zero(tctx, struct DsSyncTest);
+ if (!ctx) return NULL;
+
+ status = dcerpc_parse_binding(ctx, binding, &ctx->drsuapi_binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Bad binding string %s\n", binding);
+ return NULL;
+ }
+ ctx->drsuapi_binding->flags |= DCERPC_SIGN | DCERPC_SEAL;
+
+ ctx->ldap_url = talloc_asprintf(ctx, "ldap://%s/", ctx->drsuapi_binding->host);
+
+ /* ctx->admin ...*/
+ ctx->admin.credentials = cmdline_credentials;
+
+ our_bind_info28 = &ctx->admin.drsuapi.our_bind_info28;
+ our_bind_info28->supported_extensions = 0xFFFFFFFF;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
+ our_bind_info28->site_guid = GUID_zero();
+ our_bind_info28->pid = 0;
+ our_bind_info28->repl_epoch = 1;
+
+ our_bind_info_ctr = &ctx->admin.drsuapi.our_bind_info_ctr;
+ our_bind_info_ctr->length = 28;
+ our_bind_info_ctr->info.info28 = *our_bind_info28;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &ctx->admin.drsuapi.bind_guid);
+
+ ctx->admin.drsuapi.req.in.bind_guid = &ctx->admin.drsuapi.bind_guid;
+ ctx->admin.drsuapi.req.in.bind_info = our_bind_info_ctr;
+ ctx->admin.drsuapi.req.out.bind_handle = &ctx->admin.drsuapi.bind_handle;
+
+ /* ctx->new_dc ...*/
+ ctx->new_dc.credentials = cmdline_credentials;
+
+ our_bind_info28 = &ctx->new_dc.drsuapi.our_bind_info28;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "xpress", false)) {
+ our_bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_XPRESS_COMPRESS;
+ }
+ our_bind_info28->site_guid = GUID_zero();
+ our_bind_info28->pid = 0;
+ our_bind_info28->repl_epoch = 0;
+
+ our_bind_info_ctr = &ctx->new_dc.drsuapi.our_bind_info_ctr;
+ our_bind_info_ctr->length = 28;
+ our_bind_info_ctr->info.info28 = *our_bind_info28;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID_W2K3, &ctx->new_dc.drsuapi.bind_guid);
+
+ ctx->new_dc.drsuapi.req.in.bind_guid = &ctx->new_dc.drsuapi.bind_guid;
+ ctx->new_dc.drsuapi.req.in.bind_info = our_bind_info_ctr;
+ ctx->new_dc.drsuapi.req.out.bind_handle = &ctx->new_dc.drsuapi.bind_handle;
+
+ ctx->new_dc.invocation_id = ctx->new_dc.drsuapi.bind_guid;
+
+ /* ctx->old_dc ...*/
+
+ return ctx;
+}
+
+static bool _test_DsBind(struct torture_context *tctx,
+ struct DsSyncTest *ctx, struct cli_credentials *credentials, struct DsSyncBindInfo *b)
+{
+ NTSTATUS status;
+ bool ret = true;
+
+ status = dcerpc_pipe_connect_b(ctx,
+ &b->pipe, ctx->drsuapi_binding,
+ &ndr_table_drsuapi,
+ credentials, tctx->ev, tctx->lp_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect to server as a BDC: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_drsuapi_DsBind(b->pipe, ctx, &b->req);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(ctx, b->pipe->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsBind failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(b->req.out.result)) {
+ printf("DsBind failed - %s\n", win_errstr(b->req.out.result));
+ ret = false;
+ }
+
+ ZERO_STRUCT(b->peer_bind_info28);
+ if (b->req.out.bind_info) {
+ switch (b->req.out.bind_info->length) {
+ case 24: {
+ struct drsuapi_DsBindInfo24 *info24;
+ info24 = &b->req.out.bind_info->info.info24;
+ b->peer_bind_info28.supported_extensions= info24->supported_extensions;
+ b->peer_bind_info28.site_guid = info24->site_guid;
+ b->peer_bind_info28.pid = info24->pid;
+ b->peer_bind_info28.repl_epoch = 0;
+ break;
+ }
+ case 48: {
+ struct drsuapi_DsBindInfo48 *info48;
+ info48 = &b->req.out.bind_info->info.info48;
+ b->peer_bind_info28.supported_extensions= info48->supported_extensions;
+ b->peer_bind_info28.site_guid = info48->site_guid;
+ b->peer_bind_info28.pid = info48->pid;
+ b->peer_bind_info28.repl_epoch = info48->repl_epoch;
+ break;
+ }
+ case 28:
+ b->peer_bind_info28 = b->req.out.bind_info->info.info28;
+ break;
+ default:
+ printf("DsBind - warning: unknown BindInfo length: %u\n",
+ b->req.out.bind_info->length);
+ }
+ }
+
+ return ret;
+}
+
+static bool test_LDAPBind(struct torture_context *tctx, struct DsSyncTest *ctx,
+ struct cli_credentials *credentials, struct DsSyncLDAPInfo *l)
+{
+ NTSTATUS status;
+ bool ret = true;
+
+ status = torture_ldap_connection(tctx, &l->conn, ctx->ldap_url);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to connect to LDAP: %s\n", ctx->ldap_url);
+ return false;
+ }
+
+ printf("connected to LDAP: %s\n", ctx->ldap_url);
+
+ status = torture_ldap_bind_sasl(l->conn, credentials, tctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to bind to LDAP:\n");
+ return false;
+ }
+ printf("bound to LDAP.\n");
+
+ return ret;
+}
+
+static bool test_GetInfo(struct torture_context *tctx, struct DsSyncTest *ctx)
+{
+ NTSTATUS status;
+ struct drsuapi_DsCrackNames r;
+ struct drsuapi_DsNameString names[1];
+ bool ret = true;
+ struct cldap_socket *cldap;
+ struct cldap_netlogon search;
+
+ cldap = cldap_socket_init(ctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx));
+
+ r.in.bind_handle = &ctx->admin.drsuapi.bind_handle;
+ r.in.level = 1;
+ r.in.req.req1.codepage = 1252; /* western european */
+ 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_NT4_ACCOUNT;
+ r.in.req.req1.format_desired = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
+ names[0].str = talloc_asprintf(ctx, "%s\\", lp_workgroup(tctx->lp_ctx));
+
+ status = dcerpc_drsuapi_DsCrackNames(ctx->admin.drsuapi.pipe, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(ctx, ctx->admin.drsuapi.pipe->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsCrackNames failed - %s\n", errstr);
+ return false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsCrackNames failed - %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ ctx->domain_dn = r.out.ctr.ctr1->array[0].result_name;
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = ctx->drsuapi_binding->host;
+ search.in.dest_port = lp_cldap_port(tctx->lp_ctx);
+ search.in.acct_control = -1;
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ search.in.map_response = true;
+ status = cldap_netlogon(cldap, ctx, &search);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ ctx->site_name = talloc_asprintf(ctx, "%s", "Default-First-Site-Name");
+ printf("cldap_netlogon() returned %s. Defaulting to Site-Name: %s\n", errstr, ctx->site_name);
+ } else {
+ ctx->site_name = talloc_steal(ctx, search.out.netlogon.nt5_ex.client_site);
+ printf("cldap_netlogon() returned Client Site-Name: %s.\n",ctx->site_name);
+ printf("cldap_netlogon() returned Server Site-Name: %s.\n",search.out.netlogon.nt5_ex.server_site);
+ }
+
+ if (!ctx->domain_dn) {
+ struct ldb_context *ldb = ldb_init(ctx, tctx->ev);
+ struct ldb_dn *dn = samdb_dns_domain_to_dn(ldb, ctx, search.out.netlogon.nt5_ex.dns_domain);
+ ctx->domain_dn = ldb_dn_alloc_linearized(ctx, dn);
+ talloc_free(dn);
+ talloc_free(ldb);
+ }
+
+ return ret;
+}
+
+static DATA_BLOB decrypt_blob(TALLOC_CTX *mem_ctx,
+ const DATA_BLOB *gensec_skey,
+ bool rcrypt,
+ struct drsuapi_DsReplicaObjectIdentifier *id,
+ uint32_t rid,
+ const DATA_BLOB *buffer)
+{
+ DATA_BLOB confounder;
+ DATA_BLOB enc_buffer;
+
+ struct MD5Context md5;
+ uint8_t _enc_key[16];
+ DATA_BLOB enc_key;
+
+ DATA_BLOB dec_buffer;
+
+ uint32_t crc32_given;
+ uint32_t crc32_calc;
+ DATA_BLOB checked_buffer;
+
+ DATA_BLOB plain_buffer;
+
+ /*
+ * the combination "c[3] s[1] e[1] d[0]..."
+ * was successful!!!!!!!!!!!!!!!!!!!!!!!!!!
+ */
+
+ /*
+ * the first 16 bytes at the beginning are the confounder
+ * followed by the 4 byte crc32 checksum
+ */
+ if (buffer->length < 20) {
+ return data_blob_const(NULL, 0);
+ }
+ confounder = data_blob_const(buffer->data, 16);
+ enc_buffer = data_blob_const(buffer->data + 16, buffer->length - 16);
+
+ /*
+ * build the encryption key md5 over the session key followed
+ * by the confounder
+ *
+ * here the gensec session key is used and
+ * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
+ */
+ enc_key = data_blob_const(_enc_key, sizeof(_enc_key));
+ MD5Init(&md5);
+ MD5Update(&md5, gensec_skey->data, gensec_skey->length);
+ MD5Update(&md5, confounder.data, confounder.length);
+ MD5Final(enc_key.data, &md5);
+
+ /*
+ * copy the encrypted buffer part and
+ * decrypt it using the created encryption key using arcfour
+ */
+ dec_buffer = data_blob_talloc(mem_ctx, enc_buffer.data, enc_buffer.length);
+ if (!dec_buffer.data) {
+ return data_blob_const(NULL, 0);
+ }
+ arcfour_crypt_blob(dec_buffer.data, dec_buffer.length, &enc_key);
+
+ /*
+ * the first 4 byte are the crc32 checksum
+ * of the remaining bytes
+ */
+ crc32_given = IVAL(dec_buffer.data, 0);
+ crc32_calc = crc32_calc_buffer(dec_buffer.data + 4 , dec_buffer.length - 4);
+ if (crc32_given != crc32_calc) {
+ DEBUG(0,("CRC32: given[0x%08X] calc[0x%08X]\n",
+ crc32_given, crc32_calc));
+ return data_blob_const(NULL, 0);
+ }
+ checked_buffer = data_blob_talloc(mem_ctx, dec_buffer.data + 4, dec_buffer.length - 4);
+ if (!checked_buffer.data) {
+ return data_blob_const(NULL, 0);
+ }
+
+ /*
+ * some attributes seem to be in a usable form after this decryption
+ * (supplementalCredentials, priorValue, currentValue, trustAuthOutgoing,
+ * trustAuthIncoming, initialAuthOutgoing, initialAuthIncoming)
+ * At least supplementalCredentials contains plaintext
+ * like "Primary:Kerberos" (in unicode form)
+ *
+ * some attributes seem to have some additional encryption
+ * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
+ *
+ * it's the sam_rid_crypt() function, as the value is constant,
+ * so it doesn't depend on sessionkeys.
+ */
+ if (rcrypt) {
+ uint32_t i, num_hashes;
+
+ if ((checked_buffer.length % 16) != 0) {
+ return data_blob_const(NULL, 0);
+ }
+
+ plain_buffer = data_blob_talloc(mem_ctx, checked_buffer.data, checked_buffer.length);
+ if (!plain_buffer.data) {
+ return data_blob_const(NULL, 0);
+ }
+
+ num_hashes = plain_buffer.length / 16;
+ for (i = 0; i < num_hashes; i++) {
+ uint32_t offset = i * 16;
+ sam_rid_crypt(rid, checked_buffer.data + offset, plain_buffer.data + offset, 0);
+ }
+ } else {
+ plain_buffer = checked_buffer;
+ }
+
+ return plain_buffer;
+}
+
+static void test_analyse_objects(struct torture_context *tctx,
+ struct DsSyncTest *ctx,
+ const DATA_BLOB *gensec_skey,
+ struct drsuapi_DsReplicaObjectListItemEx *cur)
+{
+ static uint32_t object_id;
+ const char *save_values_dir;
+
+ if (!lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "print_pwd_blobs", false)) {
+ return;
+ }
+
+ save_values_dir = lp_parm_string(tctx->lp_ctx, NULL, "dssync", "save_pwd_blobs_dir");
+
+ for (; cur; cur = cur->next_object) {
+ const char *dn;
+ struct dom_sid *sid = NULL;
+ uint32_t rid = 0;
+ bool dn_printed = false;
+ uint32_t i;
+
+ if (!cur->object.identifier) continue;
+
+ dn = cur->object.identifier->dn;
+ if (cur->object.identifier->sid.num_auths > 0) {
+ sid = &cur->object.identifier->sid;
+ rid = sid->sub_auths[sid->num_auths - 1];
+ }
+
+ for (i=0; i < cur->object.attribute_ctr.num_attributes; i++) {
+ const char *name = NULL;
+ bool rcrypt = false;
+ DATA_BLOB *enc_data = NULL;
+ DATA_BLOB plain_data;
+ struct drsuapi_DsReplicaAttribute *attr;
+ ndr_pull_flags_fn_t pull_fn = NULL;
+ ndr_print_fn_t print_fn = NULL;
+ void *ptr = NULL;
+ attr = &cur->object.attribute_ctr.attributes[i];
+
+ switch (attr->attid) {
+ case DRSUAPI_ATTRIBUTE_dBCSPwd:
+ name = "dBCSPwd";
+ rcrypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_unicodePwd:
+ name = "unicodePwd";
+ rcrypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_ntPwdHistory:
+ name = "ntPwdHistory";
+ rcrypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_lmPwdHistory:
+ name = "lmPwdHistory";
+ rcrypt = true;
+ break;
+ case DRSUAPI_ATTRIBUTE_supplementalCredentials:
+ name = "supplementalCredentials";
+ pull_fn = (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob;
+ print_fn = (ndr_print_fn_t)ndr_print_supplementalCredentialsBlob;
+ ptr = talloc(ctx, struct supplementalCredentialsBlob);
+ break;
+ case DRSUAPI_ATTRIBUTE_priorValue:
+ name = "priorValue";
+ break;
+ case DRSUAPI_ATTRIBUTE_currentValue:
+ name = "currentValue";
+ break;
+ case DRSUAPI_ATTRIBUTE_trustAuthOutgoing:
+ name = "trustAuthOutgoing";
+ pull_fn = (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob;
+ print_fn = (ndr_print_fn_t)ndr_print_trustAuthInOutBlob;
+ ptr = talloc(ctx, struct trustAuthInOutBlob);
+ break;
+ case DRSUAPI_ATTRIBUTE_trustAuthIncoming:
+ name = "trustAuthIncoming";
+ pull_fn = (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob;
+ print_fn = (ndr_print_fn_t)ndr_print_trustAuthInOutBlob;
+ ptr = talloc(ctx, struct trustAuthInOutBlob);
+ break;
+ case DRSUAPI_ATTRIBUTE_initialAuthOutgoing:
+ name = "initialAuthOutgoing";
+ break;
+ case DRSUAPI_ATTRIBUTE_initialAuthIncoming:
+ name = "initialAuthIncoming";
+ break;
+ default:
+ continue;
+ }
+
+ if (attr->value_ctr.num_values != 1) continue;
+
+ if (!attr->value_ctr.values[0].blob) continue;
+
+ enc_data = attr->value_ctr.values[0].blob;
+ ZERO_STRUCT(plain_data);
+
+ plain_data = decrypt_blob(ctx, gensec_skey, rcrypt,
+ cur->object.identifier, rid,
+ enc_data);
+ if (!dn_printed) {
+ object_id++;
+ DEBUG(0,("DN[%u] %s\n", object_id, dn));
+ dn_printed = true;
+ }
+ DEBUGADD(0,("ATTR: %s enc.length=%lu plain.length=%lu\n",
+ name, (long)enc_data->length, (long)plain_data.length));
+ if (plain_data.length) {
+ enum ndr_err_code ndr_err;
+ dump_data(0, plain_data.data, plain_data.length);
+ if (save_values_dir) {
+ char *fname;
+ fname = talloc_asprintf(ctx, "%s/%s%02d",
+ save_values_dir,
+ name, object_id);
+ if (fname) {
+ bool ok;
+ ok = file_save(fname, plain_data.data, plain_data.length);
+ if (!ok) {
+ DEBUGADD(0,("Failed to save '%s'\n", fname));
+ }
+ }
+ talloc_free(fname);
+ }
+
+ if (pull_fn) {
+ /* Can't use '_all' because of PIDL bugs with relative pointers */
+ ndr_err = ndr_pull_struct_blob(&plain_data, ptr,
+ lp_iconv_convenience(tctx->lp_ctx), ptr,
+ pull_fn);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ ndr_print_debug(print_fn, name, ptr);
+ } else {
+ DEBUG(0, ("Failed to decode %s\n", name));
+ }
+ }
+ } else {
+ dump_data(0, enc_data->data, enc_data->length);
+ }
+ talloc_free(ptr);
+ }
+ }
+}
+
+static bool test_FetchData(struct torture_context *tctx, struct DsSyncTest *ctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ int i, y = 0;
+ uint64_t highest_usn = 0;
+ const char *partition = NULL;
+ struct drsuapi_DsGetNCChanges r;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct drsuapi_DsGetNCChangesCtr1 *ctr1 = NULL;
+ struct drsuapi_DsGetNCChangesCtr6 *ctr6 = NULL;
+ int32_t out_level = 0;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ DATA_BLOB gensec_skey;
+ struct {
+ int32_t level;
+ } array[] = {
+/* {
+ 5
+ },
+*/ {
+ 8
+ }
+ };
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+
+ partition = lp_parm_string(tctx->lp_ctx, NULL, "dssync", "partition");
+ if (partition == NULL) {
+ partition = ctx->domain_dn;
+ printf("dssync:partition not specified, defaulting to %s.\n", ctx->domain_dn);
+ }
+
+ highest_usn = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "highest_usn", 0);
+
+ array[0].level = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "get_nc_changes_level", array[0].level);
+
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "print_pwd_blobs", false)) {
+ const struct samr_Password *nthash;
+ nthash = cli_credentials_get_nt_hash(ctx->new_dc.credentials, ctx);
+ if (nthash) {
+ dump_data_pw("CREDENTIALS nthash:", nthash->hash, sizeof(nthash->hash));
+ }
+ }
+ status = gensec_session_key(ctx->new_dc.drsuapi.pipe->conn->security_state.generic_state,
+ &gensec_skey);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to get gensec session key: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ printf("testing DsGetNCChanges level %d\n",
+ array[i].level);
+
+ r.in.bind_handle = &ctx->new_dc.drsuapi.bind_handle;
+ r.in.level = &array[i].level;
+
+ switch (*r.in.level) {
+ case 5:
+ nc.guid = null_guid;
+ nc.sid = null_sid;
+ nc.dn = partition;
+
+ r.in.req.req5.destination_dsa_guid = ctx->new_dc.invocation_id;
+ r.in.req.req5.source_dsa_invocation_id = null_guid;
+ r.in.req.req5.naming_context = &nc;
+ r.in.req.req5.highwatermark.tmp_highest_usn = highest_usn;
+ r.in.req.req5.highwatermark.reserved_usn = 0;
+ r.in.req.req5.highwatermark.highest_usn = highest_usn;
+ r.in.req.req5.uptodateness_vector = NULL;
+ r.in.req.req5.replica_flags = 0;
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "compression", false)) {
+ r.in.req.req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+ }
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "neighbour_writeable", true)) {
+ r.in.req.req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE;
+ }
+ r.in.req.req5.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
+ ;
+ r.in.req.req5.max_object_count = 133;
+ r.in.req.req5.max_ndr_size = 1336770;
+ r.in.req.req5.extended_op = DRSUAPI_EXOP_NONE;
+ r.in.req.req5.fsmo_info = 0;
+
+ break;
+ case 8:
+ nc.guid = null_guid;
+ nc.sid = null_sid;
+ nc.dn = partition;
+ /* nc.dn can be set to any other ad partition */
+
+ r.in.req.req8.destination_dsa_guid = ctx->new_dc.invocation_id;
+ r.in.req.req8.source_dsa_invocation_id = null_guid;
+ r.in.req.req8.naming_context = &nc;
+ r.in.req.req8.highwatermark.tmp_highest_usn = highest_usn;
+ r.in.req.req8.highwatermark.reserved_usn = 0;
+ r.in.req.req8.highwatermark.highest_usn = highest_usn;
+ r.in.req.req8.uptodateness_vector = NULL;
+ r.in.req.req8.replica_flags = 0;
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "compression", false)) {
+ r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_COMPRESS_CHANGES;
+ }
+ if (lp_parm_bool(tctx->lp_ctx, NULL, "dssync", "neighbour_writeable", true)) {
+ r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_WRITEABLE;
+ }
+ r.in.req.req8.replica_flags |= DRSUAPI_DS_REPLICA_NEIGHBOUR_SYNC_ON_STARTUP
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_DO_SCHEDULED_SYNCS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_RETURN_OBJECT_PARENTS
+ | DRSUAPI_DS_REPLICA_NEIGHBOUR_NEVER_SYNCED
+ ;
+ r.in.req.req8.max_object_count = 402;
+ r.in.req.req8.max_ndr_size = 402116;
+
+ r.in.req.req8.extended_op = DRSUAPI_EXOP_NONE;
+ r.in.req.req8.fsmo_info = 0;
+ r.in.req.req8.partial_attribute_set = NULL;
+ r.in.req.req8.partial_attribute_set_ex = NULL;
+ r.in.req.req8.mapping_ctr.num_mappings = 0;
+ r.in.req.req8.mapping_ctr.mappings = NULL;
+
+ break;
+ }
+
+ printf("Dumping AD partition: %s\n", nc.dn);
+ for (y=0; ;y++) {
+ int32_t _level = 0;
+ ZERO_STRUCT(r.out);
+ r.out.level = &_level;
+
+ if (*r.in.level == 5) {
+ DEBUG(0,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)r.in.req.req5.highwatermark.tmp_highest_usn,
+ (long long)r.in.req.req5.highwatermark.highest_usn));
+ }
+
+ if (*r.in.level == 8) {
+ DEBUG(0,("start[%d] tmp_higest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)r.in.req.req8.highwatermark.tmp_highest_usn,
+ (long long)r.in.req.req8.highwatermark.highest_usn));
+ }
+
+ status = dcerpc_drsuapi_DsGetNCChanges(ctx->new_dc.drsuapi.pipe, ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(ctx, ctx->new_dc.drsuapi.pipe->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsGetNCChanges failed - %s\n", errstr);
+ ret = false;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsGetNCChanges failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (ret == true && *r.out.level == 1) {
+ out_level = 1;
+ ctr1 = &r.out.ctr.ctr1;
+ } else if (ret == true && *r.out.level == 2 &&
+ r.out.ctr.ctr2.mszip1.ts) {
+ out_level = 1;
+ ctr1 = &r.out.ctr.ctr2.mszip1.ts->ctr1;
+ }
+
+ if (out_level == 1) {
+ DEBUG(0,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)ctr1->new_highwatermark.tmp_highest_usn,
+ (long long)ctr1->new_highwatermark.highest_usn));
+
+ test_analyse_objects(tctx, ctx, &gensec_skey, ctr1->first_object);
+
+ if (ctr1->more_data) {
+ r.in.req.req5.highwatermark = ctr1->new_highwatermark;
+ continue;
+ }
+ }
+
+ if (ret == true && *r.out.level == 6) {
+ out_level = 6;
+ ctr6 = &r.out.ctr.ctr6;
+ } else if (ret == true && *r.out.level == 7
+ && r.out.ctr.ctr7.level == 6
+ && r.out.ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_MSZIP
+ && r.out.ctr.ctr7.ctr.mszip6.ts) {
+ out_level = 6;
+ ctr6 = &r.out.ctr.ctr7.ctr.mszip6.ts->ctr6;
+ } else if (ret == true && *r.out.level == 7
+ && r.out.ctr.ctr7.level == 6
+ && r.out.ctr.ctr7.type == DRSUAPI_COMPRESSION_TYPE_XPRESS
+ && r.out.ctr.ctr7.ctr.xpress6.ts) {
+ out_level = 6;
+ ctr6 = &r.out.ctr.ctr7.ctr.xpress6.ts->ctr6;
+ }
+
+ if (out_level == 6) {
+ DEBUG(0,("end[%d] tmp_highest_usn: %llu , highest_usn: %llu\n",y,
+ (long long)ctr6->new_highwatermark.tmp_highest_usn,
+ (long long)ctr6->new_highwatermark.highest_usn));
+
+ test_analyse_objects(tctx, ctx, &gensec_skey, ctr6->first_object);
+
+ if (ctr6->more_data) {
+ r.in.req.req8.highwatermark = ctr6->new_highwatermark;
+ continue;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_FetchNT4Data(struct torture_context *tctx,
+ struct DsSyncTest *ctx)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct drsuapi_DsGetNT4ChangeLog r;
+ struct GUID null_guid;
+ struct dom_sid null_sid;
+ DATA_BLOB cookie;
+
+ ZERO_STRUCT(null_guid);
+ ZERO_STRUCT(null_sid);
+ ZERO_STRUCT(cookie);
+
+ ZERO_STRUCT(r);
+ r.in.bind_handle = &ctx->new_dc.drsuapi.bind_handle;
+ r.in.level = 1;
+
+ r.in.req.req1.unknown1 = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "nt4-1", 3);
+ r.in.req.req1.unknown2 = lp_parm_int(tctx->lp_ctx, NULL, "dssync", "nt4-2", 0x00004000);
+
+ while (1) {
+ r.in.req.req1.length = cookie.length;
+ r.in.req.req1.data = cookie.data;
+
+ status = dcerpc_drsuapi_DsGetNT4ChangeLog(ctx->new_dc.drsuapi.pipe, ctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ printf("DsGetNT4ChangeLog not supported by target server\n");
+ break;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(ctx, ctx->new_dc.drsuapi.pipe->last_fault_code);
+ }
+ printf("dcerpc_drsuapi_DsGetNT4ChangeLog failed - %s\n", errstr);
+ ret = false;
+ } else if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_DOMAIN_ROLE)) {
+ printf("DsGetNT4ChangeLog not supported by target server\n");
+ break;
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("DsGetNT4ChangeLog failed - %s\n", win_errstr(r.out.result));
+ ret = false;
+ } else if (r.out.level != 1) {
+ printf("DsGetNT4ChangeLog unknown level - %u\n", r.out.level);
+ ret = false;
+ } else if (NT_STATUS_IS_OK(r.out.info.info1.status)) {
+ } else if (NT_STATUS_EQUAL(r.out.info.info1.status, STATUS_MORE_ENTRIES)) {
+ cookie.length = r.out.info.info1.length1;
+ cookie.data = r.out.info.info1.data1;
+ continue;
+ } else {
+ printf("DsGetNT4ChangeLog failed - %s\n", nt_errstr(r.out.info.info1.status));
+ ret = false;
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
+bool torture_rpc_dssync(struct torture_context *torture)
+{
+ bool ret = true;
+ TALLOC_CTX *mem_ctx;
+ struct DsSyncTest *ctx;
+
+ mem_ctx = talloc_init("torture_rpc_dssync");
+ ctx = test_create_context(torture);
+
+ ret &= _test_DsBind(torture, ctx, ctx->admin.credentials, &ctx->admin.drsuapi);
+ if (!ret) {
+ return ret;
+ }
+ ret &= test_LDAPBind(torture, ctx, ctx->admin.credentials, &ctx->admin.ldap);
+ if (!ret) {
+ return ret;
+ }
+ ret &= test_GetInfo(torture, ctx);
+ ret &= _test_DsBind(torture, ctx, ctx->new_dc.credentials, &ctx->new_dc.drsuapi);
+ if (!ret) {
+ return ret;
+ }
+ ret &= test_FetchData(torture, ctx);
+ ret &= test_FetchNT4Data(torture, ctx);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/echo.c b/source4/torture/rpc/echo.c
new file mode 100644
index 0000000000..5c027d3299
--- /dev/null
+++ b/source4/torture/rpc/echo.c
@@ -0,0 +1,452 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for echo rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2005
+ Copyright (C) Jelmer Vernooij 2005
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "lib/events/events.h"
+#include "librpc/gen_ndr/ndr_echo_c.h"
+
+
+/*
+ test the AddOne interface
+*/
+#define TEST_ADDONE(tctx, value) do { \
+ n = i = value; \
+ r.in.in_data = n; \
+ r.out.out_data = &n; \
+ status = dcerpc_echo_AddOne(p, tctx, &r); \
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx, "AddOne(%d) failed", i)); \
+ torture_assert (tctx, n == i+1, talloc_asprintf(tctx, "%d + 1 != %u (should be %u)\n", i, n, i+1)); \
+ torture_comment (tctx, "%d + 1 = %u\n", i, n); \
+} while(0)
+
+static bool test_addone(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ uint32_t i;
+ NTSTATUS status;
+ uint32_t n;
+ struct echo_AddOne r;
+
+ for (i=0;i<10;i++) {
+ TEST_ADDONE(tctx, i);
+ }
+
+ TEST_ADDONE(tctx, 0x7FFFFFFE);
+ TEST_ADDONE(tctx, 0xFFFFFFFE);
+ TEST_ADDONE(tctx, 0xFFFFFFFF);
+ TEST_ADDONE(tctx, random() & 0xFFFFFFFF);
+ return true;
+}
+
+/*
+ test the EchoData interface
+*/
+static bool test_echodata(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+ NTSTATUS status;
+ uint8_t *data_in, *data_out;
+ int len;
+ struct echo_EchoData r;
+
+ if (torture_setting_bool(tctx, "quick", false) &&
+ (p->conn->flags & DCERPC_DEBUG_VALIDATE_BOTH)) {
+ len = 1 + (random() % 500);
+ } else {
+ len = 1 + (random() % 5000);
+ }
+
+ data_in = talloc_array(tctx, uint8_t, len);
+ data_out = talloc_array(tctx, uint8_t, len);
+ for (i=0;i<len;i++) {
+ data_in[i] = i;
+ }
+
+ r.in.len = len;
+ r.in.in_data = data_in;
+
+ status = dcerpc_echo_EchoData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx,
+ "EchoData(%d) failed\n", len));
+
+ data_out = r.out.out_data;
+
+ for (i=0;i<len;i++) {
+ if (data_in[i] != data_out[i]) {
+ torture_comment(tctx, "Bad data returned for len %d at offset %d\n",
+ len, i);
+ torture_comment(tctx, "in:\n");
+ dump_data(0, data_in+i, MIN(len-i, 16));
+ torture_comment(tctx, "out:\n");
+ dump_data(0, data_out+i, MIN(len-1, 16));
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ test the SourceData interface
+*/
+static bool test_sourcedata(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+ NTSTATUS status;
+ int len;
+ struct echo_SourceData r;
+
+ if (torture_setting_bool(tctx, "quick", false) &&
+ (p->conn->flags & DCERPC_DEBUG_VALIDATE_BOTH)) {
+ len = 100 + (random() % 500);
+ } else {
+ len = 200000 + (random() % 5000);
+ }
+
+ r.in.len = len;
+
+ status = dcerpc_echo_SourceData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx,
+ "SourceData(%d) failed", len));
+
+ for (i=0;i<len;i++) {
+ uint8_t *v = (uint8_t *)r.out.data;
+ torture_assert(tctx, v[i] == (i & 0xFF),
+ talloc_asprintf(tctx,
+ "bad data 0x%x at %d\n", (uint8_t)r.out.data[i], i));
+ }
+ return true;
+}
+
+/*
+ test the SinkData interface
+*/
+static bool test_sinkdata(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+ NTSTATUS status;
+ uint8_t *data_in;
+ int len;
+ struct echo_SinkData r;
+
+ if (torture_setting_bool(tctx, "quick", false) &&
+ (p->conn->flags & DCERPC_DEBUG_VALIDATE_BOTH)) {
+ len = 100 + (random() % 5000);
+ } else {
+ len = 200000 + (random() % 5000);
+ }
+
+ data_in = talloc_array(tctx, uint8_t, len);
+ for (i=0;i<len;i++) {
+ data_in[i] = i+1;
+ }
+
+ r.in.len = len;
+ r.in.data = data_in;
+
+ status = dcerpc_echo_SinkData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx,
+ "SinkData(%d) failed",
+ len));
+
+ torture_comment(tctx, "sunk %d bytes\n", len);
+ return true;
+}
+
+
+/*
+ test the testcall interface
+*/
+static bool test_testcall(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct echo_TestCall r;
+ const char *s = NULL;
+
+ r.in.s1 = "input string";
+ r.out.s2 = &s;
+
+ status = dcerpc_echo_TestCall(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "TestCall failed");
+
+ torture_assert_str_equal(tctx, s, "input string", "Didn't receive back same string");
+
+ return true;
+}
+
+/*
+ test the testcall interface
+*/
+static bool test_testcall2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct echo_TestCall2 r;
+ int i;
+
+ for (i=1;i<=7;i++) {
+ r.in.level = i;
+ r.out.info = talloc(tctx, union echo_Info);
+
+ torture_comment(tctx, "Testing TestCall2 level %d\n", i);
+ status = dcerpc_echo_TestCall2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "TestCall2 failed");
+ }
+ return true;
+}
+
+/*
+ test the TestSleep interface
+*/
+static bool test_sleep(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+ NTSTATUS status;
+#define ASYNC_COUNT 3
+ struct rpc_request *req[ASYNC_COUNT];
+ struct echo_TestSleep r[ASYNC_COUNT];
+ bool done[ASYNC_COUNT];
+ struct timeval snd[ASYNC_COUNT];
+ struct timeval rcv[ASYNC_COUNT];
+ struct timeval diff[ASYNC_COUNT];
+ struct event_context *ctx;
+ int total_done = 0;
+
+ if (torture_setting_bool(tctx, "quick", false)) {
+ torture_skip(tctx, "TestSleep disabled - use \"torture:quick=no\" to enable\n");
+ }
+ torture_comment(tctx, "Testing TestSleep - use \"torture:quick=yes\" to disable\n");
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ done[i] = false;
+ snd[i] = timeval_current();
+ rcv[i] = timeval_zero();
+ r[i].in.seconds = ASYNC_COUNT-i;
+ req[i] = dcerpc_echo_TestSleep_send(p, tctx, &r[i]);
+ torture_assert(tctx, req[i], "Failed to send async sleep request\n");
+ }
+
+ ctx = dcerpc_event_context(p);
+ while (total_done < ASYNC_COUNT) {
+ torture_assert(tctx, event_loop_once(ctx) == 0,
+ "Event context loop failed");
+ for (i=0;i<ASYNC_COUNT;i++) {
+ if (done[i] == false && req[i]->state == RPC_REQUEST_DONE) {
+ int rounded_tdiff;
+ total_done++;
+ done[i] = true;
+ rcv[i] = timeval_current();
+ diff[i] = timeval_until(&snd[i], &rcv[i]);
+ rounded_tdiff = (int)(0.5 + diff[i].tv_sec + (1.0e-6*diff[i].tv_usec));
+ status = dcerpc_ndr_request_recv(req[i]);
+ torture_comment(tctx, "rounded_tdiff=%d\n", rounded_tdiff);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "TestSleep(%d) failed", i));
+ torture_assert(tctx, r[i].out.result == r[i].in.seconds,
+ talloc_asprintf(tctx, "Failed - Asked to sleep for %u seconds (server replied with %u seconds and the reply takes only %u seconds)",
+ r[i].out.result, r[i].in.seconds, (uint_t)diff[i].tv_sec));
+ torture_assert(tctx, r[i].out.result <= rounded_tdiff,
+ talloc_asprintf(tctx, "Failed - Slept for %u seconds (but reply takes only %u.%06u seconds)",
+ r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec));
+ if (r[i].out.result+1 == rounded_tdiff) {
+ torture_comment(tctx, "Slept for %u seconds (but reply takes %u.%06u seconds - busy server?)\n",
+ r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec);
+ } else if (r[i].out.result == rounded_tdiff) {
+ torture_comment(tctx, "Slept for %u seconds (reply takes %u.%06u seconds - ok)\n",
+ r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec);
+ } else {
+ torture_comment(tctx, "(Failed) - Not async - Slept for %u seconds (but reply takes %u.%06u seconds)",
+ r[i].out.result, (uint_t)diff[i].tv_sec, (uint_t)diff[i].tv_usec);
+ /* TODO: let the test fail here, when we support async rpc on ncacn_np */
+ }
+ }
+ }
+ }
+ torture_comment(tctx, "\n");
+ return true;
+}
+
+/*
+ test enum handling
+*/
+static bool test_enum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct echo_TestEnum r;
+ enum echo_Enum1 v = ECHO_ENUM1;
+ struct echo_Enum2 e2;
+ union echo_Enum3 e3;
+
+ r.in.foo1 = &v;
+ r.in.foo2 = &e2;
+ r.in.foo3 = &e3;
+ r.out.foo1 = &v;
+ r.out.foo2 = &e2;
+ r.out.foo3 = &e3;
+
+ e2.e1 = 76;
+ e2.e2 = ECHO_ENUM1_32;
+ e3.e1 = ECHO_ENUM2;
+
+ status = dcerpc_echo_TestEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "TestEnum failed");
+ return true;
+}
+
+/*
+ test surrounding conformant array handling
+*/
+static bool test_surrounding(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct echo_TestSurrounding r;
+
+ ZERO_STRUCT(r);
+ r.in.data = talloc(tctx, struct echo_Surrounding);
+
+ r.in.data->x = 20;
+ r.in.data->surrounding = talloc_zero_array(tctx, uint16_t, r.in.data->x);
+
+ r.out.data = talloc(tctx, struct echo_Surrounding);
+
+ status = dcerpc_echo_TestSurrounding(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "TestSurrounding failed");
+
+ torture_assert(tctx, r.out.data->x == 2 * r.in.data->x,
+ "TestSurrounding did not make the array twice as large");
+
+ return true;
+}
+
+/*
+ test multiple levels of pointers
+*/
+static bool test_doublepointer(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct echo_TestDoublePointer r;
+ uint16_t value = 12;
+ uint16_t *pvalue = &value;
+ uint16_t **ppvalue = &pvalue;
+
+ ZERO_STRUCT(r);
+ r.in.data = &ppvalue;
+
+ status = dcerpc_echo_TestDoublePointer(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "TestDoublePointer failed");
+
+ torture_assert_int_equal(tctx, value, r.out.result,
+ "TestDoublePointer did not return original value");
+ return true;
+}
+
+
+/*
+ test request timeouts
+*/
+static bool test_timeout(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct rpc_request *req;
+ struct echo_TestSleep r;
+ int timeout_saved = p->request_timeout;
+
+ if (torture_setting_bool(tctx, "quick", false)) {
+ torture_skip(tctx, "timeout testing disabled - use \"torture:quick=no\" to enable\n");
+ }
+
+ torture_comment(tctx, "testing request timeouts\n");
+ r.in.seconds = 2;
+ p->request_timeout = 1;
+
+ req = dcerpc_echo_TestSleep_send(p, tctx, &r);
+ if (!req) {
+ torture_comment(tctx, "Failed to send async sleep request\n");
+ goto failed;
+ }
+ req->ignore_timeout = true;
+
+ status = dcerpc_ndr_request_recv(req);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_IO_TIMEOUT,
+ "request should have timed out");
+
+ torture_comment(tctx, "testing request destruction\n");
+ req = dcerpc_echo_TestSleep_send(p, tctx, &r);
+ if (!req) {
+ torture_comment(tctx, "Failed to send async sleep request\n");
+ goto failed;
+ }
+ talloc_free(req);
+
+ req = dcerpc_echo_TestSleep_send(p, tctx, &r);
+ if (!req) {
+ torture_comment(tctx, "Failed to send async sleep request\n");
+ goto failed;
+ }
+ req->ignore_timeout = true;
+ status = dcerpc_ndr_request_recv(req);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_IO_TIMEOUT,
+ "request should have timed out");
+
+ p->request_timeout = timeout_saved;
+
+ return test_addone(tctx, p);
+
+failed:
+ p->request_timeout = timeout_saved;
+ return false;
+}
+
+
+struct torture_suite *torture_rpc_echo(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(
+ mem_ctx, "ECHO");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "echo",
+ &ndr_table_rpcecho);
+
+ torture_rpc_tcase_add_test(tcase, "addone", test_addone);
+ torture_rpc_tcase_add_test(tcase, "sinkdata", test_sinkdata);
+ torture_rpc_tcase_add_test(tcase, "echodata", test_echodata);
+ torture_rpc_tcase_add_test(tcase, "sourcedata", test_sourcedata);
+ torture_rpc_tcase_add_test(tcase, "testcall", test_testcall);
+ torture_rpc_tcase_add_test(tcase, "testcall2", test_testcall2);
+ torture_rpc_tcase_add_test(tcase, "enum", test_enum);
+ torture_rpc_tcase_add_test(tcase, "surrounding", test_surrounding);
+ torture_rpc_tcase_add_test(tcase, "doublepointer", test_doublepointer);
+ torture_rpc_tcase_add_test(tcase, "sleep", test_sleep);
+ torture_rpc_tcase_add_test(tcase, "timeout", test_timeout);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/epmapper.c b/source4/torture/rpc/epmapper.c
new file mode 100644
index 0000000000..708d46a5dc
--- /dev/null
+++ b/source4/torture/rpc/epmapper.c
@@ -0,0 +1,277 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for epmapper rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "librpc/ndr/ndr_table.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "torture/rpc/rpc.h"
+
+
+/*
+ display any protocol tower
+ */
+static void display_tower(TALLOC_CTX *mem_ctx, struct epm_tower *twr)
+{
+ int i;
+
+ for (i=0;i<twr->num_floors;i++) {
+ printf(" %s", epm_floor_string(mem_ctx, &twr->floors[i]));
+ }
+ printf("\n");
+}
+
+
+static bool test_Map(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct epm_twr_t *twr)
+{
+ NTSTATUS status;
+ struct epm_Map r;
+ struct GUID uuid;
+ struct policy_handle handle;
+ int i;
+ struct ndr_syntax_id syntax;
+ uint32_t num_towers;
+
+ ZERO_STRUCT(uuid);
+ ZERO_STRUCT(handle);
+
+ r.in.object = &uuid;
+ r.in.map_tower = twr;
+ r.in.entry_handle = &handle;
+ r.out.entry_handle = &handle;
+ r.in.max_towers = 100;
+ r.out.num_towers = &num_towers;
+
+ dcerpc_floor_get_lhs_data(&twr->tower.floors[0], &syntax);
+
+ printf("epm_Map results for '%s':\n",
+ ndr_interface_name(&syntax.uuid, syntax.if_version));
+
+ twr->tower.floors[2].lhs.protocol = EPM_PROTOCOL_NCACN;
+ twr->tower.floors[2].lhs.lhs_data = data_blob(NULL, 0);
+ twr->tower.floors[2].rhs.ncacn.minor_version = 0;
+
+ twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_TCP;
+ twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0);
+ twr->tower.floors[3].rhs.tcp.port = 0;
+
+ twr->tower.floors[4].lhs.protocol = EPM_PROTOCOL_IP;
+ twr->tower.floors[4].lhs.lhs_data = data_blob(NULL, 0);
+ twr->tower.floors[4].rhs.ip.ipaddr = "0.0.0.0";
+
+ status = dcerpc_epm_Map(p, mem_ctx, &r);
+ if (NT_STATUS_IS_OK(status) && r.out.result == 0) {
+ for (i=0;i<*r.out.num_towers;i++) {
+ if (r.out.towers[i].twr) {
+ display_tower(mem_ctx, &r.out.towers[i].twr->tower);
+ }
+ }
+ }
+
+ twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_HTTP;
+ twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0);
+ twr->tower.floors[3].rhs.http.port = 0;
+
+ status = dcerpc_epm_Map(p, mem_ctx, &r);
+ if (NT_STATUS_IS_OK(status) && r.out.result == 0) {
+ for (i=0;i<*r.out.num_towers;i++) {
+ if (r.out.towers[i].twr) {
+ display_tower(mem_ctx, &r.out.towers[i].twr->tower);
+ }
+ }
+ }
+
+ twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_UDP;
+ twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0);
+ twr->tower.floors[3].rhs.http.port = 0;
+
+ status = dcerpc_epm_Map(p, mem_ctx, &r);
+ if (NT_STATUS_IS_OK(status) && r.out.result == 0) {
+ for (i=0;i<*r.out.num_towers;i++) {
+ if (r.out.towers[i].twr) {
+ display_tower(mem_ctx, &r.out.towers[i].twr->tower);
+ }
+ }
+ }
+
+ twr->tower.floors[3].lhs.protocol = EPM_PROTOCOL_SMB;
+ twr->tower.floors[3].lhs.lhs_data = data_blob(NULL, 0);
+ twr->tower.floors[3].rhs.smb.unc = "";
+
+ twr->tower.floors[4].lhs.protocol = EPM_PROTOCOL_NETBIOS;
+ twr->tower.floors[4].lhs.lhs_data = data_blob(NULL, 0);
+ twr->tower.floors[4].rhs.netbios.name = "";
+
+ status = dcerpc_epm_Map(p, mem_ctx, &r);
+ if (NT_STATUS_IS_OK(status) && r.out.result == 0) {
+ for (i=0;i<*r.out.num_towers;i++) {
+ if (r.out.towers[i].twr) {
+ display_tower(mem_ctx, &r.out.towers[i].twr->tower);
+ }
+ }
+ }
+
+ /* FIXME: Extend to do other protocols as well (ncacn_unix_stream, ncalrpc) */
+
+ return true;
+}
+
+static bool test_Lookup(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct epm_Lookup r;
+ struct GUID uuid;
+ struct rpc_if_id_t iface;
+ struct policy_handle handle;
+ uint32_t num_ents;
+
+ ZERO_STRUCT(handle);
+
+ r.in.inquiry_type = 0;
+ r.in.object = &uuid;
+ r.in.interface_id = &iface;
+ r.in.vers_option = 0;
+ r.in.entry_handle = &handle;
+ r.out.entry_handle = &handle;
+ r.in.max_ents = 10;
+ r.out.num_ents = &num_ents;
+
+ do {
+ int i;
+
+ ZERO_STRUCT(uuid);
+ ZERO_STRUCT(iface);
+
+ status = dcerpc_epm_Lookup(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status) || r.out.result != 0) {
+ break;
+ }
+
+ printf("epm_Lookup returned %d events GUID %s\n",
+ *r.out.num_ents, GUID_string(tctx, &handle.uuid));
+
+ for (i=0;i<*r.out.num_ents;i++) {
+ printf("\nFound '%s'\n", r.out.entries[i].annotation);
+ display_tower(tctx, &r.out.entries[i].tower->tower);
+ if (r.out.entries[i].tower->tower.num_floors == 5) {
+ test_Map(p, tctx, r.out.entries[i].tower);
+ }
+ }
+ } while (NT_STATUS_IS_OK(status) &&
+ r.out.result == 0 &&
+ *r.out.num_ents == r.in.max_ents &&
+ !policy_handle_empty(&handle));
+
+ torture_assert_ntstatus_ok(tctx, status, "Lookup failed");
+
+ return true;
+}
+
+static bool test_Delete(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct epm_entry_t *entries)
+{
+ NTSTATUS status;
+ struct epm_Delete r;
+
+ r.in.num_ents = 1;
+ r.in.entries = entries;
+
+ status = dcerpc_epm_Delete(p, mem_ctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ printf("Delete failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (r.out.result != 0) {
+ printf("Delete failed - %d\n", r.out.result);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_Insert(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct epm_Insert r;
+ struct dcerpc_binding *bd;
+
+ r.in.num_ents = 1;
+
+ r.in.entries = talloc_array(tctx, struct epm_entry_t, 1);
+ ZERO_STRUCT(r.in.entries[0].object);
+ r.in.entries[0].annotation = "smbtorture endpoint";
+ status = dcerpc_parse_binding(tctx, "ncalrpc:[SMBTORTURE]", &bd);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Unable to generate dcerpc_binding struct");
+
+ r.in.entries[0].tower = talloc(tctx, struct epm_twr_t);
+
+ status = dcerpc_binding_build_tower(tctx, bd, &r.in.entries[0].tower->tower);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Unable to build tower from binding struct");
+
+ r.in.replace = 0;
+
+ status = dcerpc_epm_Insert(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "Insert failed");
+
+ torture_assert(tctx, r.out.result == 0, "Insert failed");
+
+ if (!test_Delete(p, tctx, r.in.entries)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_InqObject(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct epm_InqObject r;
+
+ r.in.epm_object = talloc(tctx, struct GUID);
+ *r.in.epm_object = ndr_table_epmapper.syntax_id.uuid;
+
+ status = dcerpc_epm_InqObject(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "InqObject failed");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_epmapper(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "EPMAPPER");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "epmapper",
+ &ndr_table_epmapper);
+
+ torture_rpc_tcase_add_test(tcase, "Lookup", test_Lookup);
+
+ torture_rpc_tcase_add_test(tcase, "Insert", test_Insert);
+
+ torture_rpc_tcase_add_test(tcase, "InqObject", test_InqObject);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/eventlog.c b/source4/torture/rpc/eventlog.c
new file mode 100644
index 0000000000..feeeb9330b
--- /dev/null
+++ b/source4/torture/rpc/eventlog.c
@@ -0,0 +1,260 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for eventlog rpc operations
+
+ Copyright (C) Tim Potter 2003,2005
+ Copyright (C) Jelmer Vernooij 2004
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_eventlog.h"
+#include "librpc/gen_ndr/ndr_eventlog_c.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+
+static void init_lsa_String(struct lsa_String *name, const char *s)
+{
+ name->string = s;
+ name->length = 2*strlen_m(s);
+ name->size = name->length;
+}
+
+static bool get_policy_handle(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct eventlog_OpenEventLogW r;
+ struct eventlog_OpenUnknown0 unknown0;
+
+ unknown0.unknown0 = 0x005c;
+ unknown0.unknown1 = 0x0001;
+
+ r.in.unknown0 = &unknown0;
+ init_lsa_String(&r.in.logname, "dns server");
+ init_lsa_String(&r.in.servername, NULL);
+ r.in.unknown2 = 0x00000001;
+ r.in.unknown3 = 0x00000001;
+ r.out.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_OpenEventLogW(p, tctx, &r),
+ "OpenEventLog failed");
+
+ torture_assert_ntstatus_ok(tctx, r.out.result, "OpenEventLog failed");
+
+ return true;
+}
+
+
+
+static bool test_GetNumRecords(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct eventlog_GetNumRecords r;
+ struct eventlog_CloseEventLog cr;
+ struct policy_handle handle;
+ uint32_t number = 0;
+
+ if (!get_policy_handle(tctx, p, &handle))
+ return false;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &handle;
+ r.out.number = &number;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_GetNumRecords(p, tctx, &r),
+ "GetNumRecords failed");
+
+ torture_comment(tctx, "%d records\n", *r.out.number);
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "CloseEventLog failed");
+ return true;
+}
+
+static bool test_ReadEventLog(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct eventlog_ReadEventLogW r;
+ struct eventlog_CloseEventLog cr;
+ struct policy_handle handle;
+
+ if (!get_policy_handle(tctx, p, &handle))
+ return false;
+
+ ZERO_STRUCT(r);
+ r.in.offset = 0;
+ r.in.handle = &handle;
+ r.in.flags = EVENTLOG_BACKWARDS_READ|EVENTLOG_SEQUENTIAL_READ;
+
+ while (1) {
+ DATA_BLOB blob;
+ struct eventlog_Record rec;
+ struct ndr_pull *ndr;
+ enum ndr_err_code ndr_err;
+ uint32_t sent_size = 0;
+ uint32_t real_size = 0;
+
+ /* Read first for number of bytes in record */
+
+ r.in.number_of_bytes = 0;
+ r.out.data = NULL;
+ r.out.sent_size = &sent_size;
+ r.out.real_size = &real_size;
+
+ status = dcerpc_eventlog_ReadEventLogW(p, tctx, &r);
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_END_OF_FILE)) {
+ break;
+ }
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_BUFFER_TOO_SMALL,
+ "ReadEventLog failed");
+
+ /* Now read the actual record */
+
+ r.in.number_of_bytes = *r.out.real_size;
+ r.out.data = talloc_array(tctx, uint8_t, r.in.number_of_bytes);
+
+ status = dcerpc_eventlog_ReadEventLogW(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "ReadEventLog failed");
+
+ /* Decode a user-marshalled record */
+
+ blob.length = *r.out.sent_size;
+ blob.data = talloc_steal(tctx, r.out.data);
+
+ ndr = ndr_pull_init_blob(&blob, tctx, lp_iconv_convenience(tctx->lp_ctx));
+
+ ndr_err = ndr_pull_eventlog_Record(
+ ndr, NDR_SCALARS|NDR_BUFFERS, &rec);
+ status = ndr_map_error2ntstatus(ndr_err);
+
+ NDR_PRINT_DEBUG(eventlog_Record, &rec);
+
+ torture_assert_ntstatus_ok(tctx, status,
+ "ReadEventLog failed parsing event log record");
+
+ r.in.offset++;
+ }
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "CloseEventLog failed");
+
+ return true;
+}
+
+static bool test_FlushEventLog(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct eventlog_FlushEventLog r;
+ struct eventlog_CloseEventLog cr;
+ struct policy_handle handle;
+
+ if (!get_policy_handle(tctx, p, &handle))
+ return false;
+
+ r.in.handle = &handle;
+
+ /* Huh? Does this RPC always return access denied? */
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_eventlog_FlushEventLog(p, tctx, &r),
+ NT_STATUS_ACCESS_DENIED,
+ "FlushEventLog failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "CloseEventLog failed");
+
+ return true;
+}
+
+static bool test_ClearEventLog(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct eventlog_ClearEventLogW r;
+ struct eventlog_CloseEventLog cr;
+ struct policy_handle handle;
+
+ if (!get_policy_handle(tctx, p, &handle))
+ return false;
+
+ r.in.handle = &handle;
+ r.in.unknown = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_ClearEventLogW(p, tctx, &r),
+ "ClearEventLog failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "CloseEventLog failed");
+
+ return true;
+}
+
+static bool test_OpenEventLog(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct policy_handle handle;
+ struct eventlog_CloseEventLog cr;
+
+ if (!get_policy_handle(tctx, p, &handle))
+ return false;
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog(p, tctx, &cr),
+ "CloseEventLog failed");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_eventlog(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+ struct torture_rpc_tcase *tcase;
+ struct torture_test *test;
+
+ suite = torture_suite_create(mem_ctx, "EVENTLOG");
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "eventlog",
+ &ndr_table_eventlog);
+
+ torture_rpc_tcase_add_test(tcase, "OpenEventLog", test_OpenEventLog);
+ test = torture_rpc_tcase_add_test(tcase, "ClearEventLog",
+ test_ClearEventLog);
+ test->dangerous = true;
+ torture_rpc_tcase_add_test(tcase, "GetNumRecords", test_GetNumRecords);
+ torture_rpc_tcase_add_test(tcase, "ReadEventLog", test_ReadEventLog);
+ torture_rpc_tcase_add_test(tcase, "FlushEventLog", test_FlushEventLog);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/frsapi.c b/source4/torture/rpc/frsapi.c
new file mode 100644
index 0000000000..c8e421a674
--- /dev/null
+++ b/source4/torture/rpc/frsapi.c
@@ -0,0 +1,275 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for rpc frsapi operations
+
+ Copyright (C) Guenther Deschner 2007
+
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_frsapi_c.h"
+#include "torture/util.h"
+#include "param/param.h"
+
+static bool test_GetDsPollingIntervalW(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ uint32_t *CurrentInterval,
+ uint32_t *DsPollingLongInterval,
+ uint32_t *DsPollingShortInterval)
+{
+ struct frsapi_GetDsPollingIntervalW r;
+
+ ZERO_STRUCT(r);
+
+ r.out.CurrentInterval = CurrentInterval;
+ r.out.DsPollingLongInterval = DsPollingLongInterval;
+ r.out.DsPollingShortInterval = DsPollingShortInterval;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_frsapi_GetDsPollingIntervalW(p, tctx, &r),
+ "GetDsPollingIntervalW failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "GetDsPollingIntervalW failed");
+
+ return true;
+}
+
+static bool test_SetDsPollingIntervalW(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ uint32_t CurrentInterval,
+ uint32_t DsPollingLongInterval,
+ uint32_t DsPollingShortInterval)
+{
+ struct frsapi_SetDsPollingIntervalW r;
+
+ ZERO_STRUCT(r);
+
+ r.in.CurrentInterval = CurrentInterval;
+ r.in.DsPollingLongInterval = DsPollingLongInterval;
+ r.in.DsPollingShortInterval = DsPollingShortInterval;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_frsapi_SetDsPollingIntervalW(p, tctx, &r),
+ "SetDsPollingIntervalW failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "SetDsPollingIntervalW failed");
+
+ return true;
+}
+
+static bool test_DsPollingIntervalW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ uint32_t i1, i2, i3;
+ uint32_t k1, k2, k3;
+
+ if (!test_GetDsPollingIntervalW(tctx, p, &i1, &i2, &i3)) {
+ return false;
+ }
+
+ if (!test_SetDsPollingIntervalW(tctx, p, i1, i2, i3)) {
+ return false;
+ }
+
+ k1 = i1;
+ k2 = k3 = 0;
+
+ if (!test_SetDsPollingIntervalW(tctx, p, k1, k2, k3)) {
+ return false;
+ }
+
+ if (!test_GetDsPollingIntervalW(tctx, p, &k1, &k2, &k3)) {
+ return false;
+ }
+
+ if ((i1 != k1) || (i2 != k2) || (i3 != k3)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_IsPathReplicated_err(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *path,
+ uint32_t type,
+ WERROR werr)
+{
+ struct frsapi_IsPathReplicated r;
+ struct GUID guid;
+ uint32_t unknown1, unknown2, unknown3 = 0;
+
+ ZERO_STRUCT(r);
+
+ r.in.path = path;
+ r.in.replica_set_type = type;
+ r.out.unknown1 = &unknown1;
+ r.out.unknown2 = &unknown2;
+ r.out.unknown3 = &unknown3;
+ r.out.replica_set_guid = &guid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_frsapi_IsPathReplicated(p, tctx, &r),
+ "IsPathReplicated failed");
+
+ torture_assert_werr_equal(tctx, r.out.result, werr,
+ "GetDsPollingIntervalW failed");
+
+ return true;
+}
+
+static bool _test_IsPathReplicated(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *path,
+ uint32_t type)
+{
+ return test_IsPathReplicated_err(tctx, p, path, type, WERR_OK);
+}
+
+static bool test_IsPathReplicated(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ const uint32_t lvls[] = {
+ FRSAPI_REPLICA_SET_TYPE_0,
+ FRSAPI_REPLICA_SET_TYPE_DOMAIN,
+ FRSAPI_REPLICA_SET_TYPE_DFS };
+ int i;
+ bool ret = true;
+
+ if (!test_IsPathReplicated_err(tctx, p, NULL, 0,
+ WERR_FRS_INVALID_SERVICE_PARAMETER)) {
+ ret = false;
+ }
+
+ for (i=0; i<ARRAY_SIZE(lvls); i++) {
+ if (!_test_IsPathReplicated(tctx, p, dcerpc_server_name(p),
+ lvls[i])) {
+ ret = false;
+ }
+ }
+
+ for (i=0; i<ARRAY_SIZE(lvls); i++) {
+ const char *path = talloc_asprintf(tctx, "\\\\%s\\SYSVOL",
+ dcerpc_server_name(p));
+ if (!_test_IsPathReplicated(tctx, p, path, lvls[i])) {
+ ret = false;
+ }
+ }
+
+ for (i=0; i<ARRAY_SIZE(lvls); i++) {
+ if (!_test_IsPathReplicated(tctx, p,
+ "C:\\windows\\sysvol\\domain",
+ lvls[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_ForceReplication(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct frsapi_ForceReplication r;
+
+ ZERO_STRUCT(r);
+
+ r.in.guid1 = NULL;
+ r.in.guid2 = NULL;
+ r.in.replica_set = talloc_asprintf(tctx, "%s",
+ lp_realm(tctx->lp_ctx));
+ r.in.partner_name = dcerpc_server_name(p);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_frsapi_ForceReplication(p, tctx, &r),
+ "ForceReplication failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "ForceReplication failed");
+
+ return true;
+}
+
+static bool test_InfoW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+
+ for (i=0; i<10; i++) {
+
+ struct frsapi_InfoW r;
+ struct frsapi_Info *info;
+ int d;
+ DATA_BLOB blob;
+
+ ZERO_STRUCT(r);
+
+ info = talloc_zero(tctx, struct frsapi_Info);
+
+ r.in.length = 0x1000;
+ r.in.info = r.out.info = info;
+
+ info->length = r.in.length;
+ info->length2 = r.in.length;
+ info->level = i;
+ info->offset = 0x2c;
+ info->blob_len = 0x2c;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_frsapi_InfoW(p, tctx, &r),
+ "InfoW failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "InfoW failed");
+
+ /* display the formatted blob text */
+ blob = r.out.info->blob;
+ for (d = 0; d < blob.length; d++) {
+ if (blob.data[d]) {
+ printf("%c", blob.data[d]);
+ }
+ }
+ printf("\n");
+ }
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_frsapi(TALLOC_CTX *mem_ctx)
+{
+ struct torture_rpc_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "FRSAPI");
+ struct torture_test *test;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "frsapi",
+ &ndr_table_frsapi);
+
+ test = torture_rpc_tcase_add_test(tcase, "DsPollingIntervalW",
+ test_DsPollingIntervalW);
+
+ test = torture_rpc_tcase_add_test(tcase, "IsPathReplicated",
+ test_IsPathReplicated);
+
+ test = torture_rpc_tcase_add_test(tcase, "ForceReplication",
+ test_ForceReplication);
+
+ test = torture_rpc_tcase_add_test(tcase, "InfoW",
+ test_InfoW);
+ return suite;
+}
diff --git a/source4/torture/rpc/handles.c b/source4/torture/rpc/handles.c
new file mode 100644
index 0000000000..f35897b3df
--- /dev/null
+++ b/source4/torture/rpc/handles.c
@@ -0,0 +1,580 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for behaviour of rpc policy handles
+
+ Copyright (C) Andrew Tridgell 2007
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "torture/rpc/rpc.h"
+
+/*
+ this tests the use of policy handles between connections
+*/
+
+static bool test_handles_lsa(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p1, *p2;
+ struct policy_handle handle;
+ struct policy_handle handle2;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy r;
+ struct lsa_Close c;
+ uint16_t system_name = '\\';
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+
+ torture_comment(torture, "RPC-HANDLE-LSARPC\n");
+
+ status = torture_rpc_connection(torture, &p1, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1");
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1");
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = &system_name;
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ status = dcerpc_lsa_OpenPolicy(p1, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(torture, "lsa_OpenPolicy not supported - skipping\n");
+ talloc_free(mem_ctx);
+ return true;
+ }
+
+ c.in.handle = &handle;
+ c.out.handle = &handle2;
+
+ status = dcerpc_lsa_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2");
+ torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p2");
+
+ status = dcerpc_lsa_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1");
+
+ status = dcerpc_lsa_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p1 again");
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+static bool test_handles_lsa_shared(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p1, *p2, *p3, *p4, *p5;
+ struct policy_handle handle;
+ struct policy_handle handle2;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy r;
+ struct lsa_Close c;
+ struct lsa_QuerySecurity qsec;
+ uint16_t system_name = '\\';
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+ enum dcerpc_transport_t transport;
+ uint32_t assoc_group_id;
+
+ torture_comment(torture, "RPC-HANDLE-LSARPC-SHARED\n");
+
+ torture_comment(torture, "connect lsa pipe1\n");
+ status = torture_rpc_connection(torture, &p1, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1");
+
+ transport = p1->conn->transport.transport,
+ assoc_group_id = p1->assoc_group_id;
+
+ torture_comment(torture, "use assoc_group_id[0x%08X] for new connections\n", assoc_group_id);
+
+ torture_comment(torture, "connect lsa pipe2\n");
+ status = torture_rpc_connection_transport(torture, &p2, &ndr_table_lsarpc,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2");
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = &system_name;
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ torture_comment(torture, "open lsa policy handle\n");
+ status = dcerpc_lsa_OpenPolicy(p1, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(torture, "lsa_OpenPolicy not supported - skipping\n");
+ talloc_free(mem_ctx);
+ return true;
+ }
+
+ /*
+ * connect p3 after the policy handle is opened
+ */
+ torture_comment(torture, "connect lsa pipe3 after the policy handle is opened\n");
+ status = torture_rpc_connection_transport(torture, &p3, &ndr_table_lsarpc,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe3");
+
+ qsec.in.handle = &handle;
+ qsec.in.sec_info = 0;
+ c.in.handle = &handle;
+ c.out.handle = &handle2;
+
+ /*
+ * use policy handle on all 3 connections
+ */
+ torture_comment(torture, "use the policy handle on p1,p2,p3\n");
+ status = dcerpc_lsa_QuerySecurity(p1, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p1");
+
+ status = dcerpc_lsa_QuerySecurity(p2, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p2");
+
+ status = dcerpc_lsa_QuerySecurity(p3, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p3");
+
+ /*
+ * close policy handle on connection 2 and the others get a fault
+ */
+ torture_comment(torture, "close the policy handle on p2 others get a fault\n");
+ status = dcerpc_lsa_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "closing policy handle on p2");
+
+ status = dcerpc_lsa_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p1 again");
+
+ status = dcerpc_lsa_Close(p3, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p3");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p3");
+
+ status = dcerpc_lsa_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p2 again");
+
+ /*
+ * open a new policy handle on p3
+ */
+ torture_comment(torture, "open a new policy handle on p3\n");
+ status = dcerpc_lsa_OpenPolicy(p3, mem_ctx, &r);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "open policy handle on p3");
+
+ /*
+ * use policy handle on all 3 connections
+ */
+ torture_comment(torture, "use the policy handle on p1,p2,p3\n");
+ status = dcerpc_lsa_QuerySecurity(p1, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p1");
+
+ status = dcerpc_lsa_QuerySecurity(p2, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p2");
+
+ status = dcerpc_lsa_QuerySecurity(p3, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p3");
+
+ /*
+ * close policy handle on connection 2 and the others get a fault
+ */
+ torture_comment(torture, "close the policy handle on p2 others get a fault\n");
+ status = dcerpc_lsa_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "closing policy handle on p2");
+
+ status = dcerpc_lsa_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p1 again");
+
+ status = dcerpc_lsa_Close(p3, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p3");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p3");
+
+ status = dcerpc_lsa_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p2 again");
+
+ /*
+ * open a new policy handle
+ */
+ torture_comment(torture, "open a new policy handle on p1 and use it\n");
+ status = dcerpc_lsa_OpenPolicy(p1, mem_ctx, &r);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "open 2nd policy handle on p1");
+
+ status = dcerpc_lsa_QuerySecurity(p1, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "QuerySecurity handle on p1");
+
+ /* close first connection */
+ torture_comment(torture, "disconnect p1\n");
+ talloc_free(p1);
+ msleep(5);
+
+ /*
+ * and it's still available on p2,p3
+ */
+ torture_comment(torture, "use policy handle on p2,p3\n");
+ status = dcerpc_lsa_QuerySecurity(p2, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "QuerySecurity handle on p2 after p1 was disconnected");
+
+ status = dcerpc_lsa_QuerySecurity(p3, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "QuerySecurity handle on p3 after p1 was disconnected");
+
+ /*
+ * now open p4
+ * and use the handle on it
+ */
+ torture_comment(torture, "connect lsa pipe4 and use policy handle\n");
+ status = torture_rpc_connection_transport(torture, &p4, &ndr_table_lsarpc,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe4");
+
+ status = dcerpc_lsa_QuerySecurity(p4, mem_ctx, &qsec);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "using policy handle on p4");
+
+ /*
+ * now close p2,p3,p4
+ * without closing the policy handle
+ */
+ torture_comment(torture, "disconnect p2,p3,p4\n");
+ talloc_free(p2);
+ talloc_free(p3);
+ talloc_free(p4);
+ msleep(10);
+
+ /*
+ * now open p5
+ */
+ torture_comment(torture, "connect lsa pipe5 - should fail\n");
+ status = torture_rpc_connection_transport(torture, &p5, &ndr_table_lsarpc,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL,
+ "opening lsa pipe5");
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+
+static bool test_handles_samr(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p1, *p2;
+ struct policy_handle handle;
+ struct policy_handle handle2;
+ struct samr_Connect r;
+ struct samr_Close c;
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+
+ torture_comment(torture, "RPC-HANDLE-SAMR\n");
+
+ status = torture_rpc_connection(torture, &p1, &ndr_table_samr);
+ torture_assert_ntstatus_ok(torture, status, "opening samr pipe1");
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_samr);
+ torture_assert_ntstatus_ok(torture, status, "opening samr pipe1");
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = &handle;
+
+ status = dcerpc_samr_Connect(p1, mem_ctx, &r);
+ torture_assert_ntstatus_ok(torture, status, "opening policy handle on p1");
+
+ c.in.handle = &handle;
+ c.out.handle = &handle2;
+
+ status = dcerpc_samr_Close(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2");
+ torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p2");
+
+ status = dcerpc_samr_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1");
+
+ status = dcerpc_samr_Close(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p1 again");
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+static bool test_handles_mixed_shared(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p1, *p2, *p3, *p4, *p5, *p6;
+ struct policy_handle handle;
+ struct policy_handle handle2;
+ struct samr_Connect r;
+ struct lsa_Close lc;
+ struct samr_Close sc;
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+ enum dcerpc_transport_t transport;
+ uint32_t assoc_group_id;
+
+ torture_comment(torture, "RPC-HANDLE-MIXED-SHARED\n");
+
+ torture_comment(torture, "connect samr pipe1\n");
+ status = torture_rpc_connection(torture, &p1, &ndr_table_samr);
+ torture_assert_ntstatus_ok(torture, status, "opening samr pipe1");
+
+ transport = p1->conn->transport.transport,
+ assoc_group_id = p1->assoc_group_id;
+
+ torture_comment(torture, "use assoc_group_id[0x%08X] for new connections\n", assoc_group_id);
+
+ torture_comment(torture, "connect lsa pipe2\n");
+ status = torture_rpc_connection_transport(torture, &p2, &ndr_table_lsarpc,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2");
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = &handle;
+
+ torture_comment(torture, "samr_Connect to open a policy handle on samr p1\n");
+ status = dcerpc_samr_Connect(p1, mem_ctx, &r);
+ torture_assert_ntstatus_ok(torture, status, "opening policy handle on p1");
+
+ lc.in.handle = &handle;
+ lc.out.handle = &handle2;
+ sc.in.handle = &handle;
+ sc.out.handle = &handle2;
+
+ torture_comment(torture, "use policy handle on lsa p2 - should fail\n");
+ status = dcerpc_lsa_Close(p2, mem_ctx, &lc);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing handle on lsa p2");
+ torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing handle on lsa p2");
+
+ torture_comment(torture, "closing policy handle on samr p1\n");
+ status = dcerpc_samr_Close(p1, mem_ctx, &sc);
+ torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1");
+
+ talloc_free(p1);
+ talloc_free(p2);
+ msleep(10);
+
+ torture_comment(torture, "connect samr pipe3 - should fail\n");
+ status = torture_rpc_connection_transport(torture, &p3, &ndr_table_samr,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL,
+ "opening samr pipe3");
+
+ torture_comment(torture, "connect lsa pipe4 - should fail\n");
+ status = torture_rpc_connection_transport(torture, &p4, &ndr_table_lsarpc,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL,
+ "opening lsa pipe4");
+
+ torture_comment(torture, "connect samr pipe5 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id);
+ status = torture_rpc_connection_transport(torture, &p5, &ndr_table_samr,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL,
+ "opening samr pipe5");
+
+ torture_comment(torture, "connect lsa pipe6 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id);
+ status = torture_rpc_connection_transport(torture, &p6, &ndr_table_lsarpc,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL,
+ "opening lsa pipe6");
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+static bool test_handles_random_assoc(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p1, *p2, *p3;
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+ enum dcerpc_transport_t transport;
+ uint32_t assoc_group_id;
+
+ torture_comment(torture, "RPC-HANDLE-RANDOM-ASSOC\n");
+
+ torture_comment(torture, "connect samr pipe1\n");
+ status = torture_rpc_connection(torture, &p1, &ndr_table_samr);
+ torture_assert_ntstatus_ok(torture, status, "opening samr pipe1");
+
+ transport = p1->conn->transport.transport,
+ assoc_group_id = p1->assoc_group_id;
+
+ torture_comment(torture, "pip1 use assoc_group_id[0x%08X]\n", assoc_group_id);
+
+ torture_comment(torture, "connect samr pipe2 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id);
+ status = torture_rpc_connection_transport(torture, &p2, &ndr_table_samr,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL,
+ "opening samr pipe2");
+
+ torture_comment(torture, "connect samr pipe3 with assoc_group_id[0x%08X]- should fail\n", ++assoc_group_id);
+ status = torture_rpc_connection_transport(torture, &p3, &ndr_table_samr,
+ transport,
+ assoc_group_id);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL,
+ "opening samr pipe3");
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+
+static bool test_handles_drsuapi(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p1, *p2;
+ struct policy_handle handle;
+ struct policy_handle handle2;
+ struct GUID bind_guid;
+ struct drsuapi_DsBind r;
+ struct drsuapi_DsUnbind c;
+ TALLOC_CTX *mem_ctx = talloc_new(torture);
+
+ torture_comment(torture, "RPC-HANDLE-DRSUAPI\n");
+
+ status = torture_rpc_connection(torture, &p1, &ndr_table_drsuapi);
+ torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1");
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_drsuapi);
+ torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1");
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid);
+
+ r.in.bind_guid = &bind_guid;
+ r.in.bind_info = NULL;
+ r.out.bind_handle = &handle;
+
+ status = dcerpc_drsuapi_DsBind(p1, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(torture, "drsuapi_DsBind not supported - skipping\n");
+ talloc_free(mem_ctx);
+ return true;
+ }
+
+ c.in.bind_handle = &handle;
+ c.out.bind_handle = &handle2;
+
+ status = dcerpc_drsuapi_DsUnbind(p2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p2");
+ torture_assert_int_equal(torture, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p2");
+
+ status = dcerpc_drsuapi_DsUnbind(p1, mem_ctx, &c);
+ torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1");
+
+ status = dcerpc_drsuapi_DsUnbind(p1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_NET_WRITE_FAULT,
+ "closing policy handle on p1 again");
+ torture_assert_int_equal(torture, p1->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "closing policy handle on p1 again");
+
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+
+struct torture_suite *torture_rpc_handles(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+
+ suite = torture_suite_create(mem_ctx, "HANDLES");
+ torture_suite_add_simple_test(suite, "lsarpc", test_handles_lsa);
+ torture_suite_add_simple_test(suite, "lsarpc-shared", test_handles_lsa_shared);
+ torture_suite_add_simple_test(suite, "samr", test_handles_samr);
+ torture_suite_add_simple_test(suite, "mixed-shared", test_handles_mixed_shared);
+ torture_suite_add_simple_test(suite, "random-assoc", test_handles_random_assoc);
+ torture_suite_add_simple_test(suite, "drsuapi", test_handles_drsuapi);
+ return suite;
+}
diff --git a/source4/torture/rpc/initshutdown.c b/source4/torture/rpc/initshutdown.c
new file mode 100644
index 0000000000..92fec5be0c
--- /dev/null
+++ b/source4/torture/rpc/initshutdown.c
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for initshutdown operations
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Jelmer Vernooij 2004-2005
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_initshutdown_c.h"
+#include "torture/rpc/rpc.h"
+
+static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s)
+{
+ name->string = s;
+}
+
+
+static bool test_Abort(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct initshutdown_Abort r;
+ NTSTATUS status;
+ uint16_t server = 0x0;
+
+ r.in.server = &server;
+
+ status = dcerpc_initshutdown_Abort(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status,
+ "initshutdown_Abort failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "initshutdown_Abort failed");
+
+ return true;
+}
+
+static bool test_Init(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct initshutdown_Init r;
+ NTSTATUS status;
+ uint16_t hostname = 0x0;
+
+ r.in.hostname = &hostname;
+ r.in.message = talloc(tctx, struct lsa_StringLarge);
+ init_lsa_StringLarge(r.in.message, "spottyfood");
+ r.in.force_apps = 1;
+ r.in.timeout = 30;
+ r.in.reboot = 1;
+
+ status = dcerpc_initshutdown_Init(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "initshutdown_Init failed");
+ torture_assert_werr_ok(tctx, r.out.result, "initshutdown_Init failed");
+
+ return test_Abort(tctx, p);
+}
+
+static bool test_InitEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct initshutdown_InitEx r;
+ NTSTATUS status;
+ uint16_t hostname = 0x0;
+
+ r.in.hostname = &hostname;
+ r.in.message = talloc(tctx, struct lsa_StringLarge);
+ init_lsa_StringLarge(r.in.message, "spottyfood");
+ r.in.force_apps = 1;
+ r.in.timeout = 30;
+ r.in.reboot = 1;
+ r.in.reason = 0;
+
+ status = dcerpc_initshutdown_InitEx(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "initshutdown_InitEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "initshutdown_InitEx failed");
+
+ return test_Abort(tctx, p);
+}
+
+
+struct torture_suite *torture_rpc_initshutdown(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "INITSHUTDOWN");
+ struct torture_rpc_tcase *tcase;
+ struct torture_test *test;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "initshutdown",
+ &ndr_table_initshutdown);
+
+ test = torture_rpc_tcase_add_test(tcase, "Init", test_Init);
+ test->dangerous = true;
+ test = torture_rpc_tcase_add_test(tcase, "InitEx", test_InitEx);
+ test->dangerous = true;
+
+ return suite;
+}
diff --git a/source4/torture/rpc/join.c b/source4/torture/rpc/join.c
new file mode 100644
index 0000000000..77da32d572
--- /dev/null
+++ b/source4/torture/rpc/join.c
@@ -0,0 +1,82 @@
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "libcli/libcli.h"
+
+#include "auth/credentials/credentials.h"
+#include "torture/rpc/rpc.h"
+
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+
+#define TORTURE_NETBIOS_NAME "smbtorturejoin"
+
+
+bool torture_rpc_join(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct test_join *tj;
+ struct cli_credentials *machine_account;
+ struct smbcli_state *cli;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ struct smbcli_options options;
+
+ /* Join domain as a member server. */
+ tj = torture_join_domain(torture,
+ TORTURE_NETBIOS_NAME,
+ ACB_WSTRUST,
+ &machine_account);
+
+ if (!tj) {
+ DEBUG(0, ("%s failed to join domain as workstation\n",
+ TORTURE_NETBIOS_NAME));
+ return false;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+
+ status = smbcli_full_connection(tj, &cli, host,
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ machine_account,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("%s failed to connect to IPC$ with workstation credentials\n",
+ TORTURE_NETBIOS_NAME));
+ return false;
+ }
+ smbcli_tdis(cli);
+
+ /* Leave domain. */
+ torture_leave_domain(torture, tj);
+
+ /* Join domain as a domain controller. */
+ tj = torture_join_domain(torture, TORTURE_NETBIOS_NAME,
+ ACB_SVRTRUST,
+ &machine_account);
+ if (!tj) {
+ DEBUG(0, ("%s failed to join domain as domain controller\n",
+ TORTURE_NETBIOS_NAME));
+ return false;
+ }
+
+ status = smbcli_full_connection(tj, &cli, host,
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ machine_account,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("%s failed to connect to IPC$ with workstation credentials\n",
+ TORTURE_NETBIOS_NAME));
+ return false;
+ }
+
+ smbcli_tdis(cli);
+
+ /* Leave domain. */
+ torture_leave_domain(torture, tj);
+
+ return true;
+}
+
diff --git a/source4/torture/rpc/lsa.c b/source4/torture/rpc/lsa.c
new file mode 100644
index 0000000000..a13a8d8818
--- /dev/null
+++ b/source4/torture/rpc/lsa.c
@@ -0,0 +1,2558 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for lsa rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "lib/events/events.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "lib/crypto/crypto.h"
+#define TEST_MACHINENAME "lsatestmach"
+
+static void init_lsa_String(struct lsa_String *name, const char *s)
+{
+ name->string = s;
+}
+
+static bool test_OpenPolicy(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
+{
+ struct lsa_ObjectAttribute attr;
+ struct policy_handle handle;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy r;
+ NTSTATUS status;
+ uint16_t system_name = '\\';
+
+ printf("\ntesting OpenPolicy\n");
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = &system_name;
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ status = dcerpc_lsa_OpenPolicy(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ return true;
+ }
+ printf("OpenPolicy failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool test_lsa_OpenPolicy2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle **handle)
+{
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+ NTSTATUS status;
+
+ printf("\ntesting OpenPolicy2\n");
+
+ *handle = talloc(mem_ctx, struct policy_handle);
+ if (!*handle) {
+ return false;
+ }
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = "\\";
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = *handle;
+
+ status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ talloc_free(*handle);
+ *handle = NULL;
+ return true;
+ }
+ printf("OpenPolicy2 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static const char *sid_type_lookup(enum lsa_SidType r)
+{
+ switch (r) {
+ case SID_NAME_USE_NONE: return "SID_NAME_USE_NONE"; break;
+ case SID_NAME_USER: return "SID_NAME_USER"; break;
+ case SID_NAME_DOM_GRP: return "SID_NAME_DOM_GRP"; break;
+ case SID_NAME_DOMAIN: return "SID_NAME_DOMAIN"; break;
+ case SID_NAME_ALIAS: return "SID_NAME_ALIAS"; break;
+ case SID_NAME_WKN_GRP: return "SID_NAME_WKN_GRP"; break;
+ case SID_NAME_DELETED: return "SID_NAME_DELETED"; break;
+ case SID_NAME_INVALID: return "SID_NAME_INVALID"; break;
+ case SID_NAME_UNKNOWN: return "SID_NAME_UNKNOWN"; break;
+ case SID_NAME_COMPUTER: return "SID_NAME_COMPUTER"; break;
+ }
+ return "Invalid sid type\n";
+}
+
+static bool test_LookupNames(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_TransNameArray *tnames)
+{
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_String *names;
+ uint32_t count = 0;
+ NTSTATUS status;
+ int i;
+
+ printf("\nTesting LookupNames with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(mem_ctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ init_lsa_String(&names[i], tnames->names[i].name.string);
+ }
+
+ r.in.handle = handle;
+ r.in.num_names = tnames->count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+
+ status = dcerpc_lsa_LookupNames(p, mem_ctx, &r);
+
+ if (NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ for (i=0;i< tnames->count;i++) {
+ if (i < count && sids.sids[i].sid_type == SID_NAME_UNKNOWN) {
+ printf("LookupName of %s was unmapped\n",
+ tnames->names[i].name.string);
+ } else if (i >=count) {
+ printf("LookupName of %s failed to return a result\n",
+ tnames->names[i].name.string);
+ }
+ }
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i=0;i< tnames->count;i++) {
+ if (i < count && sids.sids[i].sid_type != tnames->names[i].sid_type) {
+ printf("LookupName of %s got unexpected name type: %s\n",
+ tnames->names[i].name.string, sid_type_lookup(sids.sids[i].sid_type));
+ } else if (i >=count) {
+ printf("LookupName of %s failed to return a result\n",
+ tnames->names[i].name.string);
+ }
+ }
+ printf("\n");
+
+ return true;
+}
+
+static bool test_LookupNames_bogus(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_String *names;
+ uint32_t count = 0;
+ NTSTATUS status;
+ int i;
+
+ struct lsa_TranslatedName name;
+ struct lsa_TransNameArray tnames;
+
+ tnames.names = &name;
+ tnames.count = 1;
+ name.name.string = "NT AUTHORITY\\BOGUS";
+
+ printf("\nTesting LookupNames with bogus names\n");
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(mem_ctx, struct lsa_String, tnames.count);
+ for (i=0;i<tnames.count;i++) {
+ init_lsa_String(&names[i], tnames.names[i].name.string);
+ }
+
+ r.in.handle = handle;
+ r.in.num_names = tnames.count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+
+ status = dcerpc_lsa_LookupNames(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+static bool test_LookupNames_wellknown(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ struct lsa_TranslatedName name;
+ struct lsa_TransNameArray tnames;
+ bool ret = true;
+
+ printf("Testing LookupNames with well known names\n");
+
+ tnames.names = &name;
+ tnames.count = 1;
+ name.name.string = "NT AUTHORITY\\SYSTEM";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(p, mem_ctx, handle, &tnames);
+
+ name.name.string = "NT AUTHORITY\\ANONYMOUS LOGON";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(p, mem_ctx, handle, &tnames);
+
+ name.name.string = "NT AUTHORITY\\Authenticated Users";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(p, mem_ctx, handle, &tnames);
+
+#if 0
+ name.name.string = "NT AUTHORITY";
+ ret &= test_LookupNames(p, mem_ctx, handle, &tnames);
+
+ name.name.string = "NT AUTHORITY\\";
+ ret &= test_LookupNames(p, mem_ctx, handle, &tnames);
+#endif
+
+ name.name.string = "BUILTIN\\";
+ name.sid_type = SID_NAME_DOMAIN;
+ ret &= test_LookupNames(p, mem_ctx, handle, &tnames);
+
+ name.name.string = "BUILTIN\\Administrators";
+ name.sid_type = SID_NAME_ALIAS;
+ ret &= test_LookupNames(p, mem_ctx, handle, &tnames);
+
+ name.name.string = "SYSTEM";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(p, mem_ctx, handle, &tnames);
+
+ name.name.string = "Everyone";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(p, mem_ctx, handle, &tnames);
+ return ret;
+}
+
+static bool test_LookupNames2(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_TransNameArray2 *tnames)
+{
+ struct lsa_LookupNames2 r;
+ struct lsa_TransSidArray2 sids;
+ struct lsa_String *names;
+ uint32_t count = 0;
+ NTSTATUS status;
+ int i;
+
+ printf("\nTesting LookupNames2 with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(mem_ctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ init_lsa_String(&names[i], tnames->names[i].name.string);
+ }
+
+ r.in.handle = handle;
+ r.in.num_names = tnames->count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.count = &count;
+ r.out.sids = &sids;
+
+ status = dcerpc_lsa_LookupNames2(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames2 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+
+static bool test_LookupNames3(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_TransNameArray2 *tnames)
+{
+ struct lsa_LookupNames3 r;
+ struct lsa_TransSidArray3 sids;
+ struct lsa_String *names;
+ uint32_t count = 0;
+ NTSTATUS status;
+ int i;
+
+ printf("\nTesting LookupNames3 with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(mem_ctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ init_lsa_String(&names[i], tnames->names[i].name.string);
+ }
+
+ r.in.handle = handle;
+ r.in.num_names = tnames->count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.count = &count;
+ r.out.sids = &sids;
+
+ status = dcerpc_lsa_LookupNames3(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames3 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+static bool test_LookupNames4(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_TransNameArray2 *tnames)
+{
+ struct lsa_LookupNames4 r;
+ struct lsa_TransSidArray3 sids;
+ struct lsa_String *names;
+ uint32_t count = 0;
+ NTSTATUS status;
+ int i;
+
+ printf("\nTesting LookupNames4 with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names = talloc_array(mem_ctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ init_lsa_String(&names[i], tnames->names[i].name.string);
+ }
+
+ r.in.num_names = tnames->count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.count = &count;
+ r.out.sids = &sids;
+
+ status = dcerpc_lsa_LookupNames4(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames4 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+
+static bool test_LookupSids(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_SidArray *sids)
+{
+ struct lsa_LookupSids r;
+ struct lsa_TransNameArray names;
+ uint32_t count = sids->num_sids;
+ NTSTATUS status;
+
+ printf("\nTesting LookupSids\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.handle = handle;
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.names = &names;
+
+ status = dcerpc_lsa_LookupSids(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupSids failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ if (!test_LookupNames(p, mem_ctx, handle, &names)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_LookupSids2(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_SidArray *sids)
+{
+ struct lsa_LookupSids2 r;
+ struct lsa_TransNameArray2 names;
+ uint32_t count = sids->num_sids;
+ NTSTATUS status;
+
+ printf("\nTesting LookupSids2\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.handle = handle;
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.count = &count;
+ r.out.names = &names;
+
+ status = dcerpc_lsa_LookupSids2(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupSids2 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ if (!test_LookupNames2(p, mem_ctx, handle, &names)) {
+ return false;
+ }
+
+ if (!test_LookupNames3(p, mem_ctx, handle, &names)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_LookupSids3(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct lsa_SidArray *sids)
+{
+ struct lsa_LookupSids3 r;
+ struct lsa_TransNameArray2 names;
+ uint32_t count = sids->num_sids;
+ NTSTATUS status;
+
+ printf("\nTesting LookupSids3\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.count = &count;
+ r.out.names = &names;
+
+ status = dcerpc_lsa_LookupSids3(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ return true;
+ }
+ printf("LookupSids3 failed - %s - not considered an error\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ if (!test_LookupNames4(p, mem_ctx, &names)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool test_many_LookupSids(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ uint32_t count;
+ NTSTATUS status;
+ struct lsa_SidArray sids;
+ int i;
+
+ printf("\nTesting LookupSids with lots of SIDs\n");
+
+ sids.num_sids = 100;
+
+ sids.sids = talloc_array(mem_ctx, struct lsa_SidPtr, sids.num_sids);
+
+ for (i=0; i<sids.num_sids; i++) {
+ const char *sidstr = "S-1-5-32-545";
+ sids.sids[i].sid = dom_sid_parse_talloc(mem_ctx, sidstr);
+ }
+
+ count = sids.num_sids;
+
+ if (handle) {
+ struct lsa_LookupSids r;
+ struct lsa_TransNameArray names;
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.handle = handle;
+ r.in.sids = &sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &names.count;
+ r.out.count = &count;
+ r.out.names = &names;
+
+ status = dcerpc_lsa_LookupSids(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupSids failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ if (!test_LookupNames(p, mem_ctx, handle, &names)) {
+ return false;
+ }
+ } else if (p->conn->security_state.auth_info->auth_type == DCERPC_AUTH_TYPE_SCHANNEL &&
+ p->conn->security_state.auth_info->auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
+ struct lsa_LookupSids3 r;
+ struct lsa_TransNameArray2 names;
+
+ names.count = 0;
+ names.names = NULL;
+
+ printf("\nTesting LookupSids3\n");
+
+ r.in.sids = &sids;
+ r.in.names = &names;
+ r.in.level = 1;
+ r.in.count = &count;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.count = &count;
+ r.out.names = &names;
+
+ status = dcerpc_lsa_LookupSids3(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ return true;
+ }
+ printf("LookupSids3 failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ if (!test_LookupNames4(p, mem_ctx, &names)) {
+ return false;
+ }
+ }
+
+ printf("\n");
+
+
+
+ return true;
+}
+
+static void lookupsids_cb(struct rpc_request *req)
+{
+ int *replies = (int *)req->async.private_data;
+ NTSTATUS status;
+
+ status = dcerpc_ndr_request_recv(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lookupsids returned %s\n", nt_errstr(status));
+ *replies = -1;
+ }
+
+ if (*replies >= 0) {
+ *replies += 1;
+ }
+}
+
+static bool test_LookupSids_async(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ struct lsa_SidArray sids;
+ struct lsa_SidPtr sidptr;
+ uint32_t *count;
+ struct lsa_TransNameArray *names;
+ struct lsa_LookupSids *r;
+ struct rpc_request **req;
+ int i, replies;
+ bool ret = true;
+ const int num_async_requests = 50;
+
+ count = talloc_array(mem_ctx, uint32_t, num_async_requests);
+ names = talloc_array(mem_ctx, struct lsa_TransNameArray, num_async_requests);
+ r = talloc_array(mem_ctx, struct lsa_LookupSids, num_async_requests);
+
+ printf("\nTesting %d async lookupsids request\n", num_async_requests);
+
+ req = talloc_array(mem_ctx, struct rpc_request *, num_async_requests);
+
+ sids.num_sids = 1;
+ sids.sids = &sidptr;
+ sidptr.sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-545");
+
+ replies = 0;
+
+ for (i=0; i<num_async_requests; i++) {
+ count[i] = 0;
+ names[i].count = 0;
+ names[i].names = NULL;
+
+ r[i].in.handle = handle;
+ r[i].in.sids = &sids;
+ r[i].in.names = &names[i];
+ r[i].in.level = 1;
+ r[i].in.count = &names[i].count;
+ r[i].out.count = &count[i];
+ r[i].out.names = &names[i];
+
+ req[i] = dcerpc_lsa_LookupSids_send(p, req, &r[i]);
+ if (req[i] == NULL) {
+ ret = false;
+ break;
+ }
+
+ req[i]->async.callback = lookupsids_cb;
+ req[i]->async.private_data = &replies;
+ }
+
+ while (replies >= 0 && replies < num_async_requests) {
+ event_loop_once(p->conn->event_ctx);
+ }
+
+ talloc_free(req);
+
+ if (replies < 0) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_LookupPrivValue(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_String *name)
+{
+ NTSTATUS status;
+ struct lsa_LookupPrivValue r;
+ struct lsa_LUID luid;
+
+ r.in.handle = handle;
+ r.in.name = name;
+ r.out.luid = &luid;
+
+ status = dcerpc_lsa_LookupPrivValue(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("\nLookupPrivValue failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_LookupPrivName(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_LUID *luid)
+{
+ NTSTATUS status;
+ struct lsa_LookupPrivName r;
+
+ r.in.handle = handle;
+ r.in.luid = luid;
+
+ status = dcerpc_lsa_LookupPrivName(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("\nLookupPrivName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_RemovePrivilegesFromAccount(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle,
+ struct lsa_LUID *luid)
+{
+ NTSTATUS status;
+ struct lsa_RemovePrivilegesFromAccount r;
+ struct lsa_PrivilegeSet privs;
+ bool ret = true;
+
+ printf("Testing RemovePrivilegesFromAccount\n");
+
+ r.in.handle = acct_handle;
+ r.in.remove_all = 0;
+ r.in.privs = &privs;
+
+ privs.count = 1;
+ privs.unknown = 0;
+ privs.set = talloc_array(mem_ctx, struct lsa_LUIDAttribute, 1);
+ privs.set[0].luid = *luid;
+ privs.set[0].attribute = 0;
+
+ status = dcerpc_lsa_RemovePrivilegesFromAccount(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+
+ struct lsa_LookupPrivName r_name;
+
+ r_name.in.handle = handle;
+ r_name.in.luid = luid;
+
+ status = dcerpc_lsa_LookupPrivName(p, mem_ctx, &r_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("\nLookupPrivName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ /* Windows 2008 does not allow this to be removed */
+ if (strcmp("SeAuditPrivilege", r_name.out.name->string) == 0) {
+ return ret;
+ }
+
+ printf("RemovePrivilegesFromAccount failed to remove %s - %s\n",
+ r_name.out.name->string,
+ nt_errstr(status));
+ return false;
+ }
+
+ return ret;
+}
+
+static bool test_AddPrivilegesToAccount(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *acct_handle,
+ struct lsa_LUID *luid)
+{
+ NTSTATUS status;
+ struct lsa_AddPrivilegesToAccount r;
+ struct lsa_PrivilegeSet privs;
+ bool ret = true;
+
+ printf("Testing AddPrivilegesToAccount\n");
+
+ r.in.handle = acct_handle;
+ r.in.privs = &privs;
+
+ privs.count = 1;
+ privs.unknown = 0;
+ privs.set = talloc_array(mem_ctx, struct lsa_LUIDAttribute, 1);
+ privs.set[0].luid = *luid;
+ privs.set[0].attribute = 0;
+
+ status = dcerpc_lsa_AddPrivilegesToAccount(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddPrivilegesToAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrivsAccount(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle)
+{
+ NTSTATUS status;
+ struct lsa_EnumPrivsAccount r;
+ bool ret = true;
+
+ printf("Testing EnumPrivsAccount\n");
+
+ r.in.handle = acct_handle;
+
+ status = dcerpc_lsa_EnumPrivsAccount(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumPrivsAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (r.out.privs && r.out.privs->count > 0) {
+ int i;
+ for (i=0;i<r.out.privs->count;i++) {
+ test_LookupPrivName(p, mem_ctx, handle,
+ &r.out.privs->set[i].luid);
+ }
+
+ ret &= test_RemovePrivilegesFromAccount(p, mem_ctx, handle, acct_handle,
+ &r.out.privs->set[0].luid);
+ ret &= test_AddPrivilegesToAccount(p, mem_ctx, acct_handle,
+ &r.out.privs->set[0].luid);
+ }
+
+ return ret;
+}
+
+static bool test_Delete(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_Delete r;
+
+ printf("testing Delete\n");
+
+ r.in.handle = handle;
+ status = dcerpc_lsa_Delete(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("Delete should have failed NT_STATUS_NOT_SUPPORTED - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DeleteObject(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_DeleteObject r;
+
+ printf("testing DeleteObject\n");
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+ status = dcerpc_lsa_DeleteObject(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Delete failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_CreateAccount(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_CreateAccount r;
+ struct dom_sid2 *newsid;
+ struct policy_handle acct_handle;
+
+ newsid = dom_sid_parse_talloc(mem_ctx, "S-1-5-12349876-4321-2854");
+
+ printf("Testing CreateAccount\n");
+
+ r.in.handle = handle;
+ r.in.sid = newsid;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.acct_handle = &acct_handle;
+
+ status = dcerpc_lsa_CreateAccount(p, mem_ctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ struct lsa_OpenAccount r_o;
+ r_o.in.handle = handle;
+ r_o.in.sid = newsid;
+ r_o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r_o.out.acct_handle = &acct_handle;
+
+ status = dcerpc_lsa_OpenAccount(p, mem_ctx, &r_o);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_Delete(p, mem_ctx, &acct_handle)) {
+ return false;
+ }
+
+ if (!test_DeleteObject(p, mem_ctx, &acct_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DeleteTrustedDomain(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_StringLarge name)
+{
+ NTSTATUS status;
+ struct lsa_OpenTrustedDomainByName r;
+ struct policy_handle trustdom_handle;
+
+ r.in.handle = handle;
+ r.in.name.string = name.string;
+ r.in.access_mask = SEC_STD_DELETE;
+ r.out.trustdom_handle = &trustdom_handle;
+
+ status = dcerpc_lsa_OpenTrustedDomainByName(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lsa_OpenTrustedDomainByName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_Delete(p, mem_ctx, &trustdom_handle)) {
+ return false;
+ }
+
+ if (!test_DeleteObject(p, mem_ctx, &trustdom_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DeleteTrustedDomainBySid(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct dom_sid *sid)
+{
+ NTSTATUS status;
+ struct lsa_DeleteTrustedDomain r;
+
+ r.in.handle = handle;
+ r.in.dom_sid = sid;
+
+ status = dcerpc_lsa_DeleteTrustedDomain(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lsa_DeleteTrustedDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_CreateSecret(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_CreateSecret r;
+ struct lsa_OpenSecret r2;
+ struct lsa_SetSecret r3;
+ struct lsa_QuerySecret r4;
+ struct lsa_SetSecret r5;
+ struct lsa_QuerySecret r6;
+ struct lsa_SetSecret r7;
+ struct lsa_QuerySecret r8;
+ struct policy_handle sec_handle, sec_handle2, sec_handle3;
+ struct lsa_DeleteObject d_o;
+ struct lsa_DATA_BUF buf1;
+ struct lsa_DATA_BUF_PTR bufp1;
+ struct lsa_DATA_BUF_PTR bufp2;
+ DATA_BLOB enc_key;
+ bool ret = true;
+ DATA_BLOB session_key;
+ NTTIME old_mtime, new_mtime;
+ DATA_BLOB blob1, blob2;
+ const char *secret1 = "abcdef12345699qwerty";
+ char *secret2;
+ const char *secret3 = "ABCDEF12345699QWERTY";
+ char *secret4;
+ const char *secret5 = "NEW-SAMBA4-SECRET";
+ char *secret6;
+ char *secname[2];
+ int i;
+ const int LOCAL = 0;
+ const int GLOBAL = 1;
+
+ secname[LOCAL] = talloc_asprintf(mem_ctx, "torturesecret-%u", (uint_t)random());
+ secname[GLOBAL] = talloc_asprintf(mem_ctx, "G$torturesecret-%u", (uint_t)random());
+
+ for (i=0; i< 2; i++) {
+ printf("Testing CreateSecret of %s\n", secname[i]);
+
+ init_lsa_String(&r.in.name, secname[i]);
+
+ r.in.handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.sec_handle = &sec_handle;
+
+ status = dcerpc_lsa_CreateSecret(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateSecret failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r.in.handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.sec_handle = &sec_handle3;
+
+ status = dcerpc_lsa_CreateSecret(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ printf("CreateSecret should have failed OBJECT_NAME_COLLISION - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r2.in.handle = handle;
+ r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r2.in.name = r.in.name;
+ r2.out.sec_handle = &sec_handle2;
+
+ printf("Testing OpenSecret\n");
+
+ status = dcerpc_lsa_OpenSecret(p, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenSecret failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_fetch_session_key failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ enc_key = sess_encrypt_string(secret1, &session_key);
+
+ r3.in.sec_handle = &sec_handle;
+ r3.in.new_val = &buf1;
+ r3.in.old_val = NULL;
+ r3.in.new_val->data = enc_key.data;
+ r3.in.new_val->length = enc_key.length;
+ r3.in.new_val->size = enc_key.length;
+
+ printf("Testing SetSecret\n");
+
+ status = dcerpc_lsa_SetSecret(p, mem_ctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetSecret failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r3.in.sec_handle = &sec_handle;
+ r3.in.new_val = &buf1;
+ r3.in.old_val = NULL;
+ r3.in.new_val->data = enc_key.data;
+ r3.in.new_val->length = enc_key.length;
+ r3.in.new_val->size = enc_key.length;
+
+ /* break the encrypted data */
+ enc_key.data[0]++;
+
+ printf("Testing SetSecret with broken key\n");
+
+ status = dcerpc_lsa_SetSecret(p, mem_ctx, &r3);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_UNKNOWN_REVISION)) {
+ printf("SetSecret should have failed UNKNOWN_REVISION - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ data_blob_free(&enc_key);
+
+ ZERO_STRUCT(new_mtime);
+ ZERO_STRUCT(old_mtime);
+
+ /* fetch the secret back again */
+ r4.in.sec_handle = &sec_handle;
+ r4.in.new_val = &bufp1;
+ r4.in.new_mtime = &new_mtime;
+ r4.in.old_val = NULL;
+ r4.in.old_mtime = NULL;
+
+ bufp1.buf = NULL;
+
+ printf("Testing QuerySecret\n");
+ status = dcerpc_lsa_QuerySecret(p, mem_ctx, &r4);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecret failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (r4.out.new_val == NULL || r4.out.new_val->buf == NULL) {
+ printf("No secret buffer returned\n");
+ ret = false;
+ } else {
+ blob1.data = r4.out.new_val->buf->data;
+ blob1.length = r4.out.new_val->buf->size;
+
+ blob2 = data_blob_talloc(mem_ctx, NULL, blob1.length);
+
+ secret2 = sess_decrypt_string(mem_ctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret1, secret2) != 0) {
+ printf("Returned secret (r4) '%s' doesn't match '%s'\n",
+ secret2, secret1);
+ ret = false;
+ }
+ }
+ }
+
+ enc_key = sess_encrypt_string(secret3, &session_key);
+
+ r5.in.sec_handle = &sec_handle;
+ r5.in.new_val = &buf1;
+ r5.in.old_val = NULL;
+ r5.in.new_val->data = enc_key.data;
+ r5.in.new_val->length = enc_key.length;
+ r5.in.new_val->size = enc_key.length;
+
+
+ msleep(200);
+ printf("Testing SetSecret (existing value should move to old)\n");
+
+ status = dcerpc_lsa_SetSecret(p, mem_ctx, &r5);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetSecret failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ data_blob_free(&enc_key);
+
+ ZERO_STRUCT(new_mtime);
+ ZERO_STRUCT(old_mtime);
+
+ /* fetch the secret back again */
+ r6.in.sec_handle = &sec_handle;
+ r6.in.new_val = &bufp1;
+ r6.in.new_mtime = &new_mtime;
+ r6.in.old_val = &bufp2;
+ r6.in.old_mtime = &old_mtime;
+
+ bufp1.buf = NULL;
+ bufp2.buf = NULL;
+
+ status = dcerpc_lsa_QuerySecret(p, mem_ctx, &r6);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecret failed - %s\n", nt_errstr(status));
+ ret = false;
+ secret4 = NULL;
+ } else {
+
+ if (r6.out.new_val->buf == NULL || r6.out.old_val->buf == NULL
+ || r6.out.new_mtime == NULL || r6.out.old_mtime == NULL) {
+ printf("Both secret buffers and both times not returned\n");
+ ret = false;
+ secret4 = NULL;
+ } else {
+ blob1.data = r6.out.new_val->buf->data;
+ blob1.length = r6.out.new_val->buf->size;
+
+ blob2 = data_blob_talloc(mem_ctx, NULL, blob1.length);
+
+ secret4 = sess_decrypt_string(mem_ctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret3, secret4) != 0) {
+ printf("Returned NEW secret %s doesn't match %s\n", secret4, secret3);
+ ret = false;
+ }
+
+ blob1.data = r6.out.old_val->buf->data;
+ blob1.length = r6.out.old_val->buf->length;
+
+ blob2 = data_blob_talloc(mem_ctx, NULL, blob1.length);
+
+ secret2 = sess_decrypt_string(mem_ctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret1, secret2) != 0) {
+ printf("Returned OLD secret %s doesn't match %s\n", secret2, secret1);
+ ret = false;
+ }
+
+ if (*r6.out.new_mtime == *r6.out.old_mtime) {
+ printf("Returned secret (r6-%d) %s must not have same mtime for both secrets: %s != %s\n",
+ i,
+ secname[i],
+ nt_time_string(mem_ctx, *r6.out.old_mtime),
+ nt_time_string(mem_ctx, *r6.out.new_mtime));
+ ret = false;
+ }
+ }
+ }
+
+ enc_key = sess_encrypt_string(secret5, &session_key);
+
+ r7.in.sec_handle = &sec_handle;
+ r7.in.old_val = &buf1;
+ r7.in.old_val->data = enc_key.data;
+ r7.in.old_val->length = enc_key.length;
+ r7.in.old_val->size = enc_key.length;
+ r7.in.new_val = NULL;
+
+ printf("Testing SetSecret of old Secret only\n");
+
+ status = dcerpc_lsa_SetSecret(p, mem_ctx, &r7);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetSecret failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ data_blob_free(&enc_key);
+
+ /* fetch the secret back again */
+ r8.in.sec_handle = &sec_handle;
+ r8.in.new_val = &bufp1;
+ r8.in.new_mtime = &new_mtime;
+ r8.in.old_val = &bufp2;
+ r8.in.old_mtime = &old_mtime;
+
+ bufp1.buf = NULL;
+ bufp2.buf = NULL;
+
+ status = dcerpc_lsa_QuerySecret(p, mem_ctx, &r8);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecret failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (!r8.out.new_val || !r8.out.old_val) {
+ printf("in/out pointers not returned, despite being set on in for QuerySecret\n");
+ ret = false;
+ } else if (r8.out.new_val->buf != NULL) {
+ printf("NEW secret buffer must not be returned after OLD set\n");
+ ret = false;
+ } else if (r8.out.old_val->buf == NULL) {
+ printf("OLD secret buffer was not returned after OLD set\n");
+ ret = false;
+ } else if (r8.out.new_mtime == NULL || r8.out.old_mtime == NULL) {
+ printf("Both times not returned after OLD set\n");
+ ret = false;
+ } else {
+ blob1.data = r8.out.old_val->buf->data;
+ blob1.length = r8.out.old_val->buf->size;
+
+ blob2 = data_blob_talloc(mem_ctx, NULL, blob1.length);
+
+ secret6 = sess_decrypt_string(mem_ctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret5, secret6) != 0) {
+ printf("Returned OLD secret %s doesn't match %s\n", secret5, secret6);
+ ret = false;
+ }
+
+ if (*r8.out.new_mtime != *r8.out.old_mtime) {
+ printf("Returned secret (r8) %s did not had same mtime for both secrets: %s != %s\n",
+ secname[i],
+ nt_time_string(mem_ctx, *r8.out.old_mtime),
+ nt_time_string(mem_ctx, *r8.out.new_mtime));
+ ret = false;
+ }
+ }
+ }
+
+ if (!test_Delete(p, mem_ctx, &sec_handle)) {
+ ret = false;
+ }
+
+ if (!test_DeleteObject(p, mem_ctx, &sec_handle)) {
+ return false;
+ }
+
+ d_o.in.handle = &sec_handle2;
+ d_o.out.handle = &sec_handle2;
+ status = dcerpc_lsa_DeleteObject(p, mem_ctx, &d_o);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ printf("Second delete expected INVALID_HANDLE - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+
+ printf("Testing OpenSecret of just-deleted secret\n");
+
+ status = dcerpc_lsa_OpenSecret(p, mem_ctx, &r2);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("OpenSecret expected OBJECT_NAME_NOT_FOUND - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ }
+
+ return ret;
+}
+
+
+static bool test_EnumAccountRights(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *acct_handle,
+ struct dom_sid *sid)
+{
+ NTSTATUS status;
+ struct lsa_EnumAccountRights r;
+ struct lsa_RightSet rights;
+
+ printf("Testing EnumAccountRights\n");
+
+ r.in.handle = acct_handle;
+ r.in.sid = sid;
+ r.out.rights = &rights;
+
+ status = dcerpc_lsa_EnumAccountRights(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumAccountRights of %s failed - %s\n",
+ dom_sid_string(mem_ctx, sid), nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_QuerySecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle)
+{
+ NTSTATUS status;
+ struct lsa_QuerySecurity r;
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping QuerySecurity test against Samba4\n");
+ return true;
+ }
+
+ printf("Testing QuerySecurity\n");
+
+ r.in.handle = acct_handle;
+ r.in.sec_info = 7;
+
+ status = dcerpc_lsa_QuerySecurity(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_OpenAccount(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct dom_sid *sid)
+{
+ NTSTATUS status;
+ struct lsa_OpenAccount r;
+ struct policy_handle acct_handle;
+
+ printf("Testing OpenAccount\n");
+
+ r.in.handle = handle;
+ r.in.sid = sid;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.acct_handle = &acct_handle;
+
+ status = dcerpc_lsa_OpenAccount(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_EnumPrivsAccount(p, mem_ctx, handle, &acct_handle)) {
+ return false;
+ }
+
+ if (!test_QuerySecurity(p, mem_ctx, handle, &acct_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_EnumAccounts(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_EnumAccounts r;
+ struct lsa_SidArray sids1, sids2;
+ uint32_t resume_handle = 0;
+ int i;
+ bool ret = true;
+
+ printf("\ntesting EnumAccounts\n");
+
+ r.in.handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.num_entries = 100;
+ r.out.resume_handle = &resume_handle;
+ r.out.sids = &sids1;
+
+ resume_handle = 0;
+ while (true) {
+ status = dcerpc_lsa_EnumAccounts(p, mem_ctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ break;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumAccounts failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_LookupSids(p, mem_ctx, handle, &sids1)) {
+ return false;
+ }
+
+ if (!test_LookupSids2(p, mem_ctx, handle, &sids1)) {
+ return false;
+ }
+
+ /* Can't test lookupSids3 here, as clearly we must not
+ * be on schannel, or we would not be able to do the
+ * rest */
+
+ printf("testing all accounts\n");
+ for (i=0;i<sids1.num_sids;i++) {
+ ret &= test_OpenAccount(p, mem_ctx, handle, sids1.sids[i].sid);
+ ret &= test_EnumAccountRights(p, mem_ctx, handle, sids1.sids[i].sid);
+ }
+ printf("\n");
+ }
+
+ if (sids1.num_sids < 3) {
+ return ret;
+ }
+
+ printf("trying EnumAccounts partial listing (asking for 1 at 2)\n");
+ resume_handle = 2;
+ r.in.num_entries = 1;
+ r.out.sids = &sids2;
+
+ status = dcerpc_lsa_EnumAccounts(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumAccounts failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (sids2.num_sids != 1) {
+ printf("Returned wrong number of entries (%d)\n", sids2.num_sids);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_LookupPrivDisplayName(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_String *priv_name)
+{
+ struct lsa_LookupPrivDisplayName r;
+ NTSTATUS status;
+ /* produce a reasonable range of language output without screwing up
+ terminals */
+ uint16_t language_id = (random() % 4) + 0x409;
+
+ printf("testing LookupPrivDisplayName(%s)\n", priv_name->string);
+
+ r.in.handle = handle;
+ r.in.name = priv_name;
+ r.in.language_id = &language_id;
+ r.out.language_id = &language_id;
+ r.in.unknown = 0;
+
+ status = dcerpc_lsa_LookupPrivDisplayName(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupPrivDisplayName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ printf("%s -> \"%s\" (language 0x%x/0x%x)\n",
+ priv_name->string, r.out.disp_name->string,
+ *r.in.language_id, *r.out.language_id);
+
+ return true;
+}
+
+static bool test_EnumAccountsWithUserRight(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_String *priv_name)
+{
+ struct lsa_EnumAccountsWithUserRight r;
+ struct lsa_SidArray sids;
+ NTSTATUS status;
+
+ ZERO_STRUCT(sids);
+
+ printf("testing EnumAccountsWithUserRight(%s)\n", priv_name->string);
+
+ r.in.handle = handle;
+ r.in.name = priv_name;
+ r.out.sids = &sids;
+
+ status = dcerpc_lsa_EnumAccountsWithUserRight(p, mem_ctx, &r);
+
+ /* NT_STATUS_NO_MORE_ENTRIES means noone has this privilege */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ return true;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumAccountsWithUserRight failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_EnumPrivs(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_EnumPrivs r;
+ struct lsa_PrivArray privs1;
+ uint32_t resume_handle = 0;
+ int i;
+ bool ret = true;
+
+ printf("\ntesting EnumPrivs\n");
+
+ r.in.handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_count = 100;
+ r.out.resume_handle = &resume_handle;
+ r.out.privs = &privs1;
+
+ resume_handle = 0;
+ status = dcerpc_lsa_EnumPrivs(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumPrivs failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i = 0; i< privs1.count; i++) {
+ test_LookupPrivDisplayName(p, mem_ctx, handle, (struct lsa_String *)&privs1.privs[i].name);
+ test_LookupPrivValue(p, mem_ctx, handle, (struct lsa_String *)&privs1.privs[i].name);
+ if (!test_EnumAccountsWithUserRight(p, mem_ctx, handle, (struct lsa_String *)&privs1.privs[i].name)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryForestTrustInformation(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *trusted_domain_name)
+{
+ bool ret = true;
+ struct lsa_lsaRQueryForestTrustInformation r;
+ NTSTATUS status;
+ struct lsa_String string;
+ struct lsa_ForestTrustInformation info, *info_ptr;
+
+ printf("\nTesting lsaRQueryForestTrustInformation\n");
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping QueryForestTrustInformation against Samba4\n");
+ return true;
+ }
+
+ ZERO_STRUCT(string);
+
+ if (trusted_domain_name) {
+ init_lsa_String(&string, trusted_domain_name);
+ }
+
+ info_ptr = &info;
+
+ r.in.handle = handle;
+ r.in.trusted_domain_name = &string;
+ r.in.unknown = 0;
+ r.out.forest_trust_info = &info_ptr;
+
+ status = dcerpc_lsa_lsaRQueryForestTrustInformation(p, tctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("lsaRQueryForestTrustInformation failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_query_each_TrustDomEx(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_DomainListEx *domains)
+{
+ int i;
+ bool ret = true;
+
+ for (i=0; i< domains->count; i++) {
+
+ if (domains->domains[i].trust_attributes & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ ret &= test_QueryForestTrustInformation(p, mem_ctx, handle,
+ domains->domains[i].domain_name.string);
+ }
+ }
+
+ return ret;
+}
+
+static bool test_query_each_TrustDom(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle,
+ struct lsa_DomainList *domains)
+{
+ NTSTATUS status;
+ int i,j;
+ bool ret = true;
+
+ printf("\nTesting OpenTrustedDomain, OpenTrustedDomainByName and QueryInfoTrustedDomain\n");
+ for (i=0; i< domains->count; i++) {
+ struct lsa_OpenTrustedDomain trust;
+ struct lsa_OpenTrustedDomainByName trust_by_name;
+ struct policy_handle trustdom_handle;
+ struct policy_handle handle2;
+ struct lsa_Close c;
+ struct lsa_CloseTrustedDomainEx c_trust;
+ int levels [] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
+ int ok[] = {1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1};
+
+ if (domains->domains[i].sid) {
+ trust.in.handle = handle;
+ trust.in.sid = domains->domains[i].sid;
+ trust.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ trust.out.trustdom_handle = &trustdom_handle;
+
+ status = dcerpc_lsa_OpenTrustedDomain(p, mem_ctx, &trust);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenTrustedDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &handle2;
+
+ c_trust.in.handle = &trustdom_handle;
+ c_trust.out.handle = &handle2;
+
+ for (j=0; j < ARRAY_SIZE(levels); j++) {
+ struct lsa_QueryTrustedDomainInfo q;
+ union lsa_TrustedDomainInfo info;
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = levels[j];
+ q.out.info = &info;
+ status = dcerpc_lsa_QueryTrustedDomainInfo(p, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status) && ok[j]) {
+ printf("QueryTrustedDomainInfo level %d failed - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(status) && !ok[j]) {
+ printf("QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ status = dcerpc_lsa_CloseTrustedDomainEx(p, mem_ctx, &c_trust);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) {
+ printf("Expected CloseTrustedDomainEx to return NT_STATUS_NOT_IMPLEMENTED, instead - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &handle2;
+
+ status = dcerpc_lsa_Close(p, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close of trusted domain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (j=0; j < ARRAY_SIZE(levels); j++) {
+ struct lsa_QueryTrustedDomainInfoBySid q;
+ union lsa_TrustedDomainInfo info;
+
+ if (!domains->domains[i].sid) {
+ continue;
+ }
+
+ q.in.handle = handle;
+ q.in.dom_sid = domains->domains[i].sid;
+ q.in.level = levels[j];
+ q.out.info = &info;
+ status = dcerpc_lsa_QueryTrustedDomainInfoBySid(p, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status) && ok[j]) {
+ printf("QueryTrustedDomainInfoBySid level %d failed - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(status) && !ok[j]) {
+ printf("QueryTrustedDomainInfoBySid level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ }
+ }
+ }
+
+ trust_by_name.in.handle = handle;
+ trust_by_name.in.name.string = domains->domains[i].name.string;
+ trust_by_name.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ trust_by_name.out.trustdom_handle = &trustdom_handle;
+
+ status = dcerpc_lsa_OpenTrustedDomainByName(p, mem_ctx, &trust_by_name);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenTrustedDomainByName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (j=0; j < ARRAY_SIZE(levels); j++) {
+ struct lsa_QueryTrustedDomainInfo q;
+ union lsa_TrustedDomainInfo info;
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = levels[j];
+ q.out.info = &info;
+ status = dcerpc_lsa_QueryTrustedDomainInfo(p, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status) && ok[j]) {
+ printf("QueryTrustedDomainInfo level %d failed - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(status) && !ok[j]) {
+ printf("QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &handle2;
+
+ status = dcerpc_lsa_Close(p, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close of trusted domain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (j=0; j < ARRAY_SIZE(levels); j++) {
+ struct lsa_QueryTrustedDomainInfoByName q;
+ union lsa_TrustedDomainInfo info;
+ q.in.handle = handle;
+ q.in.trusted_domain.string = domains->domains[i].name.string;
+ q.in.level = levels[j];
+ q.out.info = &info;
+ status = dcerpc_lsa_QueryTrustedDomainInfoByName(p, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status) && ok[j]) {
+ printf("QueryTrustedDomainInfoByName level %d failed - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(status) && !ok[j]) {
+ printf("QueryTrustedDomainInfoByName level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(status));
+ ret = false;
+ }
+ }
+ }
+ return ret;
+}
+
+static bool test_EnumTrustDom(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ struct lsa_EnumTrustDom r;
+ struct lsa_EnumTrustedDomainsEx r_ex;
+ NTSTATUS enum_status;
+ uint32_t resume_handle = 0;
+ struct lsa_DomainList domains;
+ struct lsa_DomainListEx domains_ex;
+ bool ret = true;
+
+ printf("\nTesting EnumTrustDom\n");
+
+ r.in.handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = 0;
+ r.out.domains = &domains;
+ r.out.resume_handle = &resume_handle;
+
+ enum_status = dcerpc_lsa_EnumTrustDom(p, mem_ctx, &r);
+
+ if (NT_STATUS_IS_OK(enum_status)) {
+ if (domains.count == 0) {
+ printf("EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+ } else if (!(NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES))) {
+ printf("EnumTrustDom of zero size failed - %s\n", nt_errstr(enum_status));
+ return false;
+ }
+
+ /* Start from the bottom again */
+ resume_handle = 0;
+
+ do {
+ r.in.handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = LSA_ENUM_TRUST_DOMAIN_MULTIPLIER * 3;
+ r.out.domains = &domains;
+ r.out.resume_handle = &resume_handle;
+
+ enum_status = dcerpc_lsa_EnumTrustDom(p, mem_ctx, &r);
+
+ /* NO_MORE_ENTRIES is allowed */
+ if (NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES)) {
+ return true;
+ } else if (NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES)) {
+ /* Windows 2003 gets this off by one on the first run */
+ if (r.out.domains->count < 3 || r.out.domains->count > 4) {
+ printf("EnumTrustDom didn't fill the buffer we "
+ "asked it to (got %d, expected %d / %d == %d entries)\n",
+ r.out.domains->count, LSA_ENUM_TRUST_DOMAIN_MULTIPLIER * 3,
+ LSA_ENUM_TRUST_DOMAIN_MULTIPLIER, r.in.max_size);
+ ret = false;
+ }
+ } else if (!NT_STATUS_IS_OK(enum_status)) {
+ printf("EnumTrustDom failed - %s\n", nt_errstr(enum_status));
+ return false;
+ }
+
+ if (domains.count == 0) {
+ printf("EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+
+ ret &= test_query_each_TrustDom(p, mem_ctx, handle, &domains);
+
+ } while ((NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES)));
+
+ printf("\nTesting EnumTrustedDomainsEx\n");
+
+ r_ex.in.handle = handle;
+ r_ex.in.resume_handle = &resume_handle;
+ r_ex.in.max_size = LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER * 3;
+ r_ex.out.domains = &domains_ex;
+ r_ex.out.resume_handle = &resume_handle;
+
+ enum_status = dcerpc_lsa_EnumTrustedDomainsEx(p, mem_ctx, &r_ex);
+
+ if (!(NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES))) {
+ printf("EnumTrustedDomainEx of zero size failed - %s\n", nt_errstr(enum_status));
+ return false;
+ }
+
+ resume_handle = 0;
+ do {
+ r_ex.in.handle = handle;
+ r_ex.in.resume_handle = &resume_handle;
+ r_ex.in.max_size = LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER * 3;
+ r_ex.out.domains = &domains_ex;
+ r_ex.out.resume_handle = &resume_handle;
+
+ enum_status = dcerpc_lsa_EnumTrustedDomainsEx(p, mem_ctx, &r_ex);
+
+ /* NO_MORE_ENTRIES is allowed */
+ if (NT_STATUS_EQUAL(enum_status, NT_STATUS_NO_MORE_ENTRIES)) {
+ return true;
+ } else if (NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES)) {
+ /* Windows 2003 gets this off by one on the first run */
+ if (r_ex.out.domains->count < 3 || r_ex.out.domains->count > 4) {
+ printf("EnumTrustDom didn't fill the buffer we "
+ "asked it to (got %d, expected %d / %d == %d entries)\n",
+ r_ex.out.domains->count,
+ r_ex.in.max_size,
+ LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER,
+ r_ex.in.max_size / LSA_ENUM_TRUST_DOMAIN_EX_MULTIPLIER);
+ }
+ } else if (!NT_STATUS_IS_OK(enum_status)) {
+ printf("EnumTrustedDomainEx failed - %s\n", nt_errstr(enum_status));
+ return false;
+ }
+
+ if (domains_ex.count == 0) {
+ printf("EnumTrustDomainEx failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+
+ ret &= test_query_each_TrustDomEx(p, mem_ctx, handle, &domains_ex);
+
+ } while ((NT_STATUS_EQUAL(enum_status, STATUS_MORE_ENTRIES)));
+
+ return ret;
+}
+
+static bool test_CreateTrustedDomain(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_CreateTrustedDomain r;
+ struct lsa_DomainInfo trustinfo;
+ struct dom_sid *domsid[12];
+ struct policy_handle trustdom_handle[12];
+ struct lsa_QueryTrustedDomainInfo q;
+ int i;
+
+ printf("Testing CreateTrustedDomain for 12 domains\n");
+
+ if (!test_EnumTrustDom(p, mem_ctx, handle)) {
+ ret = false;
+ }
+
+ for (i=0; i< 12; i++) {
+ char *trust_name = talloc_asprintf(mem_ctx, "torturedom%02d", i);
+ char *trust_sid = talloc_asprintf(mem_ctx, "S-1-5-21-97398-379795-100%02d", i);
+
+ domsid[i] = dom_sid_parse_talloc(mem_ctx, trust_sid);
+
+ trustinfo.sid = domsid[i];
+ init_lsa_String((struct lsa_String *)&trustinfo.name, trust_name);
+
+ r.in.policy_handle = handle;
+ r.in.info = &trustinfo;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.trustdom_handle = &trustdom_handle[i];
+
+ status = dcerpc_lsa_CreateTrustedDomain(p, mem_ctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ test_DeleteTrustedDomain(p, mem_ctx, handle, trustinfo.name);
+ status = dcerpc_lsa_CreateTrustedDomain(p, mem_ctx, &r);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateTrustedDomain failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+
+ q.in.trustdom_handle = &trustdom_handle[i];
+ q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX;
+ status = dcerpc_lsa_QueryTrustedDomainInfo(p, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryTrustedDomainInfo level 1 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else if (!q.out.info) {
+ ret = false;
+ } else {
+ if (strcmp(q.out.info->info_ex.netbios_name.string, trustinfo.name.string) != 0) {
+ printf("QueryTrustedDomainInfo returned inconsistant short name: %s != %s\n",
+ q.out.info->info_ex.netbios_name.string, trustinfo.name.string);
+ ret = false;
+ }
+ if (q.out.info->info_ex.trust_type != LSA_TRUST_TYPE_DOWNLEVEL) {
+ printf("QueryTrustedDomainInfo of %s returned incorrect trust type %d != %d\n",
+ trust_name, q.out.info->info_ex.trust_type, LSA_TRUST_TYPE_DOWNLEVEL);
+ ret = false;
+ }
+ if (q.out.info->info_ex.trust_attributes != 0) {
+ printf("QueryTrustedDomainInfo of %s returned incorrect trust attributes %d != %d\n",
+ trust_name, q.out.info->info_ex.trust_attributes, 0);
+ ret = false;
+ }
+ if (q.out.info->info_ex.trust_direction != LSA_TRUST_DIRECTION_OUTBOUND) {
+ printf("QueryTrustedDomainInfo of %s returned incorrect trust direction %d != %d\n",
+ trust_name, q.out.info->info_ex.trust_direction, LSA_TRUST_DIRECTION_OUTBOUND);
+ ret = false;
+ }
+ }
+ }
+ }
+
+ /* now that we have some domains to look over, we can test the enum calls */
+ if (!test_EnumTrustDom(p, mem_ctx, handle)) {
+ ret = false;
+ }
+
+ for (i=0; i<12; i++) {
+ if (!test_DeleteTrustedDomainBySid(p, mem_ctx, handle, domsid[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_CreateTrustedDomainEx2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_CreateTrustedDomainEx2 r;
+ struct lsa_TrustDomainInfoInfoEx trustinfo;
+ struct lsa_TrustDomainInfoAuthInfoInternal authinfo;
+ struct trustAuthInAndOutBlob auth_struct;
+ DATA_BLOB auth_blob;
+ struct dom_sid *domsid[12];
+ struct policy_handle trustdom_handle[12];
+ struct lsa_QueryTrustedDomainInfo q;
+ DATA_BLOB session_key;
+ enum ndr_err_code ndr_err;
+ int i;
+
+ printf("Testing CreateTrustedDomainEx2 for 12 domains\n");
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_fetch_session_key failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i=0; i< 12; i++) {
+ char *trust_name = talloc_asprintf(mem_ctx, "torturedom%02d", i);
+ char *trust_name_dns = talloc_asprintf(mem_ctx, "torturedom%02d.samba.example.com", i);
+ char *trust_sid = talloc_asprintf(mem_ctx, "S-1-5-21-97398-379795-100%02d", i);
+
+ domsid[i] = dom_sid_parse_talloc(mem_ctx, trust_sid);
+
+ trustinfo.sid = domsid[i];
+ trustinfo.netbios_name.string = trust_name;
+ trustinfo.domain_name.string = trust_name_dns;
+
+ /* Create inbound, some outbound, and some
+ * bi-directional trusts in a repeating pattern based
+ * on i */
+
+ /* 1 == inbound, 2 == outbound, 3 == both */
+ trustinfo.trust_direction = (i % 3) + 1;
+
+ /* Try different trust types too */
+
+ /* 1 == downleven (NT4), 2 == uplevel (ADS), 3 == MIT (kerberos but not AD) */
+ trustinfo.trust_type = (((i / 3) + 1) % 3) + 1;
+
+ trustinfo.trust_attributes = LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION;
+
+ generate_random_buffer(auth_struct.confounder, sizeof(auth_struct.confounder));
+
+ auth_struct.outgoing.count = 0;
+ auth_struct.incoming.count = 0;
+
+ ndr_err = ndr_push_struct_blob(&auth_blob, mem_ctx, lp_iconv_convenience(tctx->lp_ctx), &auth_struct,
+ (ndr_push_flags_fn_t)ndr_push_trustAuthInAndOutBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ printf("ndr_push_struct_blob of trustAuthInAndOutBlob structure failed");
+ ret = false;
+ }
+
+ arcfour_crypt_blob(auth_blob.data, auth_blob.length, &session_key);
+
+ authinfo.auth_blob.size = auth_blob.length;
+ authinfo.auth_blob.data = auth_blob.data;
+
+ r.in.policy_handle = handle;
+ r.in.info = &trustinfo;
+ r.in.auth_info = &authinfo;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.trustdom_handle = &trustdom_handle[i];
+
+ status = dcerpc_lsa_CreateTrustedDomainEx2(p, mem_ctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ test_DeleteTrustedDomain(p, mem_ctx, handle, trustinfo.netbios_name);
+ status = dcerpc_lsa_CreateTrustedDomainEx2(p, mem_ctx, &r);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateTrustedDomainEx failed2 - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+
+ q.in.trustdom_handle = &trustdom_handle[i];
+ q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX;
+ status = dcerpc_lsa_QueryTrustedDomainInfo(p, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryTrustedDomainInfo level 1 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else if (!q.out.info) {
+ ret = false;
+ } else {
+ if (strcmp(q.out.info->info_ex.netbios_name.string, trustinfo.netbios_name.string) != 0) {
+ printf("QueryTrustedDomainInfo returned inconsistant short name: %s != %s\n",
+ q.out.info->info_ex.netbios_name.string, trustinfo.netbios_name.string);
+ ret = false;
+ }
+ if (q.out.info->info_ex.trust_type != trustinfo.trust_type) {
+ printf("QueryTrustedDomainInfo of %s returned incorrect trust type %d != %d\n",
+ trust_name, q.out.info->info_ex.trust_type, trustinfo.trust_type);
+ ret = false;
+ }
+ if (q.out.info->info_ex.trust_attributes != LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION) {
+ printf("QueryTrustedDomainInfo of %s returned incorrect trust attributes %d != %d\n",
+ trust_name, q.out.info->info_ex.trust_attributes, LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION);
+ ret = false;
+ }
+ if (q.out.info->info_ex.trust_direction != trustinfo.trust_direction) {
+ printf("QueryTrustedDomainInfo of %s returned incorrect trust direction %d != %d\n",
+ trust_name, q.out.info->info_ex.trust_direction, trustinfo.trust_direction);
+ ret = false;
+ }
+ }
+ }
+ }
+
+ /* now that we have some domains to look over, we can test the enum calls */
+ if (!test_EnumTrustDom(p, mem_ctx, handle)) {
+ ret = false;
+ }
+
+ for (i=0; i<12; i++) {
+ if (!test_DeleteTrustedDomainBySid(p, mem_ctx, handle, domsid[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryDomainInfoPolicy(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_QueryDomainInformationPolicy r;
+ NTSTATUS status;
+ int i;
+ bool ret = true;
+
+ printf("\nTesting QueryDomainInformationPolicy\n");
+
+ for (i=2;i<4;i++) {
+ r.in.handle = handle;
+ r.in.level = i;
+
+ printf("\ntrying QueryDomainInformationPolicy level %d\n", i);
+
+ status = dcerpc_lsa_QueryDomainInformationPolicy(p, tctx, &r);
+
+ /* If the server does not support EFS, then this is the correct return */
+ if (i == LSA_DOMAIN_INFO_POLICY_EFS && NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ continue;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInformationPolicy failed - %s\n", nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_QueryInfoPolicy(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_QueryInfoPolicy r;
+ NTSTATUS status;
+ int i;
+ bool ret = true;
+ printf("\nTesting QueryInfoPolicy\n");
+
+ for (i=1;i<13;i++) {
+ r.in.handle = handle;
+ r.in.level = i;
+
+ printf("\ntrying QueryInfoPolicy level %d\n", i);
+
+ status = dcerpc_lsa_QueryInfoPolicy(p, tctx, &r);
+
+ switch (i) {
+ case LSA_POLICY_INFO_DB:
+ case LSA_POLICY_INFO_AUDIT_FULL_SET:
+ case LSA_POLICY_INFO_AUDIT_FULL_QUERY:
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("server should have failed level %u: %s\n", i, nt_errstr(status));
+ ret = false;
+ }
+ break;
+ case LSA_POLICY_INFO_DOMAIN:
+ case LSA_POLICY_INFO_ACCOUNT_DOMAIN:
+ case LSA_POLICY_INFO_DNS:
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryInfoPolicy failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ break;
+ default:
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ /* Other levels not implemented yet */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
+ printf("QueryInfoPolicy failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryInfoPolicy failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(status) && i == LSA_POLICY_INFO_DNS) {
+ /* Let's look up some of these names */
+
+ struct lsa_TransNameArray tnames;
+ tnames.count = 14;
+ tnames.names = talloc_zero_array(tctx, struct lsa_TranslatedName, tnames.count);
+ tnames.names[0].name.string = r.out.info->dns.name.string;
+ tnames.names[0].sid_type = SID_NAME_DOMAIN;
+ tnames.names[1].name.string = r.out.info->dns.dns_domain.string;
+ tnames.names[1].sid_type = SID_NAME_DOMAIN;
+ tnames.names[2].name.string = talloc_asprintf(tctx, "%s\\", r.out.info->dns.name.string);
+ tnames.names[2].sid_type = SID_NAME_DOMAIN;
+ tnames.names[3].name.string = talloc_asprintf(tctx, "%s\\", r.out.info->dns.dns_domain.string);
+ tnames.names[3].sid_type = SID_NAME_DOMAIN;
+ tnames.names[4].name.string = talloc_asprintf(tctx, "%s\\guest", r.out.info->dns.name.string);
+ tnames.names[4].sid_type = SID_NAME_USER;
+ tnames.names[5].name.string = talloc_asprintf(tctx, "%s\\krbtgt", r.out.info->dns.name.string);
+ tnames.names[5].sid_type = SID_NAME_USER;
+ tnames.names[6].name.string = talloc_asprintf(tctx, "%s\\guest", r.out.info->dns.dns_domain.string);
+ tnames.names[6].sid_type = SID_NAME_USER;
+ tnames.names[7].name.string = talloc_asprintf(tctx, "%s\\krbtgt", r.out.info->dns.dns_domain.string);
+ tnames.names[7].sid_type = SID_NAME_USER;
+ tnames.names[8].name.string = talloc_asprintf(tctx, "krbtgt@%s", r.out.info->dns.name.string);
+ tnames.names[8].sid_type = SID_NAME_USER;
+ tnames.names[9].name.string = talloc_asprintf(tctx, "krbtgt@%s", r.out.info->dns.dns_domain.string);
+ tnames.names[9].sid_type = SID_NAME_USER;
+ tnames.names[10].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", r.out.info->dns.name.string);
+ tnames.names[10].sid_type = SID_NAME_USER;
+ tnames.names[11].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", r.out.info->dns.dns_domain.string);
+ tnames.names[11].sid_type = SID_NAME_USER;
+ tnames.names[12].name.string = talloc_asprintf(tctx, TEST_MACHINENAME "$@%s", r.out.info->dns.name.string);
+ tnames.names[12].sid_type = SID_NAME_USER;
+ tnames.names[13].name.string = talloc_asprintf(tctx, TEST_MACHINENAME "$@%s", r.out.info->dns.dns_domain.string);
+ tnames.names[13].sid_type = SID_NAME_USER;
+ ret &= test_LookupNames(p, tctx, handle, &tnames);
+
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryInfoPolicy2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_QueryInfoPolicy2 r;
+ NTSTATUS status;
+ int i;
+ bool ret = true;
+ printf("\nTesting QueryInfoPolicy2\n");
+ for (i=1;i<13;i++) {
+ r.in.handle = handle;
+ r.in.level = i;
+
+ printf("\ntrying QueryInfoPolicy2 level %d\n", i);
+
+ status = dcerpc_lsa_QueryInfoPolicy2(p, tctx, &r);
+
+ switch (i) {
+ case LSA_POLICY_INFO_DB:
+ case LSA_POLICY_INFO_AUDIT_FULL_SET:
+ case LSA_POLICY_INFO_AUDIT_FULL_QUERY:
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("server should have failed level %u: %s\n", i, nt_errstr(status));
+ ret = false;
+ }
+ break;
+ case LSA_POLICY_INFO_DOMAIN:
+ case LSA_POLICY_INFO_ACCOUNT_DOMAIN:
+ case LSA_POLICY_INFO_DNS:
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryInfoPolicy2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ break;
+ default:
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ /* Other levels not implemented yet */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
+ printf("QueryInfoPolicy2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryInfoPolicy2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetUserName(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
+{
+ struct lsa_GetUserName r;
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_StringPointer authority_name_p;
+
+ printf("\nTesting GetUserName\n");
+
+ r.in.system_name = "\\";
+ r.in.account_name = NULL;
+ r.in.authority_name = &authority_name_p;
+ authority_name_p.string = NULL;
+
+ status = dcerpc_lsa_GetUserName(p, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetUserName failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool test_lsa_Close(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_Close r;
+ struct policy_handle handle2;
+
+ printf("\ntesting Close\n");
+
+ r.in.handle = handle;
+ r.out.handle = &handle2;
+
+ status = dcerpc_lsa_Close(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_lsa_Close(p, mem_ctx, &r);
+ /* its really a fault - we need a status code for rpc fault */
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("\n");
+
+ return true;
+}
+
+bool torture_rpc_lsa(struct torture_context *tctx)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle *handle;
+ struct test_join *join = NULL;
+ struct cli_credentials *machine_creds;
+
+ status = torture_rpc_connection(tctx, &p, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (!test_OpenPolicy(p, tctx)) {
+ ret = false;
+ }
+
+ if (!test_lsa_OpenPolicy2(p, tctx, &handle)) {
+ ret = false;
+ }
+
+ if (handle) {
+ join = torture_join_domain(tctx, TEST_MACHINENAME, ACB_WSTRUST, &machine_creds);
+ if (!join) {
+ ret = false;
+ }
+
+ if (!test_LookupNames_wellknown(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_LookupNames_bogus(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_LookupSids_async(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryDomainInfoPolicy(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_CreateAccount(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_CreateSecret(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_CreateTrustedDomain(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_CreateTrustedDomainEx2(p, tctx, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumAccounts(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrivs(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryInfoPolicy(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryInfoPolicy2(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_Delete(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_many_LookupSids(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_lsa_Close(p, tctx, handle)) {
+ ret = false;
+ }
+
+ torture_leave_domain(tctx, join);
+
+ } else {
+ if (!test_many_LookupSids(p, tctx, handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_GetUserName(p, tctx)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool torture_rpc_lsa_get_user(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+
+ mem_ctx = talloc_init("torture_rpc_lsa_get_user");
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (!test_GetUserName(p, mem_ctx)) {
+ ret = false;
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/lsa_lookup.c b/source4/torture/rpc/lsa_lookup.c
new file mode 100644
index 0000000000..9c817a7061
--- /dev/null
+++ b/source4/torture/rpc/lsa_lookup.c
@@ -0,0 +1,319 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for lsa rpc lookup operations
+
+ Copyright (C) Volker Lendecke 2006
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "lib/events/events.h"
+#include "libnet/libnet_join.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/security/security.h"
+
+static bool open_policy(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *p,
+ struct policy_handle **handle)
+{
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+ NTSTATUS status;
+
+ *handle = talloc(mem_ctx, struct policy_handle);
+ if (!*handle) {
+ return false;
+ }
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = "\\";
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = *handle;
+
+ status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &r);
+
+ return NT_STATUS_IS_OK(status);
+}
+
+static bool get_domainsid(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ struct dom_sid **sid)
+{
+ struct lsa_QueryInfoPolicy r;
+ NTSTATUS status;
+
+ r.in.level = LSA_POLICY_INFO_DOMAIN;
+ r.in.handle = handle;
+
+ status = dcerpc_lsa_QueryInfoPolicy(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) return false;
+
+ *sid = r.out.info->domain.sid;
+ return true;
+}
+
+static NTSTATUS lookup_sids(TALLOC_CTX *mem_ctx, uint16_t level,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ struct dom_sid **sids, uint32_t num_sids,
+ struct lsa_TransNameArray *names)
+{
+ struct lsa_LookupSids r;
+ struct lsa_SidArray sidarray;
+ uint32_t count = 0;
+ uint32_t i;
+
+ names->count = 0;
+ names->names = NULL;
+
+ sidarray.num_sids = num_sids;
+ sidarray.sids = talloc_array(mem_ctx, struct lsa_SidPtr, num_sids);
+
+ for (i=0; i<num_sids; i++) {
+ sidarray.sids[i].sid = sids[i];
+ }
+
+ r.in.handle = handle;
+ r.in.sids = &sidarray;
+ r.in.names = names;
+ r.in.level = level;
+ r.in.count = &count;
+ r.out.names = names;
+ r.out.count = &count;
+
+ return dcerpc_lsa_LookupSids(p, mem_ctx, &r);
+}
+
+static const char *sid_type_lookup(enum lsa_SidType r)
+{
+ switch (r) {
+ case SID_NAME_USE_NONE: return "SID_NAME_USE_NONE"; break;
+ case SID_NAME_USER: return "SID_NAME_USER"; break;
+ case SID_NAME_DOM_GRP: return "SID_NAME_DOM_GRP"; break;
+ case SID_NAME_DOMAIN: return "SID_NAME_DOMAIN"; break;
+ case SID_NAME_ALIAS: return "SID_NAME_ALIAS"; break;
+ case SID_NAME_WKN_GRP: return "SID_NAME_WKN_GRP"; break;
+ case SID_NAME_DELETED: return "SID_NAME_DELETED"; break;
+ case SID_NAME_INVALID: return "SID_NAME_INVALID"; break;
+ case SID_NAME_UNKNOWN: return "SID_NAME_UNKNOWN"; break;
+ case SID_NAME_COMPUTER: return "SID_NAME_COMPUTER"; break;
+ }
+ return "Invalid sid type\n";
+}
+
+static bool test_lookupsids(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ struct dom_sid **sids, uint32_t num_sids,
+ int level, NTSTATUS expected_result,
+ enum lsa_SidType *types)
+{
+ struct lsa_TransNameArray names;
+ NTSTATUS status;
+ uint32_t i;
+ bool ret = true;
+
+ status = lookup_sids(mem_ctx, level, p, handle, sids, num_sids,
+ &names);
+ if (!NT_STATUS_EQUAL(status, expected_result)) {
+ printf("For level %d expected %s, got %s\n",
+ level, nt_errstr(expected_result),
+ nt_errstr(status));
+ return false;
+ }
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OK) &&
+ !NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ return true;
+ }
+
+ for (i=0; i<num_sids; i++) {
+ if (names.names[i].sid_type != types[i]) {
+ printf("In level %d, for sid %s expected %s, "
+ "got %s\n", level,
+ dom_sid_string(mem_ctx, sids[i]),
+ sid_type_lookup(types[i]),
+ sid_type_lookup(names.names[i].sid_type));
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+static bool get_downleveltrust(struct torture_context *tctx, struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ struct dom_sid **sid)
+{
+ struct lsa_EnumTrustDom r;
+ uint32_t resume_handle = 0;
+ struct lsa_DomainList domains;
+ NTSTATUS status;
+ int i;
+
+ r.in.handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = 1000;
+ r.out.domains = &domains;
+ r.out.resume_handle = &resume_handle;
+
+ status = dcerpc_lsa_EnumTrustDom(p, tctx, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES))
+ torture_fail(tctx, "no trusts");
+
+ if (domains.count == 0) {
+ torture_fail(tctx, "no trusts");
+ }
+
+ for (i=0; i<domains.count; i++) {
+ struct lsa_QueryTrustedDomainInfoBySid q;
+
+ if (domains.domains[i].sid == NULL)
+ continue;
+
+ q.in.handle = handle;
+ q.in.dom_sid = domains.domains[i].sid;
+ q.in.level = 6;
+ status = dcerpc_lsa_QueryTrustedDomainInfoBySid(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) continue;
+
+ if ((q.out.info->info_ex.trust_direction & 2) &&
+ (q.out.info->info_ex.trust_type == 1)) {
+ *sid = domains.domains[i].sid;
+ return true;
+ }
+ }
+
+ torture_fail(tctx, "I need a AD DC with an outgoing trust to NT4");
+}
+
+#define NUM_SIDS 8
+
+bool torture_rpc_lsa_lookup(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle *handle;
+ struct dom_sid *dom_sid;
+ struct dom_sid *trusted_sid;
+ struct dom_sid *sids[NUM_SIDS];
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_fail(torture, "unable to connect to table");
+ }
+
+ ret &= open_policy(torture, p, &handle);
+ if (!ret) return false;
+
+ ret &= get_domainsid(torture, p, handle, &dom_sid);
+ if (!ret) return false;
+
+ ret &= get_downleveltrust(torture, p, handle, &trusted_sid);
+ if (!ret) return false;
+
+ torture_comment(torture, "domain sid: %s\n",
+ dom_sid_string(torture, dom_sid));
+
+ sids[0] = dom_sid_parse_talloc(torture, "S-1-1-0");
+ sids[1] = dom_sid_parse_talloc(torture, "S-1-5-4");
+ sids[2] = dom_sid_parse_talloc(torture, "S-1-5-32");
+ sids[3] = dom_sid_parse_talloc(torture, "S-1-5-32-545");
+ sids[4] = dom_sid_dup(torture, dom_sid);
+ sids[5] = dom_sid_add_rid(torture, dom_sid, 512);
+ sids[6] = dom_sid_dup(torture, trusted_sid);
+ sids[7] = dom_sid_add_rid(torture, trusted_sid, 512);
+
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 0,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+
+ {
+ enum lsa_SidType types[NUM_SIDS] =
+ { SID_NAME_WKN_GRP, SID_NAME_WKN_GRP, SID_NAME_DOMAIN,
+ SID_NAME_ALIAS, SID_NAME_DOMAIN, SID_NAME_DOM_GRP,
+ SID_NAME_DOMAIN, SID_NAME_DOM_GRP };
+
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 1,
+ NT_STATUS_OK, types);
+ }
+
+ {
+ enum lsa_SidType types[NUM_SIDS] =
+ { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN,
+ SID_NAME_UNKNOWN, SID_NAME_UNKNOWN,
+ SID_NAME_DOMAIN, SID_NAME_DOM_GRP,
+ SID_NAME_DOMAIN, SID_NAME_DOM_GRP };
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 2,
+ STATUS_SOME_UNMAPPED, types);
+ }
+
+ {
+ enum lsa_SidType types[NUM_SIDS] =
+ { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN,
+ SID_NAME_UNKNOWN, SID_NAME_UNKNOWN,
+ SID_NAME_DOMAIN, SID_NAME_DOM_GRP,
+ SID_NAME_UNKNOWN, SID_NAME_UNKNOWN };
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 3,
+ STATUS_SOME_UNMAPPED, types);
+ }
+
+ {
+ enum lsa_SidType types[NUM_SIDS] =
+ { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN,
+ SID_NAME_UNKNOWN, SID_NAME_UNKNOWN,
+ SID_NAME_DOMAIN, SID_NAME_DOM_GRP,
+ SID_NAME_UNKNOWN, SID_NAME_UNKNOWN };
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 4,
+ STATUS_SOME_UNMAPPED, types);
+ }
+
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 5,
+ NT_STATUS_NONE_MAPPED, NULL);
+
+ {
+ enum lsa_SidType types[NUM_SIDS] =
+ { SID_NAME_UNKNOWN, SID_NAME_UNKNOWN,
+ SID_NAME_UNKNOWN, SID_NAME_UNKNOWN,
+ SID_NAME_DOMAIN, SID_NAME_DOM_GRP,
+ SID_NAME_UNKNOWN, SID_NAME_UNKNOWN };
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 6,
+ STATUS_SOME_UNMAPPED, types);
+ }
+
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 7,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 8,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 9,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+ ret &= test_lookupsids(torture, p, handle, sids, NUM_SIDS, 10,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/mgmt.c b/source4/torture/rpc/mgmt.c
new file mode 100644
index 0000000000..fed432f31c
--- /dev/null
+++ b/source4/torture/rpc/mgmt.c
@@ -0,0 +1,270 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for mgmt rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_mgmt_c.h"
+#include "auth/gensec/gensec.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc_proto.h"
+
+
+/*
+ ask the server what interface IDs are available on this endpoint
+*/
+bool test_inq_if_ids(struct torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ bool (*per_id_test)(struct torture_context *,
+ const struct ndr_interface_table *iface,
+ TALLOC_CTX *mem_ctx,
+ struct ndr_syntax_id *id),
+ const void *priv)
+{
+ NTSTATUS status;
+ struct mgmt_inq_if_ids r;
+ struct rpc_if_id_vector_t *vector;
+ int i;
+
+ vector = talloc(mem_ctx, struct rpc_if_id_vector_t);
+ r.out.if_id_vector = &vector;
+
+ status = dcerpc_mgmt_inq_if_ids(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("inq_if_ids failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("inq_if_ids gave error code %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ if (!vector) {
+ printf("inq_if_ids gave NULL if_id_vector\n");
+ return false;
+ }
+
+ for (i=0;i<vector->count;i++) {
+ struct ndr_syntax_id *id = vector->if_id[i].id;
+ if (!id) continue;
+
+ printf("\tuuid %s version 0x%08x '%s'\n",
+ GUID_string(mem_ctx, &id->uuid),
+ id->if_version,
+ ndr_interface_name(&id->uuid, id->if_version));
+
+ if (per_id_test) {
+ per_id_test(tctx, priv, mem_ctx, id);
+ }
+ }
+
+ return true;
+}
+
+static bool test_inq_stats(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct mgmt_inq_stats r;
+ struct mgmt_statistics statistics;
+
+ r.in.max_count = MGMT_STATS_ARRAY_MAX_SIZE;
+ r.in.unknown = 0;
+ r.out.statistics = &statistics;
+
+ status = dcerpc_mgmt_inq_stats(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("inq_stats failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (statistics.count != MGMT_STATS_ARRAY_MAX_SIZE) {
+ printf("Unexpected array size %d\n", statistics.count);
+ return false;
+ }
+
+ printf("\tcalls_in %6d calls_out %6d\n\tpkts_in %6d pkts_out %6d\n",
+ statistics.statistics[MGMT_STATS_CALLS_IN],
+ statistics.statistics[MGMT_STATS_CALLS_OUT],
+ statistics.statistics[MGMT_STATS_PKTS_IN],
+ statistics.statistics[MGMT_STATS_PKTS_OUT]);
+
+ return true;
+}
+
+static bool test_inq_princ_name(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct mgmt_inq_princ_name r;
+ int i;
+ bool ret = false;
+
+ for (i=0;i<100;i++) {
+ r.in.authn_proto = i; /* DCERPC_AUTH_TYPE_* */
+ r.in.princ_name_size = 100;
+
+ status = dcerpc_mgmt_inq_princ_name(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ if (W_ERROR_IS_OK(r.out.result)) {
+ const char *name = gensec_get_name_by_authtype(i);
+ ret = true;
+ if (name) {
+ printf("\tprinciple name for proto %u (%s) is '%s'\n",
+ i, name, r.out.princ_name);
+ } else {
+ printf("\tprinciple name for proto %u is '%s'\n",
+ i, r.out.princ_name);
+ }
+ }
+ }
+
+ if (!ret) {
+ printf("\tno principle names?\n");
+ }
+
+ return true;
+}
+
+static bool test_is_server_listening(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct mgmt_is_server_listening r;
+ r.out.status = talloc(mem_ctx, uint32_t);
+
+ status = dcerpc_mgmt_is_server_listening(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("is_server_listening failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (*r.out.status != 0 || r.out.result == 0) {
+ printf("\tserver is NOT listening\n");
+ } else {
+ printf("\tserver is listening\n");
+ }
+
+ return true;
+}
+
+static bool test_stop_server_listening(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct mgmt_stop_server_listening r;
+
+ status = dcerpc_mgmt_stop_server_listening(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("stop_server_listening failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("\tserver refused to stop listening - %s\n", win_errstr(r.out.result));
+ } else {
+ printf("\tserver allowed a stop_server_listening request\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+bool torture_rpc_mgmt(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *mem_ctx, *loop_ctx;
+ bool ret = true;
+ const struct ndr_interface_list *l;
+ struct dcerpc_binding *b;
+
+ mem_ctx = talloc_init("torture_rpc_mgmt");
+
+ status = torture_rpc_binding(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ for (l=ndr_table_list();l;l=l->next) {
+ loop_ctx = talloc_named(mem_ctx, 0, "torture_rpc_mgmt loop context");
+
+ /* some interfaces are not mappable */
+ if (l->table->num_calls == 0 ||
+ strcmp(l->table->name, "mgmt") == 0) {
+ talloc_free(loop_ctx);
+ continue;
+ }
+
+ printf("\nTesting pipe '%s'\n", l->table->name);
+
+ status = dcerpc_epm_map_binding(loop_ctx, b, l->table, NULL, torture->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to map port for uuid %s\n",
+ GUID_string(loop_ctx, &l->table->syntax_id.uuid));
+ talloc_free(loop_ctx);
+ continue;
+ }
+
+ lp_set_cmdline(torture->lp_ctx, "torture:binding", dcerpc_binding_string(loop_ctx, b));
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_mgmt);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ printf("Interface not available - skipping\n");
+ talloc_free(loop_ctx);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(loop_ctx);
+ ret = false;
+ continue;
+ }
+
+ if (!test_is_server_listening(p, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_stop_server_listening(p, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_inq_stats(p, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_inq_princ_name(p, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_inq_if_ids(torture, p, loop_ctx, NULL, NULL)) {
+ ret = false;
+ }
+
+ }
+
+ return ret;
+}
diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c
new file mode 100644
index 0000000000..5ec2c29a20
--- /dev/null
+++ b/source4/torture/rpc/netlogon.c
@@ -0,0 +1,1612 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for netlogon rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
+ Copyright (C) Tim Potter 2003
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "lib/events/events.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/rpc/rpc.h"
+#include "torture/rpc/netlogon.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "param/param.h"
+
+#define TEST_MACHINE_NAME "torturetest"
+
+static bool test_LogonUasLogon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonUasLogon r;
+
+ r.in.server_name = NULL;
+ r.in.account_name = cli_credentials_get_username(cmdline_credentials);
+ r.in.workstation = TEST_MACHINE_NAME;
+
+ status = dcerpc_netr_LogonUasLogon(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonUasLogon");
+
+ return true;
+}
+
+static bool test_LogonUasLogoff(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonUasLogoff r;
+
+ r.in.server_name = NULL;
+ r.in.account_name = cli_credentials_get_username(cmdline_credentials);
+ r.in.workstation = TEST_MACHINE_NAME;
+
+ status = dcerpc_netr_LogonUasLogoff(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonUasLogoff");
+
+ return true;
+}
+
+static bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct creds_CredentialState **creds_out)
+{
+ NTSTATUS status;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ const char *plain_pass;
+ const char *machine_name;
+
+ plain_pass = cli_credentials_get_password(credentials);
+ machine_name = cli_credentials_get_workstation(credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ creds = talloc(tctx, struct creds_CredentialState);
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.credentials = &credentials2;
+
+ generate_random_buffer(credentials1.data, sizeof(credentials1.data));
+
+ status = dcerpc_netr_ServerReqChallenge(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge");
+
+ E_md4hash(plain_pass, mach_password.hash);
+
+ a.in.server_name = NULL;
+ a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name);
+ a.in.secure_channel_type = SEC_CHAN_BDC;
+ a.in.computer_name = machine_name;
+ a.in.credentials = &credentials3;
+ a.out.credentials = &credentials3;
+
+ creds_client_init(creds, &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ 0);
+
+ torture_comment(tctx, "Testing ServerAuthenticate\n");
+
+ status = dcerpc_netr_ServerAuthenticate(p, tctx, &a);
+
+ /* This allows the tests to continue against the more fussy windows 2008 */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DOWNGRADE_DETECTED)) {
+ return test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ credentials, SEC_CHAN_BDC, creds_out);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "ServerAuthenticate");
+
+ torture_assert(tctx, creds_client_check(creds, &credentials3),
+ "Credential chaining failed");
+
+ *creds_out = creds;
+ return true;
+}
+
+bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx,
+ uint32_t negotiate_flags,
+ struct cli_credentials *machine_credentials,
+ int sec_chan_type,
+ struct creds_CredentialState **creds_out)
+{
+ NTSTATUS status;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate2 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ const char *machine_name;
+ const char *plain_pass;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ plain_pass = cli_credentials_get_password(machine_credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ creds = talloc(tctx, struct creds_CredentialState);
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.credentials = &credentials2;
+
+ generate_random_buffer(credentials1.data, sizeof(credentials1.data));
+
+ status = dcerpc_netr_ServerReqChallenge(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge");
+
+ E_md4hash(plain_pass, mach_password.hash);
+
+ a.in.server_name = NULL;
+ a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name);
+ a.in.secure_channel_type = sec_chan_type;
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.credentials = &credentials3;
+
+ creds_client_init(creds, &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ negotiate_flags);
+
+ torture_comment(tctx, "Testing ServerAuthenticate2\n");
+
+ status = dcerpc_netr_ServerAuthenticate2(p, tctx, &a);
+ torture_assert_ntstatus_ok(tctx, status, "ServerAuthenticate2");
+
+ torture_assert(tctx, creds_client_check(creds, &credentials3),
+ "Credential chaining failed");
+
+ torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags);
+
+ *creds_out = creds;
+ return true;
+}
+
+
+static bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx,
+ uint32_t negotiate_flags,
+ struct cli_credentials *machine_credentials,
+ struct creds_CredentialState **creds_out)
+{
+ NTSTATUS status;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ plain_pass = cli_credentials_get_password(machine_credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ creds = talloc(tctx, struct creds_CredentialState);
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.credentials = &credentials2;
+
+ generate_random_buffer(credentials1.data, sizeof(credentials1.data));
+
+ status = dcerpc_netr_ServerReqChallenge(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerReqChallenge");
+
+ E_md4hash(plain_pass, mach_password.hash);
+
+ a.in.server_name = NULL;
+ a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name);
+ a.in.secure_channel_type = SEC_CHAN_BDC;
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.credentials = &credentials3;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.out.rid = &rid;
+
+ creds_client_init(creds, &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ negotiate_flags);
+
+ torture_comment(tctx, "Testing ServerAuthenticate3\n");
+
+ status = dcerpc_netr_ServerAuthenticate3(p, tctx, &a);
+ torture_assert_ntstatus_ok(tctx, status, "ServerAuthenticate3");
+ torture_assert(tctx, creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags);
+
+ *creds_out = creds;
+ return true;
+}
+
+/*
+ try a change password for our machine account
+*/
+static bool test_SetPassword(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_ServerPasswordSet r;
+ const char *password;
+ struct creds_CredentialState *creds;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME);
+ r.in.secure_channel_type = SEC_CHAN_BDC;
+ r.in.computer_name = TEST_MACHINE_NAME;
+
+ password = generate_random_str(tctx, 8);
+ E_md4hash(password, r.in.new_password.hash);
+
+ creds_des_encrypt(creds, &r.in.new_password);
+
+ torture_comment(tctx, "Testing ServerPasswordSet on machine account\n");
+ torture_comment(tctx, "Changing machine account password to '%s'\n",
+ password);
+
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_ServerPasswordSet(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ /* by changing the machine password twice we test the
+ credentials chaining fully, and we verify that the server
+ allows the password to be set to the same value twice in a
+ row (match win2k3) */
+ torture_comment(tctx,
+ "Testing a second ServerPasswordSet on machine account\n");
+ torture_comment(tctx,
+ "Changing machine account password to '%s' (same as previous run)\n", password);
+
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_ServerPasswordSet(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet (2)");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED);
+
+ torture_assert(tctx,
+ test_SetupCredentials(p, tctx, machine_credentials, &creds),
+ "ServerPasswordSet failed to actually change the password");
+
+ return true;
+}
+
+/*
+ try a change password for our machine account
+*/
+static bool test_SetPassword2(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_ServerPasswordSet2 r;
+ const char *password;
+ struct creds_CredentialState *creds;
+ struct samr_CryptPassword password_buf;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME);
+ r.in.secure_channel_type = SEC_CHAN_BDC;
+ r.in.computer_name = TEST_MACHINE_NAME;
+
+ password = generate_random_str(tctx, 8);
+ encode_pw_buffer(password_buf.data, password, STR_UNICODE);
+ creds_arcfour_crypt(creds, password_buf.data, 516);
+
+ memcpy(r.in.new_password.data, password_buf.data, 512);
+ r.in.new_password.length = IVAL(password_buf.data, 512);
+
+ torture_comment(tctx, "Testing ServerPasswordSet2 on machine account\n");
+ torture_comment(tctx, "Changing machine account password to '%s'\n", password);
+
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet2");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED);
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ torture_comment(tctx,
+ "Not testing ability to set password to '', enable dangerous tests to perform this test\n");
+ } else {
+ /* by changing the machine password to ""
+ * we check if the server uses password restrictions
+ * for ServerPasswordSet2
+ * (win2k3 accepts "")
+ */
+ password = "";
+ encode_pw_buffer(password_buf.data, password, STR_UNICODE);
+ creds_arcfour_crypt(creds, password_buf.data, 516);
+
+ memcpy(r.in.new_password.data, password_buf.data, 512);
+ r.in.new_password.length = IVAL(password_buf.data, 512);
+
+ torture_comment(tctx,
+ "Testing ServerPasswordSet2 on machine account\n");
+ torture_comment(tctx,
+ "Changing machine account password to '%s'\n", password);
+
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet2");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED);
+ }
+
+ torture_assert(tctx, test_SetupCredentials(p, tctx, machine_credentials, &creds),
+ "ServerPasswordSet failed to actually change the password");
+
+ /* now try a random password */
+ password = generate_random_str(tctx, 8);
+ encode_pw_buffer(password_buf.data, password, STR_UNICODE);
+ creds_arcfour_crypt(creds, password_buf.data, 516);
+
+ memcpy(r.in.new_password.data, password_buf.data, 512);
+ r.in.new_password.length = IVAL(password_buf.data, 512);
+
+ torture_comment(tctx, "Testing second ServerPasswordSet2 on machine account\n");
+ torture_comment(tctx, "Changing machine account password to '%s'\n", password);
+
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet2 (2)");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ /* by changing the machine password twice we test the
+ credentials chaining fully, and we verify that the server
+ allows the password to be set to the same value twice in a
+ row (match win2k3) */
+ torture_comment(tctx,
+ "Testing a second ServerPasswordSet2 on machine account\n");
+ torture_comment(tctx,
+ "Changing machine account password to '%s' (same as previous run)\n", password);
+
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_ServerPasswordSet2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet (3)");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ cli_credentials_set_password(machine_credentials, password, CRED_SPECIFIED);
+
+ torture_assert (tctx,
+ test_SetupCredentials(p, tctx, machine_credentials, &creds),
+ "ServerPasswordSet failed to actually change the password");
+
+ return true;
+}
+
+static bool test_GetPassword(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerPasswordGet r;
+ struct creds_CredentialState *creds;
+ struct netr_Authenticator credential;
+ NTSTATUS status;
+ struct netr_Authenticator return_authenticator;
+ struct samr_Password password;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ creds_client_authenticator(creds, &credential);
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME);
+ r.in.secure_channel_type = SEC_CHAN_BDC;
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.password = &password;
+
+ status = dcerpc_netr_ServerPasswordGet(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordGet");
+
+ return true;
+}
+
+static bool test_GetTrustPasswords(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerTrustPasswordsGet r;
+ struct creds_CredentialState *creds;
+ struct netr_Authenticator credential;
+ NTSTATUS status;
+ struct netr_Authenticator return_authenticator;
+ struct samr_Password password, password2;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ creds_client_authenticator(creds, &credential);
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.account_name = talloc_asprintf(tctx, "%s$", TEST_MACHINE_NAME);
+ r.in.secure_channel_type = SEC_CHAN_BDC;
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.password = &password;
+ r.out.password2 = &password2;
+
+ status = dcerpc_netr_ServerTrustPasswordsGet(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerTrustPasswordsGet");
+
+ return true;
+}
+
+/*
+ try a netlogon SamLogon
+*/
+bool test_netlogon_ops(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct creds_CredentialState *creds)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogon r;
+ struct netr_Authenticator auth, auth2;
+ struct netr_NetworkInfo ninfo;
+ DATA_BLOB names_blob, chal, lm_resp, nt_resp;
+ int i;
+ int flags = CLI_CRED_NTLM_AUTH;
+ if (lp_client_lanman_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lp_client_ntlmv2_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ cli_credentials_get_ntlm_username_domain(cmdline_credentials, tctx,
+ &ninfo.identity_info.account_name.string,
+ &ninfo.identity_info.domain_name.string);
+
+ generate_random_buffer(ninfo.challenge,
+ sizeof(ninfo.challenge));
+ chal = data_blob_const(ninfo.challenge,
+ sizeof(ninfo.challenge));
+
+ names_blob = NTLMv2_generate_names_blob(tctx, lp_iconv_convenience(tctx->lp_ctx), cli_credentials_get_workstation(credentials),
+ cli_credentials_get_domain(credentials));
+
+ status = cli_credentials_get_ntlm_response(cmdline_credentials, tctx,
+ &flags,
+ chal,
+ names_blob,
+ &lm_resp, &nt_resp,
+ NULL, NULL);
+ torture_assert_ntstatus_ok(tctx, status, "cli_credentials_get_ntlm_response failed");
+
+ ninfo.lm.data = lm_resp.data;
+ ninfo.lm.length = lm_resp.length;
+
+ ninfo.nt.data = nt_resp.data;
+ ninfo.nt.length = nt_resp.length;
+
+ ninfo.identity_info.parameter_control = 0;
+ ninfo.identity_info.logon_id_low = 0;
+ ninfo.identity_info.logon_id_high = 0;
+ ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials);
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(credentials);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = 2;
+ r.in.logon.network = &ninfo;
+
+ d_printf("Testing LogonSamLogon with name %s\n", ninfo.identity_info.account_name.string);
+
+ for (i=2;i<3;i++) {
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = i;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+ }
+
+ r.in.credential = NULL;
+
+ for (i=2;i<=3;i++) {
+
+ r.in.validation_level = i;
+
+ torture_comment(tctx, "Testing SamLogon with validation level %d and a NULL credential\n", i);
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER,
+ "LogonSamLogon expected INVALID_PARAMETER");
+
+ }
+
+ return true;
+}
+
+/*
+ try a netlogon SamLogon
+*/
+static bool test_SamLogon(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ struct creds_CredentialState *creds;
+
+ if (!test_SetupCredentials(p, tctx, credentials, &creds)) {
+ return false;
+ }
+
+ return test_netlogon_ops(p, tctx, credentials, creds);
+}
+
+/* we remember the sequence numbers so we can easily do a DatabaseDelta */
+static uint64_t sequence_nums[3];
+
+/*
+ try a netlogon DatabaseSync
+*/
+static bool test_DatabaseSync(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_DatabaseSync r;
+ struct creds_CredentialState *creds;
+ const uint32_t database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS};
+ int i;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.preferredmaximumlength = (uint32_t)-1;
+ ZERO_STRUCT(r.in.return_authenticator);
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+ r.in.sync_context = 0;
+ r.in.database_id = database_ids[i];
+
+ torture_comment(tctx, "Testing DatabaseSync of id %d\n", r.in.database_id);
+
+ do {
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_DatabaseSync(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ /* Native mode servers don't do this */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ return true;
+ }
+ torture_assert_ntstatus_ok(tctx, status, "DatabaseSync");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ r.in.sync_context = r.out.sync_context;
+
+ if (r.out.delta_enum_array &&
+ r.out.delta_enum_array->num_deltas > 0 &&
+ r.out.delta_enum_array->delta_enum[0].delta_type == NETR_DELTA_DOMAIN &&
+ r.out.delta_enum_array->delta_enum[0].delta_union.domain) {
+ sequence_nums[r.in.database_id] =
+ r.out.delta_enum_array->delta_enum[0].delta_union.domain->sequence_num;
+ torture_comment(tctx, "\tsequence_nums[%d]=%llu\n",
+ r.in.database_id,
+ (unsigned long long)sequence_nums[r.in.database_id]);
+ }
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon DatabaseDeltas
+*/
+static bool test_DatabaseDeltas(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_DatabaseDeltas r;
+ struct creds_CredentialState *creds;
+ const uint32_t database_ids[] = {0, 1, 2};
+ int i;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.preferredmaximumlength = (uint32_t)-1;
+ ZERO_STRUCT(r.in.return_authenticator);
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+ r.in.database_id = database_ids[i];
+ r.in.sequence_num = sequence_nums[r.in.database_id];
+
+ if (r.in.sequence_num == 0) continue;
+
+ r.in.sequence_num -= 1;
+
+ torture_comment(tctx, "Testing DatabaseDeltas of id %d at %llu\n",
+ r.in.database_id, (unsigned long long)r.in.sequence_num);
+
+ do {
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_DatabaseDeltas(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status,
+ NT_STATUS_SYNCHRONIZATION_REQUIRED)) {
+ torture_comment(tctx, "not considering %s to be an error\n",
+ nt_errstr(status));
+ return true;
+ }
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ torture_assert_ntstatus_ok(tctx, status, "DatabaseDeltas");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ r.in.sequence_num++;
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon AccountDeltas
+*/
+static bool test_AccountDeltas(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_AccountDeltas r;
+ struct creds_CredentialState *creds;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ ZERO_STRUCT(r.in.return_authenticator);
+ creds_client_authenticator(creds, &r.in.credential);
+ ZERO_STRUCT(r.in.uas);
+ r.in.count=10;
+ r.in.level=0;
+ r.in.buffersize=100;
+
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ status = dcerpc_netr_AccountDeltas(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_IMPLEMENTED, "AccountDeltas");
+
+ return true;
+}
+
+/*
+ try a netlogon AccountSync
+*/
+static bool test_AccountSync(struct torture_context *tctx, struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_AccountSync r;
+ struct creds_CredentialState *creds;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ ZERO_STRUCT(r.in.return_authenticator);
+ creds_client_authenticator(creds, &r.in.credential);
+ ZERO_STRUCT(r.in.recordid);
+ r.in.reference=0;
+ r.in.level=0;
+ r.in.buffersize=100;
+
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ status = dcerpc_netr_AccountSync(p, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NOT_IMPLEMENTED, "AccountSync");
+
+ return true;
+}
+
+/*
+ try a netlogon GetDcName
+*/
+static bool test_GetDcName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_GetDcName r;
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domainname = lp_workgroup(tctx->lp_ctx);
+
+ status = dcerpc_netr_GetDcName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetDcName");
+ torture_assert_werr_ok(tctx, r.out.result, "GetDcName");
+
+ torture_comment(tctx, "\tDC is at '%s'\n", r.out.dcname);
+
+ return true;
+}
+
+/*
+ try a netlogon LogonControl
+*/
+static bool test_LogonControl(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonControl r;
+ int i;
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.function_code = 1;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl level %d\n", i);
+
+ status = dcerpc_netr_LogonControl(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon GetAnyDCName
+*/
+static bool test_GetAnyDCName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_GetAnyDCName r;
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domainname = lp_workgroup(tctx->lp_ctx);
+
+ status = dcerpc_netr_GetAnyDCName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName");
+
+ if (r.out.dcname) {
+ torture_comment(tctx, "\tDC is at '%s'\n", r.out.dcname);
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon LogonControl2
+*/
+static bool test_LogonControl2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonControl2 r;
+ int i;
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ r.in.function_code = NETLOGON_CONTROL_REDISCOVER;
+ r.in.data.domain = lp_workgroup(tctx->lp_ctx);
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ r.in.function_code = NETLOGON_CONTROL_TC_QUERY;
+ r.in.data.domain = lp_workgroup(tctx->lp_ctx);
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ r.in.function_code = NETLOGON_CONTROL_TRANSPORT_NOTIFY;
+ r.in.data.domain = lp_workgroup(tctx->lp_ctx);
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG;
+ r.in.data.debug_level = ~0;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2 level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ return true;
+}
+
+/*
+ try a netlogon DatabaseSync2
+*/
+static bool test_DatabaseSync2(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_DatabaseSync2 r;
+ struct creds_CredentialState *creds;
+ const uint32_t database_ids[] = {0, 1, 2};
+ int i;
+
+ if (!test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_FLAGS,
+ machine_credentials,
+ SEC_CHAN_BDC, &creds)) {
+ return false;
+ }
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.preferredmaximumlength = (uint32_t)-1;
+ ZERO_STRUCT(r.in.return_authenticator);
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+ r.in.sync_context = 0;
+ r.in.database_id = database_ids[i];
+ r.in.restart_state = 0;
+
+ torture_comment(tctx, "Testing DatabaseSync2 of id %d\n", r.in.database_id);
+
+ do {
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_DatabaseSync2(p, tctx, &r);
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ /* Native mode servers don't do this */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ return true;
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "DatabaseSync2");
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ r.in.sync_context = r.out.sync_context;
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon LogonControl2Ex
+*/
+static bool test_LogonControl2Ex(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonControl2Ex r;
+ int i;
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ r.in.function_code = NETLOGON_CONTROL_REDISCOVER;
+ r.in.data.domain = lp_workgroup(tctx->lp_ctx);
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2Ex level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2Ex(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ r.in.function_code = NETLOGON_CONTROL_TC_QUERY;
+ r.in.data.domain = lp_workgroup(tctx->lp_ctx);
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2Ex level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2Ex(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ r.in.function_code = NETLOGON_CONTROL_TRANSPORT_NOTIFY;
+ r.in.data.domain = lp_workgroup(tctx->lp_ctx);
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2Ex level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2Ex(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG;
+ r.in.data.debug_level = ~0;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2Ex level %d function %d\n",
+ i, r.in.function_code);
+
+ status = dcerpc_netr_LogonControl2Ex(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ }
+
+ return true;
+}
+
+static bool test_netr_DsRGetForestTrustInformation(struct torture_context *tctx,
+ struct dcerpc_pipe *p, const char *trusted_domain_name)
+{
+ NTSTATUS status;
+ struct netr_DsRGetForestTrustInformation r;
+ struct lsa_ForestTrustInformation info, *info_ptr;
+
+ info_ptr = &info;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.trusted_domain_name = trusted_domain_name;
+ r.in.flags = 0;
+ r.out.forest_trust_info = &info_ptr;
+
+ torture_comment(tctx ,"Testing netr_DsRGetForestTrustInformation\n");
+
+ status = dcerpc_netr_DsRGetForestTrustInformation(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "DsRGetForestTrustInformation");
+ torture_assert_werr_ok(tctx, r.out.result, "DsRGetForestTrustInformation");
+
+ return true;
+}
+
+/*
+ try a netlogon netr_DsrEnumerateDomainTrusts
+*/
+static bool test_DsrEnumerateDomainTrusts(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsrEnumerateDomainTrusts r;
+ int i;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.trust_flags = 0x3f;
+
+ status = dcerpc_netr_DsrEnumerateDomainTrusts(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "DsrEnumerateDomaintrusts");
+ torture_assert_werr_ok(tctx, r.out.result, "DsrEnumerateDomaintrusts");
+
+ /* when trusted_domain_name is NULL, netr_DsRGetForestTrustInformation
+ * will show non-forest trusts and all UPN suffixes of the own forest
+ * as LSA_FOREST_TRUST_TOP_LEVEL_NAME types */
+
+ if (r.out.count) {
+ if (!test_netr_DsRGetForestTrustInformation(tctx, p, NULL)) {
+ return false;
+ }
+ }
+
+ for (i=0; i<r.out.count; i++) {
+
+ /* get info for transitive forest trusts */
+
+ if (r.out.trusts[i].trust_attributes & NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ if (!test_netr_DsRGetForestTrustInformation(tctx, p,
+ r.out.trusts[i].dns_name)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_netr_NetrEnumerateTrustedDomains(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_NetrEnumerateTrustedDomains r;
+ struct netr_Blob trusted_domains_blob;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.out.trusted_domains_blob = &trusted_domains_blob;
+
+ status = dcerpc_netr_NetrEnumerateTrustedDomains(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_NetrEnumerateTrustedDomains");
+ torture_assert_werr_ok(tctx, r.out.result, "NetrEnumerateTrustedDomains");
+
+ return true;
+}
+
+static bool test_netr_NetrEnumerateTrustedDomainsEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_NetrEnumerateTrustedDomainsEx r;
+ struct netr_DomainTrustList dom_trust_list;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.out.dom_trust_list = &dom_trust_list;
+
+ status = dcerpc_netr_NetrEnumerateTrustedDomainsEx(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_NetrEnumerateTrustedDomainsEx");
+ torture_assert_werr_ok(tctx, r.out.result, "NetrEnumerateTrustedDomainsEx");
+
+ return true;
+}
+
+
+static bool test_netr_DsRGetSiteName(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *computer_name,
+ const char *expected_site)
+{
+ NTSTATUS status;
+ struct netr_DsRGetSiteName r;
+
+ if (torture_setting_bool(tctx, "samba4", false))
+ torture_skip(tctx, "skipping DsRGetSiteName test against Samba4");
+
+ r.in.computer_name = computer_name;
+ torture_comment(tctx, "Testing netr_DsRGetSiteName\n");
+
+ status = dcerpc_netr_DsRGetSiteName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "DsRGetSiteName");
+ torture_assert_werr_ok(tctx, r.out.result, "DsRGetSiteName");
+ torture_assert_str_equal(tctx, expected_site, r.out.site, "netr_DsRGetSiteName");
+
+ r.in.computer_name = talloc_asprintf(tctx, "\\\\%s", computer_name);
+ torture_comment(tctx,
+ "Testing netr_DsRGetSiteName with broken computer name: %s\n", r.in.computer_name);
+
+ status = dcerpc_netr_DsRGetSiteName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "DsRGetSiteName");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_COMPUTERNAME, "netr_DsRGetSiteName");
+
+ return true;
+}
+
+/*
+ try a netlogon netr_DsRGetDCName
+*/
+static bool test_netr_DsRGetDCName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsRGetDCName r;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domain_name = talloc_asprintf(tctx, "%s", lp_realm(tctx->lp_ctx));
+ r.in.domain_guid = NULL;
+ r.in.site_guid = NULL;
+ r.in.flags = DS_RETURN_DNS_NAME;
+
+ status = dcerpc_netr_DsRGetDCName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "DsRGetDCName");
+ torture_assert_werr_ok(tctx, r.out.result, "DsRGetDCName");
+ return test_netr_DsRGetSiteName(p, tctx,
+ r.out.info->dc_unc,
+ r.out.info->dc_site_name);
+}
+
+/*
+ try a netlogon netr_DsRGetDCNameEx
+*/
+static bool test_netr_DsRGetDCNameEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsRGetDCNameEx r;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domain_name = talloc_asprintf(tctx, "%s", lp_realm(tctx->lp_ctx));
+ r.in.domain_guid = NULL;
+ r.in.site_name = NULL;
+ r.in.flags = DS_RETURN_DNS_NAME;
+
+ status = dcerpc_netr_DsRGetDCNameEx(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx");
+
+ return test_netr_DsRGetSiteName(p, tctx, r.out.info->dc_unc,
+ r.out.info->dc_site_name);
+}
+
+/*
+ try a netlogon netr_DsRGetDCNameEx2
+*/
+static bool test_netr_DsRGetDCNameEx2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsRGetDCNameEx2 r;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.client_account = NULL;
+ r.in.mask = 0x00000000;
+ r.in.domain_name = talloc_asprintf(tctx, "%s", lp_realm(tctx->lp_ctx));
+ r.in.domain_guid = NULL;
+ r.in.site_name = NULL;
+ r.in.flags = DS_RETURN_DNS_NAME;
+
+ torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 without client account\n");
+
+ status = dcerpc_netr_DsRGetDCNameEx2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2");
+
+ torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 with client acount\n");
+ r.in.client_account = TEST_MACHINE_NAME"$";
+ r.in.mask = ACB_SVRTRUST;
+ r.in.flags = DS_RETURN_FLAT_NAME;
+
+ status = dcerpc_netr_DsRGetDCNameEx2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2");
+ return test_netr_DsRGetSiteName(p, tctx, r.out.info->dc_unc,
+ r.out.info->dc_site_name);
+}
+
+static bool test_netr_DsrGetDcSiteCoverageW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsrGetDcSiteCoverageW r;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ status = dcerpc_netr_DsrGetDcSiteCoverageW(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ return true;
+}
+
+static bool test_netr_DsRAddressToSitenamesW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsRAddressToSitenamesW r;
+ struct netr_DsRAddress addr;
+ struct netr_DsRAddressToSitenamesWCtr *ctr;
+
+ ctr = talloc(tctx, struct netr_DsRAddressToSitenamesWCtr);
+
+ addr.size = 16;
+ addr.buffer = talloc_zero_array(tctx, uint8_t, addr.size);
+
+ addr.buffer[0] = 2; /* AF_INET */
+ addr.buffer[4] = 127;
+ addr.buffer[5] = 0;
+ addr.buffer[6] = 0;
+ addr.buffer[7] = 1;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.count = 1;
+ r.in.addresses = talloc_zero_array(tctx, struct netr_DsRAddress, r.in.count);
+ r.in.addresses[0] = addr;
+ r.out.ctr = &ctr;
+
+ status = dcerpc_netr_DsRAddressToSitenamesW(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ return true;
+}
+
+static bool test_netr_DsRAddressToSitenamesExW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsRAddressToSitenamesExW r;
+ struct netr_DsRAddress addr;
+ struct netr_DsRAddressToSitenamesExWCtr *ctr;
+
+ ctr = talloc(tctx, struct netr_DsRAddressToSitenamesExWCtr);
+
+ addr.size = 16;
+ addr.buffer = talloc_zero_array(tctx, uint8_t, addr.size);
+
+ addr.buffer[0] = 2; /* AF_INET */
+ addr.buffer[4] = 127;
+ addr.buffer[5] = 0;
+ addr.buffer[6] = 0;
+ addr.buffer[7] = 1;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.count = 1;
+ r.in.addresses = talloc_zero_array(tctx, struct netr_DsRAddress, r.in.count);
+ r.in.addresses[0] = addr;
+ r.out.ctr = &ctr;
+
+ status = dcerpc_netr_DsRAddressToSitenamesExW(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ return true;
+}
+
+static bool test_GetDomainInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_LogonGetDomainInfo r;
+ struct netr_DomainQuery1 q1;
+ struct netr_Authenticator a;
+ struct creds_CredentialState *creds;
+
+ if (!test_SetupCredentials3(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ machine_credentials, &creds)) {
+ return false;
+ }
+
+ ZERO_STRUCT(r);
+
+ creds_client_authenticator(creds, &a);
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.level = 1;
+ r.in.credential = &a;
+ r.in.return_authenticator = &a;
+ r.out.return_authenticator = &a;
+
+ r.in.query.query1 = &q1;
+ ZERO_STRUCT(q1);
+
+ /* this should really be the fully qualified name */
+ q1.workstation_domain = TEST_MACHINE_NAME;
+ q1.workstation_site = "Default-First-Site-Name";
+ q1.blob2.length = 0;
+ q1.blob2.size = 0;
+ q1.blob2.data = NULL;
+ q1.product.string = "product string";
+
+ torture_comment(tctx, "Testing netr_uogonGetDomainInfo\n");
+
+ status = dcerpc_netr_LogonGetDomainInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_LogonGetDomainInfo");
+ torture_assert(tctx, creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ return true;
+}
+
+
+static void async_callback(struct rpc_request *req)
+{
+ int *counter = (int *)req->async.private_data;
+ if (NT_STATUS_IS_OK(req->status)) {
+ (*counter)++;
+ }
+}
+
+static bool test_GetDomainInfo_async(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_LogonGetDomainInfo r;
+ struct netr_DomainQuery1 q1;
+ struct netr_Authenticator a;
+#define ASYNC_COUNT 100
+ struct creds_CredentialState *creds;
+ struct creds_CredentialState *creds_async[ASYNC_COUNT];
+ struct rpc_request *req[ASYNC_COUNT];
+ int i;
+ int *async_counter = talloc(tctx, int);
+
+ if (!test_SetupCredentials3(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ machine_credentials, &creds)) {
+ return false;
+ }
+
+ ZERO_STRUCT(r);
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.level = 1;
+ r.in.credential = &a;
+ r.in.return_authenticator = &a;
+ r.out.return_authenticator = &a;
+
+ r.in.query.query1 = &q1;
+ ZERO_STRUCT(q1);
+
+ /* this should really be the fully qualified name */
+ q1.workstation_domain = TEST_MACHINE_NAME;
+ q1.workstation_site = "Default-First-Site-Name";
+ q1.blob2.length = 0;
+ q1.blob2.size = 0;
+ q1.blob2.data = NULL;
+ q1.product.string = "product string";
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo - async count %d\n", ASYNC_COUNT);
+
+ *async_counter = 0;
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ creds_client_authenticator(creds, &a);
+
+ creds_async[i] = (struct creds_CredentialState *)talloc_memdup(creds, creds, sizeof(*creds));
+ req[i] = dcerpc_netr_LogonGetDomainInfo_send(p, tctx, &r);
+
+ req[i]->async.callback = async_callback;
+ req[i]->async.private_data = async_counter;
+
+ /* even with this flush per request a w2k3 server seems to
+ clag with multiple outstanding requests. bleergh. */
+ torture_assert_int_equal(tctx, event_loop_once(dcerpc_event_context(p)), 0,
+ "event_loop_once failed");
+ }
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ status = dcerpc_ndr_request_recv(req[i]);
+
+ torture_assert_ntstatus_ok(tctx, status, "netr_LogonGetDomainInfo_async");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "netr_LogonGetDomainInfo_async");
+
+ torture_assert(tctx, creds_client_check(creds_async[i], &a.cred),
+ "Credential chaining failed at async");
+ }
+
+ torture_comment(tctx,
+ "Testing netr_LogonGetDomainInfo - async count %d OK\n", *async_counter);
+
+ torture_assert_int_equal(tctx, (*async_counter), ASYNC_COUNT, "int");
+
+ return true;
+}
+
+static bool test_ManyGetDCName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p2;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 o;
+ struct policy_handle lsa_handle;
+ struct lsa_DomainList domains;
+
+ struct lsa_EnumTrustDom t;
+ uint32_t resume_handle = 0;
+ struct netr_GetAnyDCName d;
+
+ int i;
+
+ if (p->conn->transport.transport != NCACN_NP) {
+ return true;
+ }
+
+ torture_comment(tctx, "Torturing GetDCName\n");
+
+ status = dcerpc_secondary_connection(p, &p2, p->binding);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+
+ status = dcerpc_bind_auth_none(p2, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create bind on secondary connection");
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ o.in.system_name = "\\";
+ o.in.attr = &attr;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.handle = &lsa_handle;
+
+ status = dcerpc_lsa_OpenPolicy2(p2, tctx, &o);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPolicy2 failed");
+
+ t.in.handle = &lsa_handle;
+ t.in.resume_handle = &resume_handle;
+ t.in.max_size = 1000;
+ t.out.domains = &domains;
+ t.out.resume_handle = &resume_handle;
+
+ status = dcerpc_lsa_EnumTrustDom(p2, tctx, &t);
+
+ if ((!NT_STATUS_IS_OK(status) &&
+ (!NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES))))
+ torture_fail(tctx, "Could not list domains");
+
+ talloc_free(p2);
+
+ d.in.logon_server = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+
+ for (i=0; i<domains.count * 4; i++) {
+ struct lsa_DomainInfo *info =
+ &domains.domains[rand()%domains.count];
+
+ d.in.domainname = info->name.string;
+
+ status = dcerpc_netr_GetAnyDCName(p, tctx, &d);
+ torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName");
+
+ torture_comment(tctx, "\tDC for domain %s is %s\n", info->name.string,
+ d.out.dcname ? d.out.dcname : "unknown");
+ }
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_netlogon(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "NETLOGON");
+ struct torture_rpc_tcase *tcase;
+ struct torture_test *test;
+
+ tcase = torture_suite_add_machine_rpc_iface_tcase(suite, "netlogon",
+ &ndr_table_netlogon, TEST_MACHINE_NAME);
+ torture_rpc_tcase_add_test(tcase, "LogonUasLogon", test_LogonUasLogon);
+ torture_rpc_tcase_add_test(tcase, "LogonUasLogoff", test_LogonUasLogoff);
+ torture_rpc_tcase_add_test_creds(tcase, "SamLogon", test_SamLogon);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword", test_SetPassword);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword2", test_SetPassword2);
+ torture_rpc_tcase_add_test_creds(tcase, "GetPassword", test_GetPassword);
+ torture_rpc_tcase_add_test_creds(tcase, "GetTrustPasswords", test_GetTrustPasswords);
+ torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo", test_GetDomainInfo);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync", test_DatabaseSync);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseDeltas", test_DatabaseDeltas);
+ torture_rpc_tcase_add_test_creds(tcase, "AccountDeltas", test_AccountDeltas);
+ torture_rpc_tcase_add_test_creds(tcase, "AccountSync", test_AccountSync);
+ torture_rpc_tcase_add_test(tcase, "GetDcName", test_GetDcName);
+ torture_rpc_tcase_add_test(tcase, "ManyGetDCName", test_ManyGetDCName);
+ torture_rpc_tcase_add_test(tcase, "LogonControl", test_LogonControl);
+ torture_rpc_tcase_add_test(tcase, "GetAnyDCName", test_GetAnyDCName);
+ torture_rpc_tcase_add_test(tcase, "LogonControl2", test_LogonControl2);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync2", test_DatabaseSync2);
+ torture_rpc_tcase_add_test(tcase, "LogonControl2Ex", test_LogonControl2Ex);
+ torture_rpc_tcase_add_test(tcase, "DsrEnumerateDomainTrusts", test_DsrEnumerateDomainTrusts);
+ torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomains", test_netr_NetrEnumerateTrustedDomains);
+ torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomainsEx", test_netr_NetrEnumerateTrustedDomainsEx);
+ test = torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo_async", test_GetDomainInfo_async);
+ test->dangerous = true;
+ torture_rpc_tcase_add_test(tcase, "DsRGetDCName", test_netr_DsRGetDCName);
+ torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx", test_netr_DsRGetDCNameEx);
+ torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx2", test_netr_DsRGetDCNameEx2);
+ torture_rpc_tcase_add_test(tcase, "DsrGetDcSiteCoverageW", test_netr_DsrGetDcSiteCoverageW);
+ torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesW", test_netr_DsRAddressToSitenamesW);
+ torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesExW", test_netr_DsRAddressToSitenamesExW);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/netlogon.h b/source4/torture/rpc/netlogon.h
new file mode 100644
index 0000000000..92d366b46a
--- /dev/null
+++ b/source4/torture/rpc/netlogon.h
@@ -0,0 +1,6 @@
+
+bool test_SetupCredentials2(struct dcerpc_pipe *p, struct torture_context *tctx,
+ uint32_t negotiate_flags,
+ struct cli_credentials *machine_credentials,
+ int sec_chan_type,
+ struct creds_CredentialState **creds_out);
diff --git a/source4/torture/rpc/oxidresolve.c b/source4/torture/rpc/oxidresolve.c
new file mode 100644
index 0000000000..02edb306b3
--- /dev/null
+++ b/source4/torture/rpc/oxidresolve.c
@@ -0,0 +1,237 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for oxidresolve operations
+
+ Copyright (C) Jelmer Vernooij 2004
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_oxidresolver_c.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/epmapper.h"
+#include "torture/rpc/rpc.h"
+
+#define CLSID_IMAGEDOC "02B01C80-E03D-101A-B294-00DD010F2BF9"
+
+const struct GUID IUnknown_uuid = {
+ 0x00000000,0x0000,0x0000,{0xc0,0x00},{0x00,0x00,0x00,0x00,0x00,0x46}
+};
+
+static bool test_RemoteActivation(struct torture_context *tctx,
+ uint64_t *oxid, struct GUID *oid)
+{
+ struct RemoteActivation r;
+ NTSTATUS status;
+ struct GUID iids[2];
+ uint16_t protseq[3] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_UUID };
+ struct dcerpc_pipe *p;
+
+ status = torture_rpc_connection(tctx, &p,
+ &ndr_table_IRemoteActivation);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ZERO_STRUCT(r.in);
+ r.in.this.version.MajorVersion = 5;
+ r.in.this.version.MinorVersion = 1;
+ r.in.this.cid = GUID_random();
+ GUID_from_string(CLSID_IMAGEDOC, &r.in.Clsid);
+ r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
+ r.in.num_protseqs = 3;
+ r.in.protseq = protseq;
+ r.in.Interfaces = 1;
+ iids[0] = IUnknown_uuid;
+ r.in.pIIDs = iids;
+ r.out.pOxid = oxid;
+ r.out.ipidRemUnknown = oid;
+
+ status = dcerpc_RemoteActivation(p, tctx, &r);
+ if(NT_STATUS_IS_ERR(status)) {
+ fprintf(stderr, "RemoteActivation: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if(!W_ERROR_IS_OK(r.out.result)) {
+ fprintf(stderr, "RemoteActivation: %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ if(!W_ERROR_IS_OK(*r.out.hr)) {
+ fprintf(stderr, "RemoteActivation: %s\n", win_errstr(*r.out.hr));
+ return false;
+ }
+
+ if(!W_ERROR_IS_OK(r.out.results[0])) {
+ fprintf(stderr, "RemoteActivation: %s\n", win_errstr(r.out.results[0]));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_SimplePing(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct SimplePing r;
+ NTSTATUS status;
+ uint64_t setid;
+
+ r.in.SetId = &setid;
+
+ status = dcerpc_SimplePing(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "SimplePing");
+ torture_assert_werr_ok(tctx, r.out.result, "SimplePing");
+
+ return true;
+}
+
+static bool test_ComplexPing(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ComplexPing r;
+ NTSTATUS status;
+ uint64_t setid;
+ struct GUID oid;
+ uint64_t oxid;
+
+ if (!test_RemoteActivation(tctx, &oxid, &oid))
+ return false;
+
+ setid = 0;
+ ZERO_STRUCT(r.in);
+
+ r.in.SequenceNum = 0;
+ r.in.SetId = &setid;
+ r.in.cAddToSet = 1;
+ r.in.AddToSet = &oid;
+
+ status = dcerpc_ComplexPing(p, tctx, &r);
+ if(NT_STATUS_IS_ERR(status)) {
+ fprintf(stderr, "ComplexPing: %s\n", nt_errstr(status));
+ return 0;
+ }
+
+ if(!W_ERROR_IS_OK(r.out.result)) {
+ fprintf(stderr, "ComplexPing: %s\n", win_errstr(r.out.result));
+ return 0;
+ }
+
+
+
+ return 1;
+}
+
+static bool test_ServerAlive(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ServerAlive r;
+ NTSTATUS status;
+
+ status = dcerpc_ServerAlive(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerAlive");
+ torture_assert_werr_ok(tctx, r.out.result, "ServerAlive");
+
+ return true;
+}
+
+static bool test_ResolveOxid(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ResolveOxid r;
+ NTSTATUS status;
+ uint16_t protseq[2] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB };
+ uint64_t oxid;
+ struct GUID oid;
+
+ if (!test_RemoteActivation(tctx, &oxid, &oid))
+ return false;
+
+ r.in.pOxid = oxid;
+ r.in.cRequestedProtseqs = 2;
+ r.in.arRequestedProtseqs = protseq;
+
+ status = dcerpc_ResolveOxid(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ResolveOxid");
+ torture_assert_werr_ok(tctx, r.out.result, "ResolveOxid");
+
+ return true;
+}
+
+static bool test_ResolveOxid2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ResolveOxid2 r;
+ NTSTATUS status;
+ uint16_t protseq[2] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_SMB };
+ uint64_t oxid;
+ struct GUID oid;
+
+ if (!test_RemoteActivation(tctx, &oxid, &oid))
+ return false;
+
+ r.in.pOxid = oxid;
+ r.in.cRequestedProtseqs = 2;
+ r.in.arRequestedProtseqs = protseq;
+
+ status = dcerpc_ResolveOxid2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ResolveOxid2");
+
+ torture_assert_werr_ok(tctx, r.out.result, "ResolveOxid2");
+
+ torture_comment(tctx, "Remote server versions: %d, %d\n", r.out.ComVersion->MajorVersion, r.out.ComVersion->MinorVersion);
+
+ return true;
+}
+
+static bool test_ServerAlive2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct ServerAlive2 r;
+ NTSTATUS status;
+
+ status = dcerpc_ServerAlive2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerAlive2");
+ torture_assert_werr_ok(tctx, r.out.result, "ServerAlive2");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_oxidresolve(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx,
+ "OXIDRESOLVE");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "oxidresolver",
+ &ndr_table_IOXIDResolver);
+
+ torture_rpc_tcase_add_test(tcase, "ServerAlive", test_ServerAlive);
+
+ torture_rpc_tcase_add_test(tcase, "ServerAlive2", test_ServerAlive2);
+
+ torture_rpc_tcase_add_test(tcase, "ComplexPing", test_ComplexPing);
+
+ torture_rpc_tcase_add_test(tcase, "SimplePing", test_SimplePing);
+
+ torture_rpc_tcase_add_test(tcase, "ResolveOxid", test_ResolveOxid);
+
+ torture_rpc_tcase_add_test(tcase, "ResolveOxid2", test_ResolveOxid2);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/remact.c b/source4/torture/rpc/remact.c
new file mode 100644
index 0000000000..1fc3c1376b
--- /dev/null
+++ b/source4/torture/rpc/remact.c
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for remoteactivation operations
+
+ Copyright (C) Jelmer Vernooij 2004
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "torture/rpc/rpc.h"
+
+#define CLSID_IMAGEDOC "02B01C80-E03D-101A-B294-00DD010F2BF9"
+#define DCERPC_IUNKNOWN_UUID "00000000-0000-0000-c000-000000000046"
+#define DCERPC_ICLASSFACTORY_UUID "00000001-0000-0000-c000-000000000046"
+
+static bool test_RemoteActivation(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct RemoteActivation r;
+ NTSTATUS status;
+ struct GUID iids[1];
+ uint16_t protseq[3] = { EPM_PROTOCOL_TCP, EPM_PROTOCOL_NCALRPC, EPM_PROTOCOL_UUID };
+
+ ZERO_STRUCT(r.in);
+ r.in.this.version.MajorVersion = 5;
+ r.in.this.version.MinorVersion = 1;
+ r.in.this.cid = GUID_random();
+ GUID_from_string(CLSID_IMAGEDOC, &r.in.Clsid);
+ r.in.ClientImpLevel = RPC_C_IMP_LEVEL_IDENTIFY;
+ r.in.num_protseqs = 3;
+ r.in.protseq = protseq;
+ r.in.Interfaces = 1;
+ GUID_from_string(DCERPC_IUNKNOWN_UUID, &iids[0]);
+ r.in.pIIDs = iids;
+
+ status = dcerpc_RemoteActivation(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "RemoteActivation");
+
+ torture_assert_werr_ok(tctx, r.out.result, "RemoteActivation");
+
+ torture_assert_werr_ok(tctx, *r.out.hr, "RemoteActivation");
+
+ torture_assert_werr_ok(tctx, r.out.results[0], "RemoteActivation");
+
+ GUID_from_string(DCERPC_ICLASSFACTORY_UUID, &iids[0]);
+ r.in.Interfaces = 1;
+ r.in.Mode = MODE_GET_CLASS_OBJECT;
+
+ status = dcerpc_RemoteActivation(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "RemoteActivation(GetClassObject)");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "RemoteActivation(GetClassObject)");
+
+ torture_assert_werr_ok(tctx, *r.out.hr, "RemoteActivation(GetClassObject)");
+
+ torture_assert_werr_ok(tctx, r.out.results[0],
+ "RemoteActivation(GetClassObject)");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_remact(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "REMACT");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "remact", &ndr_table_IRemoteActivation);
+
+ torture_rpc_tcase_add_test(tcase, "RemoteActivation", test_RemoteActivation);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c
new file mode 100644
index 0000000000..6419e40014
--- /dev/null
+++ b/source4/torture/rpc/remote_pac.c
@@ -0,0 +1,380 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for netlogon PAC operations
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "lib/events/events.h"
+#include "auth/auth.h"
+#include "auth/gensec/gensec.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/rpc/rpc.h"
+#include "torture/rpc/netlogon.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "param/param.h"
+#include "lib/messaging/irpc.h"
+#include "cluster/cluster.h"
+
+#define TEST_MACHINE_NAME "torturepactest"
+
+/* Check to see if we can pass the PAC across to the NETLOGON server for validation */
+
+/* Also happens to be a really good one-step verfication of our Kerberos stack */
+
+static bool test_PACVerify(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ NTSTATUS status;
+
+ struct netr_LogonSamLogon r;
+
+ struct netr_GenericInfo generic;
+ struct netr_Authenticator auth, auth2;
+
+
+ struct creds_CredentialState *creds;
+ struct gensec_security *gensec_client_context;
+ struct gensec_security *gensec_server_context;
+
+ struct messaging_context *msg_server_ctx;
+ DATA_BLOB client_to_server, server_to_client, pac_wrapped, payload;
+ struct PAC_Validate pac_wrapped_struct;
+
+ enum ndr_err_code ndr_err;
+
+ struct auth_session_info *session_info;
+
+ char *tmp_dir;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+
+ int i;
+
+ torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed");
+
+ if (!test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ credentials, SEC_CHAN_BDC,
+ &creds)) {
+ return false;
+ }
+
+ status = torture_temp_dir(tctx, "PACVerify", &tmp_dir);
+ torture_assert_ntstatus_ok(tctx, status, "torture_temp_dir failed");
+
+ msg_server_ctx = messaging_init(tctx,
+ tmp_dir,
+ cluster_id(0, 1),
+ lp_iconv_convenience(tctx->lp_ctx),
+ tctx->ev);
+
+ torture_assert(tctx, msg_server_ctx != NULL, "Failed to init messaging context");
+
+ status = gensec_client_start(tctx, &gensec_client_context, tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed");
+
+ status = gensec_set_target_hostname(gensec_client_context, TEST_MACHINE_NAME);
+
+ status = gensec_set_credentials(gensec_client_context, cmdline_credentials);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (client) failed");
+
+ status = gensec_start_mech_by_sasl_name(gensec_client_context, "GSSAPI");
+ torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (client) failed");
+
+ status = gensec_server_start(tctx, tctx->ev, tctx->lp_ctx, msg_server_ctx, &gensec_server_context);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed");
+
+ status = gensec_set_credentials(gensec_server_context, credentials);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed");
+
+ status = gensec_start_mech_by_sasl_name(gensec_server_context, "GSSAPI");
+ torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_sasl_name (server) failed");
+
+ server_to_client = data_blob(NULL, 0);
+
+ do {
+ /* Do a client-server update dance */
+ status = gensec_update(gensec_client_context, tmp_ctx, server_to_client, &client_to_server);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {;
+ torture_assert_ntstatus_ok(tctx, status, "gensec_update (client) failed");
+ }
+
+ if (client_to_server.length == 0) {
+ break;
+ }
+
+ status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {;
+ torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed");
+ }
+
+ if (server_to_client.length == 0) {
+ break;
+ }
+ } while (1);
+
+ /* Extract the PAC using Samba's code */
+
+ status = gensec_session_info(gensec_server_context, &session_info);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
+
+ pac_wrapped_struct.MessageType = 0x3;
+ pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length;
+ pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type;
+ pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length;
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tmp_ctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ session_info->server_info->pac_srv_sig.signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ session_info->server_info->pac_kdc_sig.signature.data,
+ pac_wrapped_struct.SignatureLength);
+
+ ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct,
+ (ndr_push_flags_fn_t)ndr_push_PAC_Validate);
+ torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed");
+
+ torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption");
+ creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length);
+
+ generic.length = pac_wrapped.length;
+ generic.data = pac_wrapped.data;
+
+ /* Validate it over the netlogon pipe */
+
+ generic.identity_info.parameter_control = 0;
+ generic.identity_info.logon_id_high = 0;
+ generic.identity_info.logon_id_low = 0;
+ generic.identity_info.domain_name.string = session_info->server_info->domain_name;
+ generic.identity_info.account_name.string = session_info->server_info->account_name;
+ generic.identity_info.workstation.string = TEST_MACHINE_NAME;
+
+ generic.package_name.string = "Kerberos";
+
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon.generic = &generic;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed");
+
+ /* This will break the signature nicely (even in the crypto wrapping), check we get a logon failure */
+ generic.data[generic.length-1]++;
+
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon.generic = &generic;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ /* This will break message type, check that however we still get NT_STATUS_OK */
+ for (i=0; i < 256; i++) {
+ pac_wrapped_struct.MessageType = i;
+ pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length;
+ pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type;
+ pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length;
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tmp_ctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ session_info->server_info->pac_srv_sig.signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ session_info->server_info->pac_kdc_sig.signature.data,
+ pac_wrapped_struct.SignatureLength);
+
+ ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct,
+ (ndr_push_flags_fn_t)ndr_push_PAC_Validate);
+ torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed");
+
+ torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption");
+ creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length);
+
+ generic.length = pac_wrapped.length;
+ generic.data = pac_wrapped.data;
+
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon.generic = &generic;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+ }
+
+ /* This will break the parsing nicely (even in the crypto wrapping), check we get INVALID_PARAMETER */
+ generic.length--;
+
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon.generic = &generic;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ pac_wrapped_struct.MessageType = 0x3;
+ pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length;
+ pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type;
+
+ /* Break the SignatureType */
+ pac_wrapped_struct.SignatureType++;
+
+ pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length;
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tmp_ctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ session_info->server_info->pac_srv_sig.signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ session_info->server_info->pac_kdc_sig.signature.data,
+ pac_wrapped_struct.SignatureLength);
+
+ ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct,
+ (ndr_push_flags_fn_t)ndr_push_PAC_Validate);
+ torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed");
+
+ torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption");
+ creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length);
+
+ generic.length = pac_wrapped.length;
+ generic.data = pac_wrapped.data;
+
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon.generic = &generic;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+
+ pac_wrapped_struct.MessageType = 0x3;
+ pac_wrapped_struct.ChecksumLength = session_info->server_info->pac_srv_sig.signature.length;
+ pac_wrapped_struct.SignatureType = session_info->server_info->pac_kdc_sig.type;
+ pac_wrapped_struct.SignatureLength = session_info->server_info->pac_kdc_sig.signature.length;
+
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tmp_ctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ session_info->server_info->pac_srv_sig.signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ session_info->server_info->pac_kdc_sig.signature.data,
+ pac_wrapped_struct.SignatureLength);
+
+ /* Break the signature length */
+ pac_wrapped_struct.SignatureLength++;
+
+ ndr_err = ndr_push_struct_blob(&pac_wrapped, tmp_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_wrapped_struct,
+ (ndr_push_flags_fn_t)ndr_push_PAC_Validate);
+ torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_push_struct_blob of PACValidate structure failed");
+
+ torture_assert(tctx, (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR), "not willing to even try a PACValidate without RC4 encryption");
+ creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length);
+
+ generic.length = pac_wrapped.length;
+ generic.data = pac_wrapped.data;
+
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon.generic = &generic;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(credentials);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ status = dcerpc_netr_LogonSamLogon(p, tctx, &r);
+
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed");
+
+ torture_assert(tctx, creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+ return true;
+}
+
+struct torture_suite *torture_rpc_remote_pac(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "PAC");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_machine_rpc_iface_tcase(suite, "netlogon",
+ &ndr_table_netlogon, TEST_MACHINE_NAME);
+ torture_rpc_tcase_add_test_creds(tcase, "verify", test_PACVerify);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c
new file mode 100644
index 0000000000..85f7bde16c
--- /dev/null
+++ b/source4/torture/rpc/rpc.c
@@ -0,0 +1,446 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Andrew Tridgell 1997-2003
+ Copyright (C) Jelmer Vernooij 2006
+
+ 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 "includes.h"
+#include "auth/credentials/credentials.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+#include "torture/smbtorture.h"
+#include "librpc/ndr/ndr_table.h"
+#include "lib/util/dlinklist.h"
+
+static bool torture_rpc_teardown (struct torture_context *tcase,
+ void *data)
+{
+ struct torture_rpc_tcase_data *tcase_data =
+ (struct torture_rpc_tcase_data *)data;
+ if (tcase_data->join_ctx != NULL)
+ torture_leave_domain(tcase, tcase_data->join_ctx);
+ talloc_free(tcase_data);
+ return true;
+}
+
+/**
+ * Obtain the DCE/RPC binding context associated with a torture context.
+ *
+ * @param tctx Torture context
+ * @param binding Pointer to store DCE/RPC binding
+ */
+NTSTATUS torture_rpc_binding(struct torture_context *tctx,
+ struct dcerpc_binding **binding)
+{
+ NTSTATUS status;
+ const char *binding_string = torture_setting_string(tctx, "binding",
+ NULL);
+
+ if (binding_string == NULL) {
+ torture_comment(tctx,
+ "You must specify a DCE/RPC binding string\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_parse_binding(tctx, binding_string, binding);
+ if (NT_STATUS_IS_ERR(status)) {
+ DEBUG(0,("Failed to parse dcerpc binding '%s'\n",
+ binding_string));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * open a rpc connection to the chosen binding string
+ */
+_PUBLIC_ NTSTATUS torture_rpc_connection(struct torture_context *tctx,
+ struct dcerpc_pipe **p,
+ const struct ndr_interface_table *table)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *binding;
+
+ status = torture_rpc_binding(tctx, &binding);
+ if (NT_STATUS_IS_ERR(status))
+ return status;
+
+ status = dcerpc_pipe_connect_b(tctx,
+ p, binding, table,
+ cmdline_credentials, tctx->ev, tctx->lp_ctx);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ printf("Failed to connect to remote server: %s %s\n",
+ dcerpc_binding_string(tctx, binding), nt_errstr(status));
+ }
+
+ return status;
+}
+
+/**
+ * open a rpc connection to a specific transport
+ */
+NTSTATUS torture_rpc_connection_transport(struct torture_context *tctx,
+ struct dcerpc_pipe **p,
+ const struct ndr_interface_table *table,
+ enum dcerpc_transport_t transport,
+ uint32_t assoc_group_id)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *binding;
+
+ status = torture_rpc_binding(tctx, &binding);
+ if (NT_STATUS_IS_ERR(status))
+ return status;
+
+ binding->transport = transport;
+ binding->assoc_group_id = assoc_group_id;
+
+ status = dcerpc_pipe_connect_b(tctx, p, binding, table,
+ cmdline_credentials, tctx->ev, tctx->lp_ctx);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ *p = NULL;
+ }
+
+ return status;
+}
+
+static bool torture_rpc_setup_machine(struct torture_context *tctx,
+ void **data)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *binding;
+ struct torture_rpc_tcase *tcase = talloc_get_type(tctx->active_tcase,
+ struct torture_rpc_tcase);
+ struct torture_rpc_tcase_data *tcase_data;
+
+ status = torture_rpc_binding(tctx, &binding);
+ if (NT_STATUS_IS_ERR(status))
+ return false;
+
+ *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data);
+ tcase_data->credentials = cmdline_credentials;
+ tcase_data->join_ctx = torture_join_domain(tctx, tcase->machine_name,
+ ACB_SVRTRUST,
+ &tcase_data->credentials);
+ if (tcase_data->join_ctx == NULL)
+ torture_fail(tctx, "Failed to join as BDC");
+
+ status = dcerpc_pipe_connect_b(tctx,
+ &(tcase_data->pipe),
+ binding,
+ tcase->table,
+ tcase_data->credentials, tctx->ev, tctx->lp_ctx);
+
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ return true;
+}
+
+_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_machine_rpc_iface_tcase(
+ struct torture_suite *suite,
+ const char *name,
+ const struct ndr_interface_table *table,
+ const char *machine_name)
+{
+ struct torture_rpc_tcase *tcase = talloc(suite,
+ struct torture_rpc_tcase);
+
+ torture_suite_init_rpc_tcase(suite, tcase, name, table);
+
+ tcase->machine_name = talloc_strdup(tcase, machine_name);
+ tcase->tcase.setup = torture_rpc_setup_machine;
+ tcase->tcase.teardown = torture_rpc_teardown;
+
+ return tcase;
+}
+
+_PUBLIC_ bool torture_suite_init_rpc_tcase(struct torture_suite *suite,
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ const struct ndr_interface_table *table)
+{
+ if (!torture_suite_init_tcase(suite, (struct torture_tcase *)tcase, name))
+ return false;
+
+ tcase->table = table;
+
+ return true;
+}
+
+static bool torture_rpc_setup_anonymous(struct torture_context *tctx,
+ void **data)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *binding;
+ struct torture_rpc_tcase_data *tcase_data;
+ struct torture_rpc_tcase *tcase = talloc_get_type(tctx->active_tcase,
+ struct torture_rpc_tcase);
+
+ status = torture_rpc_binding(tctx, &binding);
+ if (NT_STATUS_IS_ERR(status))
+ return false;
+
+ *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data);
+ tcase_data->credentials = cli_credentials_init_anon(tctx);
+
+ status = dcerpc_pipe_connect_b(tctx,
+ &(tcase_data->pipe),
+ binding,
+ tcase->table,
+ tcase_data->credentials, tctx->ev, tctx->lp_ctx);
+
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ return true;
+}
+
+static bool torture_rpc_setup (struct torture_context *tctx, void **data)
+{
+ NTSTATUS status;
+ struct torture_rpc_tcase *tcase = talloc_get_type(
+ tctx->active_tcase, struct torture_rpc_tcase);
+ struct torture_rpc_tcase_data *tcase_data;
+
+ *data = tcase_data = talloc_zero(tctx, struct torture_rpc_tcase_data);
+ tcase_data->credentials = cmdline_credentials;
+
+ status = torture_rpc_connection(tctx,
+ &(tcase_data->pipe),
+ tcase->table);
+
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ return true;
+}
+
+
+
+_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_anon_rpc_iface_tcase(struct torture_suite *suite,
+ const char *name,
+ const struct ndr_interface_table *table)
+{
+ struct torture_rpc_tcase *tcase = talloc(suite, struct torture_rpc_tcase);
+
+ torture_suite_init_rpc_tcase(suite, tcase, name, table);
+
+ tcase->tcase.setup = torture_rpc_setup_anonymous;
+ tcase->tcase.teardown = torture_rpc_teardown;
+
+ return tcase;
+}
+
+
+_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_rpc_iface_tcase(struct torture_suite *suite,
+ const char *name,
+ const struct ndr_interface_table *table)
+{
+ struct torture_rpc_tcase *tcase = talloc(suite, struct torture_rpc_tcase);
+
+ torture_suite_init_rpc_tcase(suite, tcase, name, table);
+
+ tcase->tcase.setup = torture_rpc_setup;
+ tcase->tcase.teardown = torture_rpc_teardown;
+
+ return tcase;
+}
+
+static bool torture_rpc_wrap_test(struct torture_context *tctx,
+ struct torture_tcase *tcase,
+ struct torture_test *test)
+{
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *);
+ struct torture_rpc_tcase_data *tcase_data =
+ (struct torture_rpc_tcase_data *)tcase->data;
+
+ fn = test->fn;
+
+ return fn(tctx, tcase_data->pipe);
+}
+
+static bool torture_rpc_wrap_test_ex(struct torture_context *tctx,
+ struct torture_tcase *tcase,
+ struct torture_test *test)
+{
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *, const void *);
+ struct torture_rpc_tcase_data *tcase_data =
+ (struct torture_rpc_tcase_data *)tcase->data;
+
+ fn = test->fn;
+
+ return fn(tctx, tcase_data->pipe, test->data);
+}
+
+
+static bool torture_rpc_wrap_test_creds(struct torture_context *tctx,
+ struct torture_tcase *tcase,
+ struct torture_test *test)
+{
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *);
+ struct torture_rpc_tcase_data *tcase_data =
+ (struct torture_rpc_tcase_data *)tcase->data;
+
+ fn = test->fn;
+
+ return fn(tctx, tcase_data->pipe, tcase_data->credentials);
+}
+
+_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *))
+{
+ struct torture_test *test;
+
+ test = talloc(tcase, struct torture_test);
+
+ test->name = talloc_strdup(test, name);
+ test->description = NULL;
+ test->run = torture_rpc_wrap_test;
+ test->dangerous = false;
+ test->data = NULL;
+ test->fn = fn;
+
+ DLIST_ADD(tcase->tcase.tests, test);
+
+ return test;
+}
+
+_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_creds(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *))
+{
+ struct torture_test *test;
+
+ test = talloc(tcase, struct torture_test);
+
+ test->name = talloc_strdup(test, name);
+ test->description = NULL;
+ test->run = torture_rpc_wrap_test_creds;
+ test->dangerous = false;
+ test->data = NULL;
+ test->fn = fn;
+
+ DLIST_ADD(tcase->tcase.tests, test);
+
+ return test;
+}
+
+_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_ex(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *,
+ void *),
+ void *userdata)
+{
+ struct torture_test *test;
+
+ test = talloc(tcase, struct torture_test);
+
+ test->name = talloc_strdup(test, name);
+ test->description = NULL;
+ test->run = torture_rpc_wrap_test_ex;
+ test->dangerous = false;
+ test->data = userdata;
+ test->fn = fn;
+
+ DLIST_ADD(tcase->tcase.tests, test);
+
+ return test;
+}
+
+NTSTATUS torture_rpc_init(void)
+{
+ struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "RPC");
+
+ dcerpc_init();
+
+ ndr_table_init();
+
+ torture_suite_add_simple_test(suite, "LSA", torture_rpc_lsa);
+ torture_suite_add_simple_test(suite, "LSALOOKUP", torture_rpc_lsa_lookup);
+ torture_suite_add_simple_test(suite, "LSA-GETUSER", torture_rpc_lsa_get_user);
+ torture_suite_add_suite(suite, torture_rpc_lsa_secrets(suite));
+ torture_suite_add_suite(suite, torture_rpc_echo(suite));
+ torture_suite_add_simple_test(suite, "DFS", torture_rpc_dfs);
+ torture_suite_add_suite(suite, torture_rpc_frsapi(suite));
+ torture_suite_add_suite(suite, torture_rpc_unixinfo(suite));
+ torture_suite_add_suite(suite, torture_rpc_eventlog(suite));
+ torture_suite_add_suite(suite, torture_rpc_atsvc(suite));
+ torture_suite_add_suite(suite, torture_rpc_wkssvc(suite));
+ torture_suite_add_suite(suite, torture_rpc_handles(suite));
+ torture_suite_add_suite(suite, torture_rpc_winreg(suite));
+ torture_suite_add_simple_test(suite, "SPOOLSS", torture_rpc_spoolss);
+ torture_suite_add_suite(suite, torture_rpc_spoolss_notify(suite));
+ torture_suite_add_suite(suite, torture_rpc_spoolss_win(suite));
+ torture_suite_add_simple_test(suite, "SAMR", torture_rpc_samr);
+ torture_suite_add_simple_test(suite, "SAMR-USERS", torture_rpc_samr_users);
+ torture_suite_add_simple_test(suite, "SAMR-PASSWORDS", torture_rpc_samr_passwords);
+ torture_suite_add_suite(suite, torture_rpc_netlogon(suite));
+ torture_suite_add_suite(suite, torture_rpc_remote_pac(suite));
+ torture_suite_add_simple_test(suite, "SAMLOGON", torture_rpc_samlogon);
+ torture_suite_add_simple_test(suite, "SAMSYNC", torture_rpc_samsync);
+ torture_suite_add_simple_test(suite, "SCHANNEL", torture_rpc_schannel);
+ torture_suite_add_simple_test(suite, "SCHANNEL2", torture_rpc_schannel2);
+ torture_suite_add_simple_test(suite, "BENCH-SCHANNEL1", torture_rpc_schannel_bench1);
+ torture_suite_add_suite(suite, torture_rpc_srvsvc(suite));
+ torture_suite_add_suite(suite, torture_rpc_svcctl(suite));
+ torture_suite_add_suite(suite, torture_rpc_samr_accessmask(suite));
+ torture_suite_add_suite(suite, torture_rpc_epmapper(suite));
+ torture_suite_add_suite(suite, torture_rpc_initshutdown(suite));
+ torture_suite_add_suite(suite, torture_rpc_oxidresolve(suite));
+ torture_suite_add_suite(suite, torture_rpc_remact(suite));
+ torture_suite_add_simple_test(suite, "MGMT", torture_rpc_mgmt);
+ torture_suite_add_simple_test(suite, "SCANNER", torture_rpc_scanner);
+ torture_suite_add_simple_test(suite, "AUTOIDL", torture_rpc_autoidl);
+ torture_suite_add_simple_test(suite, "COUNTCALLS", torture_rpc_countcalls);
+ torture_suite_add_simple_test(suite, "MULTIBIND", torture_multi_bind);
+ torture_suite_add_simple_test(suite, "AUTHCONTEXT", torture_bind_authcontext);
+ torture_suite_add_simple_test(suite, "BINDSAMBA3", torture_bind_samba3);
+ torture_suite_add_simple_test(suite, "NETLOGSAMBA3", torture_netlogon_samba3);
+ torture_suite_add_simple_test(suite, "SAMBA3SESSIONKEY", torture_samba3_sessionkey);
+ torture_suite_add_simple_test(suite, "SAMBA3-SRVSVC", torture_samba3_rpc_srvsvc);
+ torture_suite_add_simple_test(suite, "SAMBA3-SHARESEC",
+ torture_samba3_rpc_sharesec);
+ torture_suite_add_simple_test(suite, "SAMBA3-GETUSERNAME",
+ torture_samba3_rpc_getusername);
+ torture_suite_add_simple_test(suite, "SAMBA3-RANDOMAUTH2",
+ torture_samba3_rpc_randomauth2);
+ torture_suite_add_simple_test(suite, "SAMBA3-LSA", torture_samba3_rpc_lsa);
+ torture_suite_add_simple_test(suite, "SAMBA3-SPOOLSS", torture_samba3_rpc_spoolss);
+ torture_suite_add_simple_test(suite, "SAMBA3-WKSSVC", torture_samba3_rpc_wkssvc);
+ torture_suite_add_simple_test(suite, "SAMBA3-WINREG", torture_samba3_rpc_winreg);
+ torture_suite_add_simple_test(suite, "DRSUAPI", torture_rpc_drsuapi);
+ torture_suite_add_simple_test(suite, "CRACKNAMES", torture_rpc_drsuapi_cracknames);
+ torture_suite_add_suite(suite, torture_rpc_dssetup(suite));
+ torture_suite_add_simple_test(suite, "SAMBA3-REGCONFIG", torture_samba3_regconfig);
+ torture_suite_add_simple_test(suite, "ALTERCONTEXT", torture_rpc_alter_context);
+ torture_suite_add_simple_test(suite, "JOIN", torture_rpc_join);
+ torture_suite_add_simple_test(suite, "DSSYNC", torture_rpc_dssync);
+ torture_suite_add_simple_test(suite, "BENCH-RPC", torture_bench_rpc);
+ torture_suite_add_simple_test(suite, "ASYNCBIND", torture_async_bind);
+
+ suite->description = talloc_strdup(suite, "DCE/RPC protocol and interface tests");
+
+ torture_register_suite(suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/rpc/rpc.h b/source4/torture/rpc/rpc.h
new file mode 100644
index 0000000000..29b1ebee54
--- /dev/null
+++ b/source4/torture/rpc/rpc.h
@@ -0,0 +1,89 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Andrew Tridgell 1997-2003
+ Copyright (C) Jelmer Vernooij 2006
+
+ 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/>.
+*/
+
+#ifndef __TORTURE_RPC_H__
+#define __TORTURE_RPC_H__
+
+#include "torture/torture.h"
+#include "auth/credentials/credentials.h"
+#include "torture/rpc/drsuapi.h"
+#include "libnet/libnet_join.h"
+#include "librpc/rpc/dcerpc.h"
+#include "libcli/raw/libcliraw.h"
+#include "torture/rpc/proto.h"
+#include "torture/torture.h"
+
+struct torture_rpc_tcase {
+ struct torture_tcase tcase;
+ const struct ndr_interface_table *table;
+ const char *machine_name;
+};
+
+struct torture_rpc_tcase_data {
+ struct test_join *join_ctx;
+ struct dcerpc_pipe *pipe;
+ struct cli_credentials *credentials;
+};
+
+NTSTATUS torture_rpc_connection(struct torture_context *tctx,
+ struct dcerpc_pipe **p,
+ const struct ndr_interface_table *table);
+
+struct test_join *torture_join_domain(struct torture_context *tctx,
+ const char *machine_name,
+ uint32_t acct_flags,
+ struct cli_credentials **machine_credentials);
+const struct dom_sid *torture_join_sid(struct test_join *join);
+void torture_leave_domain(struct torture_context *tctx, struct test_join *join);
+struct torture_rpc_tcase *torture_suite_add_rpc_iface_tcase(struct torture_suite *suite,
+ const char *name,
+ const struct ndr_interface_table *table);
+
+struct torture_test *torture_rpc_tcase_add_test(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *));
+struct torture_rpc_tcase *torture_suite_add_anon_rpc_iface_tcase(struct torture_suite *suite,
+ const char *name,
+ const struct ndr_interface_table *table);
+
+struct torture_test *torture_rpc_tcase_add_test_ex(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *,
+ void *),
+ void *userdata);
+struct torture_rpc_tcase *torture_suite_add_machine_rpc_iface_tcase(
+ struct torture_suite *suite,
+ const char *name,
+ const struct ndr_interface_table *table,
+ const char *machine_name);
+struct torture_test *torture_rpc_tcase_add_test_creds(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *));
+bool torture_suite_init_rpc_tcase(struct torture_suite *suite,
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ const struct ndr_interface_table *table);
+
+
+
+#endif /* __TORTURE_RPC_H__ */
diff --git a/source4/torture/rpc/samba3rpc.c b/source4/torture/rpc/samba3rpc.c
new file mode 100644
index 0000000000..17342f9b86
--- /dev/null
+++ b/source4/torture/rpc/samba3rpc.c
@@ -0,0 +1,3400 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc torture tests, designed to walk Samba3 code paths
+
+ Copyright (C) Volker Lendecke 2006
+
+ 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 "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/rap/rap.h"
+#include "torture/torture.h"
+#include "torture/util.h"
+#include "torture/rap/proto.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "librpc/gen_ndr/ndr_winreg.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+#include "librpc/gen_ndr/ndr_wkssvc.h"
+#include "librpc/gen_ndr/ndr_wkssvc_c.h"
+#include "lib/cmdline/popt_common.h"
+#include "librpc/rpc/dcerpc.h"
+#include "torture/rpc/rpc.h"
+#include "libcli/libcli.h"
+#include "libcli/composite/composite.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/auth/libcli_auth.h"
+#include "lib/crypto/crypto.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "libcli/security/proto.h"
+#include "param/param.h"
+#include "lib/registry/registry.h"
+#include "libcli/resolve/resolve.h"
+
+/*
+ * This tests a RPC call using an invalid vuid
+ */
+
+bool torture_bind_authcontext(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct lsa_ObjectAttribute objectattr;
+ struct lsa_OpenPolicy2 openpolicy;
+ struct policy_handle handle;
+ struct lsa_Close close_handle;
+ struct smbcli_session *tmp;
+ struct smbcli_session *session2;
+ struct smbcli_state *cli;
+ struct dcerpc_pipe *lsa_pipe;
+ struct cli_credentials *anon_creds;
+ struct smb_composite_sesssetup setup;
+ struct smbcli_options options;
+
+ mem_ctx = talloc_init("torture_bind_authcontext");
+
+ if (mem_ctx == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL, cmdline_credentials,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ lsa_pipe = dcerpc_pipe_init(mem_ctx, cli->transport->socket->event.ctx,
+ lp_iconv_convenience(torture->lp_ctx));
+ if (lsa_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(lsa_pipe, cli->tree, "\\lsarpc");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_bind_auth_none(lsa_pipe, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ openpolicy.in.system_name =talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(lsa_pipe));
+ ZERO_STRUCT(objectattr);
+ openpolicy.in.attr = &objectattr;
+ openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ openpolicy.out.handle = &handle;
+
+ status = dcerpc_lsa_OpenPolicy2(lsa_pipe, mem_ctx, &openpolicy);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ close_handle.in.handle = &handle;
+ close_handle.out.handle = &handle;
+
+ status = dcerpc_lsa_Close(lsa_pipe, mem_ctx, &close_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_Close failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ session2 = smbcli_session_init(cli->transport, mem_ctx, false);
+ if (session2 == NULL) {
+ d_printf("smbcli_session_init failed\n");
+ goto done;
+ }
+
+ if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) {
+ d_printf("create_anon_creds failed\n");
+ goto done;
+ }
+
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities;
+ setup.in.workgroup = "";
+ setup.in.credentials = anon_creds;
+
+ status = smb_composite_sesssetup(session2, &setup);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("anon session setup failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ session2->vuid = setup.out.vuid;
+
+ tmp = cli->tree->session;
+ cli->tree->session = session2;
+
+ status = dcerpc_lsa_OpenPolicy2(lsa_pipe, mem_ctx, &openpolicy);
+
+ cli->tree->session = tmp;
+ talloc_free(lsa_pipe);
+ lsa_pipe = NULL;
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ d_printf("dcerpc_lsa_OpenPolicy2 with wrong vuid gave %s, "
+ "expected NT_STATUS_INVALID_HANDLE\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Bind to lsa using a specific auth method
+ */
+
+static bool bindtest(struct smbcli_state *cli,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ uint8_t auth_type, uint8_t auth_level)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = false;
+ NTSTATUS status;
+
+ struct dcerpc_pipe *lsa_pipe;
+ struct lsa_ObjectAttribute objectattr;
+ struct lsa_OpenPolicy2 openpolicy;
+ struct lsa_QueryInfoPolicy query;
+ struct policy_handle handle;
+ struct lsa_Close close_handle;
+
+ if ((mem_ctx = talloc_init("bindtest")) == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ lsa_pipe = dcerpc_pipe_init(mem_ctx,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx));
+ if (lsa_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(lsa_pipe, cli->tree, "\\lsarpc");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_bind_auth(lsa_pipe, &ndr_table_lsarpc,
+ credentials, lp_ctx, auth_type, auth_level,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ openpolicy.in.system_name =talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(lsa_pipe));
+ ZERO_STRUCT(objectattr);
+ openpolicy.in.attr = &objectattr;
+ openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ openpolicy.out.handle = &handle;
+
+ status = dcerpc_lsa_OpenPolicy2(lsa_pipe, mem_ctx, &openpolicy);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ query.in.handle = &handle;
+ query.in.level = LSA_POLICY_INFO_DOMAIN;
+
+ status = dcerpc_lsa_QueryInfoPolicy(lsa_pipe, mem_ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_QueryInfoPolicy failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ close_handle.in.handle = &handle;
+ close_handle.out.handle = &handle;
+
+ status = dcerpc_lsa_Close(lsa_pipe, mem_ctx, &close_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_lsa_Close failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * test authenticated RPC binds with the variants Samba3 does support
+ */
+
+bool torture_bind_samba3(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_state *cli;
+ struct smbcli_options options;
+
+ mem_ctx = talloc_init("torture_bind_authcontext");
+
+ if (mem_ctx == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL, cmdline_credentials,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+
+ ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_INTEGRITY);
+ ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_PRIVACY);
+ ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_SPNEGO,
+ DCERPC_AUTH_LEVEL_INTEGRITY);
+ ret &= bindtest(cli, cmdline_credentials, torture->lp_ctx, DCERPC_AUTH_TYPE_SPNEGO,
+ DCERPC_AUTH_LEVEL_PRIVACY);
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Lookup or create a user and return all necessary info
+ */
+
+static NTSTATUS get_usr_handle(struct smbcli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *admin_creds,
+ uint8_t auth_type,
+ uint8_t auth_level,
+ const char *username,
+ char **domain,
+ struct dcerpc_pipe **result_pipe,
+ struct policy_handle **result_handle,
+ struct dom_sid **sid)
+{
+ struct dcerpc_pipe *samr_pipe;
+ NTSTATUS status;
+ struct policy_handle conn_handle;
+ struct policy_handle domain_handle;
+ struct policy_handle *user_handle;
+ struct samr_Connect2 conn;
+ struct samr_EnumDomains enumdom;
+ uint32_t resume_handle = 0;
+ struct samr_LookupDomain l;
+ int dom_idx;
+ struct lsa_String domain_name;
+ struct lsa_String user_name;
+ struct samr_OpenDomain o;
+ struct samr_CreateUser2 c;
+ uint32_t user_rid,access_granted;
+
+ samr_pipe = dcerpc_pipe_init(mem_ctx,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx));
+ if (samr_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = dcerpc_pipe_open_smb(samr_pipe, cli->tree, "\\samr");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ if (admin_creds != NULL) {
+ status = dcerpc_bind_auth(samr_pipe, &ndr_table_samr,
+ admin_creds, lp_ctx, auth_type, auth_level,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ } else {
+ /* We must have an authenticated SMB connection */
+ status = dcerpc_bind_auth_none(samr_pipe, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ }
+
+ conn.in.system_name = talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(samr_pipe));
+ conn.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ conn.out.connect_handle = &conn_handle;
+
+ status = dcerpc_samr_Connect2(samr_pipe, mem_ctx, &conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_Connect2 failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ enumdom.in.connect_handle = &conn_handle;
+ enumdom.in.resume_handle = &resume_handle;
+ enumdom.in.buf_size = (uint32_t)-1;
+ enumdom.out.resume_handle = &resume_handle;
+
+ status = dcerpc_samr_EnumDomains(samr_pipe, mem_ctx, &enumdom);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_EnumDomains failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ if (enumdom.out.num_entries != 2) {
+ d_printf("samr_EnumDomains returned %d entries, expected 2\n",
+ enumdom.out.num_entries);
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto fail;
+ }
+
+ dom_idx = strequal(enumdom.out.sam->entries[0].name.string,
+ "builtin") ? 1:0;
+
+ l.in.connect_handle = &conn_handle;
+ domain_name.string = enumdom.out.sam->entries[dom_idx].name.string;
+ *domain = talloc_strdup(mem_ctx, domain_name.string);
+ l.in.domain_name = &domain_name;
+
+ status = dcerpc_samr_LookupDomain(samr_pipe, mem_ctx, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_LookupDomain failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ o.in.connect_handle = &conn_handle;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.in.sid = l.out.sid;
+ o.out.domain_handle = &domain_handle;
+
+ status = dcerpc_samr_OpenDomain(samr_pipe, mem_ctx, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_OpenDomain failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ c.in.domain_handle = &domain_handle;
+ user_name.string = username;
+ c.in.account_name = &user_name;
+ c.in.acct_flags = ACB_NORMAL;
+ c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ user_handle = talloc(mem_ctx, struct policy_handle);
+ c.out.user_handle = user_handle;
+ c.out.access_granted = &access_granted;
+ c.out.rid = &user_rid;
+
+ status = dcerpc_samr_CreateUser2(samr_pipe, mem_ctx, &c);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ struct samr_LookupNames ln;
+ struct samr_OpenUser ou;
+
+ ln.in.domain_handle = &domain_handle;
+ ln.in.num_names = 1;
+ ln.in.names = &user_name;
+
+ status = dcerpc_samr_LookupNames(samr_pipe, mem_ctx, &ln);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_LookupNames failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ ou.in.domain_handle = &domain_handle;
+ ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ user_rid = ou.in.rid = ln.out.rids.ids[0];
+ ou.out.user_handle = user_handle;
+
+ status = dcerpc_samr_OpenUser(samr_pipe, mem_ctx, &ou);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_OpenUser failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_CreateUser failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ *result_pipe = samr_pipe;
+ *result_handle = user_handle;
+ if (sid != NULL) {
+ *sid = dom_sid_add_rid(mem_ctx, l.out.sid, user_rid);
+ }
+ return NT_STATUS_OK;
+
+ fail:
+ return status;
+}
+
+/*
+ * Create a test user
+ */
+
+static bool create_user(TALLOC_CTX *mem_ctx, struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *admin_creds,
+ const char *username, const char *password,
+ char **domain_name,
+ struct dom_sid **user_sid)
+{
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+ struct dcerpc_pipe *samr_pipe;
+ struct policy_handle *wks_handle;
+ bool ret = false;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ status = get_usr_handle(cli, tmp_ctx, lp_ctx, admin_creds,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_INTEGRITY,
+ username, domain_name, &samr_pipe, &wks_handle,
+ user_sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_usr_handle failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ {
+ struct samr_SetUserInfo2 sui2;
+ struct samr_SetUserInfo sui;
+ struct samr_QueryUserInfo qui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+
+
+ ZERO_STRUCT(u_info);
+ encode_pw_buffer(u_info.info23.password.data, password,
+ STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(samr_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_fetch_session_key failed\n");
+ goto done;
+ }
+ arcfour_crypt_blob(u_info.info23.password.data, 516,
+ &session_key);
+ u_info.info23.info.password_expired = 0;
+ u_info.info23.info.fields_present = SAMR_FIELD_PASSWORD |
+ SAMR_FIELD_PASSWORD2 |
+ SAMR_FIELD_EXPIRED_FLAG;
+ sui2.in.user_handle = wks_handle;
+ sui2.in.info = &u_info;
+ sui2.in.level = 23;
+
+ status = dcerpc_samr_SetUserInfo2(samr_pipe, tmp_ctx, &sui2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_SetUserInfo(23) failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ u_info.info16.acct_flags = ACB_NORMAL;
+ sui.in.user_handle = wks_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 16;
+
+ status = dcerpc_samr_SetUserInfo(samr_pipe, tmp_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_SetUserInfo(16) failed\n");
+ goto done;
+ }
+
+ qui.in.user_handle = wks_handle;
+ qui.in.level = 21;
+
+ status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_QueryUserInfo(21) failed\n");
+ goto done;
+ }
+
+ qui.out.info->info21.allow_password_change = 0;
+ qui.out.info->info21.force_password_change = 0;
+ qui.out.info->info21.account_name.string = NULL;
+ qui.out.info->info21.rid = 0;
+ qui.out.info->info21.acct_expiry = 0;
+ qui.out.info->info21.fields_present = 0x81827fa; /* copy usrmgr.exe */
+
+ u_info.info21 = qui.out.info->info21;
+ sui.in.user_handle = wks_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 21;
+
+ status = dcerpc_samr_SetUserInfo(samr_pipe, tmp_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_SetUserInfo(21) failed\n");
+ goto done;
+ }
+ }
+
+ *domain_name= talloc_steal(mem_ctx, *domain_name);
+ *user_sid = talloc_steal(mem_ctx, *user_sid);
+ ret = true;
+ done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+/*
+ * Delete a test user
+ */
+
+static bool delete_user(struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *admin_creds,
+ const char *username)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ char *dom_name;
+ struct dcerpc_pipe *samr_pipe;
+ struct policy_handle *user_handle;
+ bool ret = false;
+
+ if ((mem_ctx = talloc_init("leave")) == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ status = get_usr_handle(cli, mem_ctx, lp_ctx, admin_creds,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_INTEGRITY,
+ username, &dom_name, &samr_pipe,
+ &user_handle, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_wks_handle failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ {
+ struct samr_DeleteUser d;
+
+ d.in.user_handle = user_handle;
+ d.out.user_handle = user_handle;
+
+ status = dcerpc_samr_DeleteUser(samr_pipe, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_DeleteUser failed %s\n", nt_errstr(status));
+ goto done;
+ }
+ }
+
+ ret = true;
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Do a Samba3-style join
+ */
+
+static bool join3(struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ bool use_level25,
+ struct cli_credentials *admin_creds,
+ struct cli_credentials *wks_creds)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ char *dom_name;
+ struct dcerpc_pipe *samr_pipe;
+ struct policy_handle *wks_handle;
+ bool ret = false;
+ NTTIME last_password_change;
+
+ if ((mem_ctx = talloc_init("join3")) == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ status = get_usr_handle(
+ cli, mem_ctx, lp_ctx, admin_creds,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_PRIVACY,
+ talloc_asprintf(mem_ctx, "%s$",
+ cli_credentials_get_workstation(wks_creds)),
+ &dom_name, &samr_pipe, &wks_handle, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_wks_handle failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ {
+ struct samr_QueryUserInfo q;
+
+ q.in.user_handle = wks_handle;
+ q.in.level = 21;
+
+ status = dcerpc_samr_QueryUserInfo(samr_pipe, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) QueryUserInfo failed: %s\n",
+ __location__, nt_errstr(status));
+ goto done;
+ }
+
+ last_password_change = q.out.info->info21.last_password_change;
+ }
+
+ cli_credentials_set_domain(wks_creds, dom_name, CRED_SPECIFIED);
+
+ if (use_level25) {
+ struct samr_SetUserInfo2 sui2;
+ union samr_UserInfo u_info;
+ struct samr_UserInfo21 *i21 = &u_info.info25.info;
+ DATA_BLOB session_key;
+ DATA_BLOB confounded_session_key = data_blob_talloc(
+ mem_ctx, NULL, 16);
+ struct MD5Context ctx;
+ uint8_t confounder[16];
+
+ ZERO_STRUCT(u_info);
+
+ i21->full_name.string = talloc_asprintf(
+ mem_ctx, "%s$",
+ cli_credentials_get_workstation(wks_creds));
+ i21->acct_flags = ACB_WSTRUST;
+ i21->fields_present = SAMR_FIELD_FULL_NAME |
+ SAMR_FIELD_ACCT_FLAGS | SAMR_FIELD_PASSWORD;
+
+ encode_pw_buffer(u_info.info25.password.data,
+ cli_credentials_get_password(wks_creds),
+ STR_UNICODE);
+ status = dcerpc_fetch_session_key(samr_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_fetch_session_key failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, confounder, 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(u_info.info25.password.data, 516,
+ &confounded_session_key);
+ memcpy(&u_info.info25.password.data[516], confounder, 16);
+
+ sui2.in.user_handle = wks_handle;
+ sui2.in.level = 25;
+ sui2.in.info = &u_info;
+
+ status = dcerpc_samr_SetUserInfo2(samr_pipe, mem_ctx, &sui2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_SetUserInfo2(25) failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ } else {
+ struct samr_SetUserInfo2 sui2;
+ struct samr_SetUserInfo sui;
+ union samr_UserInfo u_info;
+ DATA_BLOB session_key;
+
+ encode_pw_buffer(u_info.info24.password.data,
+ cli_credentials_get_password(wks_creds),
+ STR_UNICODE);
+ u_info.info24.pw_len =
+ strlen_m(cli_credentials_get_password(wks_creds))*2;
+
+ status = dcerpc_fetch_session_key(samr_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_fetch_session_key failed\n");
+ goto done;
+ }
+ arcfour_crypt_blob(u_info.info24.password.data, 516,
+ &session_key);
+ sui2.in.user_handle = wks_handle;
+ sui2.in.info = &u_info;
+ sui2.in.level = 24;
+
+ status = dcerpc_samr_SetUserInfo2(samr_pipe, mem_ctx, &sui2);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_SetUserInfo(24) failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ u_info.info16.acct_flags = ACB_WSTRUST;
+ sui.in.user_handle = wks_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 16;
+
+ status = dcerpc_samr_SetUserInfo(samr_pipe, mem_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("samr_SetUserInfo(16) failed\n");
+ goto done;
+ }
+ }
+
+ {
+ struct samr_QueryUserInfo q;
+
+ q.in.user_handle = wks_handle;
+ q.in.level = 21;
+
+ status = dcerpc_samr_QueryUserInfo(samr_pipe, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) QueryUserInfo failed: %s\n",
+ __location__, nt_errstr(status));
+ goto done;
+ }
+
+ if (use_level25) {
+ if (last_password_change
+ == q.out.info->info21.last_password_change) {
+ d_printf("(%s) last_password_change unchanged "
+ "during join, level25 must change "
+ "it\n", __location__);
+ goto done;
+ }
+ }
+ else {
+ if (last_password_change
+ != q.out.info->info21.last_password_change) {
+ d_printf("(%s) last_password_change changed "
+ "during join, level24 doesn't "
+ "change it\n", __location__);
+ goto done;
+ }
+ }
+ }
+
+ ret = true;
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Do a ReqChallenge/Auth2 and get the wks creds
+ */
+
+static bool auth2(struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *wks_cred)
+{
+ TALLOC_CTX *mem_ctx;
+ struct dcerpc_pipe *net_pipe;
+ bool result = false;
+ NTSTATUS status;
+ struct netr_ServerReqChallenge r;
+ struct netr_Credential netr_cli_creds;
+ struct netr_Credential netr_srv_creds;
+ uint32_t negotiate_flags;
+ struct netr_ServerAuthenticate2 a;
+ struct creds_CredentialState *creds_state;
+ struct netr_Credential netr_cred;
+ struct samr_Password mach_pw;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ d_printf("talloc_new failed\n");
+ return false;
+ }
+
+ net_pipe = dcerpc_pipe_init(mem_ctx,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx));
+ if (net_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(net_pipe, cli->tree, "\\netlogon");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_bind_auth_none(net_pipe, &ndr_table_netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ r.in.computer_name = cli_credentials_get_workstation(wks_cred);
+ r.in.server_name = talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe));
+ if (r.in.server_name == NULL) {
+ d_printf("talloc_asprintf failed\n");
+ goto done;
+ }
+ generate_random_buffer(netr_cli_creds.data,
+ sizeof(netr_cli_creds.data));
+ r.in.credentials = &netr_cli_creds;
+ r.out.credentials = &netr_srv_creds;
+
+ status = dcerpc_netr_ServerReqChallenge(net_pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_ServerReqChallenge failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ E_md4hash(cli_credentials_get_password(wks_cred), mach_pw.hash);
+
+ creds_state = talloc(mem_ctx, struct creds_CredentialState);
+ creds_client_init(creds_state, r.in.credentials,
+ r.out.credentials, &mach_pw,
+ &netr_cred, negotiate_flags);
+
+ a.in.server_name = talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe));
+ a.in.account_name = talloc_asprintf(
+ mem_ctx, "%s$", cli_credentials_get_workstation(wks_cred));
+ a.in.computer_name = cli_credentials_get_workstation(wks_cred);
+ a.in.secure_channel_type = SEC_CHAN_WKSTA;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &netr_cred;
+ a.out.credentials = &netr_cred;
+
+ status = dcerpc_netr_ServerAuthenticate2(net_pipe, mem_ctx, &a);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_ServerServerAuthenticate2 failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ if (!creds_client_check(creds_state, a.out.credentials)) {
+ d_printf("creds_client_check failed\n");
+ goto done;
+ }
+
+ cli_credentials_set_netlogon_creds(wks_cred, creds_state);
+
+ result = true;
+
+ done:
+ talloc_free(mem_ctx);
+ return result;
+}
+
+/*
+ * Do a couple of schannel protected Netlogon ops: Interactive and Network
+ * login, and change the wks password
+ */
+
+static bool schan(struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *wks_creds,
+ struct cli_credentials *user_creds)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct dcerpc_pipe *net_pipe;
+ int i;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ d_printf("talloc_new failed\n");
+ return false;
+ }
+
+ net_pipe = dcerpc_pipe_init(mem_ctx,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx));
+ if (net_pipe == NULL) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(net_pipe, cli->tree, "\\netlogon");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+#if 0
+ net_pipe->conn->flags |= DCERPC_DEBUG_PRINT_IN |
+ DCERPC_DEBUG_PRINT_OUT;
+#endif
+#if 1
+ net_pipe->conn->flags |= (DCERPC_SIGN | DCERPC_SEAL);
+ status = dcerpc_bind_auth(net_pipe, &ndr_table_netlogon,
+ wks_creds, lp_ctx, DCERPC_AUTH_TYPE_SCHANNEL,
+ DCERPC_AUTH_LEVEL_PRIVACY,
+ NULL);
+#else
+ status = dcerpc_bind_auth_none(net_pipe, &ndr_table_netlogon);
+#endif
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("schannel bind failed: %s\n", nt_errstr(status));
+ goto done;
+ }
+
+
+ for (i=2; i<4; i++) {
+ int flags;
+ DATA_BLOB chal, nt_resp, lm_resp, names_blob, session_key;
+ struct creds_CredentialState *creds_state;
+ struct netr_Authenticator netr_auth, netr_auth2;
+ struct netr_NetworkInfo ninfo;
+ struct netr_PasswordInfo pinfo;
+ struct netr_LogonSamLogon r;
+
+ flags = CLI_CRED_LANMAN_AUTH | CLI_CRED_NTLM_AUTH |
+ CLI_CRED_NTLMv2_AUTH;
+
+ chal = data_blob_talloc(mem_ctx, NULL, 8);
+ if (chal.data == NULL) {
+ d_printf("data_blob_talloc failed\n");
+ goto done;
+ }
+
+ generate_random_buffer(chal.data, chal.length);
+ names_blob = NTLMv2_generate_names_blob(
+ mem_ctx, lp_iconv_convenience(lp_ctx),
+ cli_credentials_get_workstation(user_creds),
+ cli_credentials_get_domain(user_creds));
+ status = cli_credentials_get_ntlm_response(
+ user_creds, mem_ctx, &flags, chal, names_blob,
+ &lm_resp, &nt_resp, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("cli_credentials_get_ntlm_response failed:"
+ " %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ creds_state = cli_credentials_get_netlogon_creds(wks_creds);
+ creds_client_authenticator(creds_state, &netr_auth);
+
+ ninfo.identity_info.account_name.string =
+ cli_credentials_get_username(user_creds);
+ ninfo.identity_info.domain_name.string =
+ cli_credentials_get_domain(user_creds);
+ ninfo.identity_info.parameter_control = 0;
+ ninfo.identity_info.logon_id_low = 0;
+ ninfo.identity_info.logon_id_high = 0;
+ ninfo.identity_info.workstation.string =
+ cli_credentials_get_workstation(user_creds);
+ memcpy(ninfo.challenge, chal.data, sizeof(ninfo.challenge));
+ ninfo.nt.length = nt_resp.length;
+ ninfo.nt.data = nt_resp.data;
+ ninfo.lm.length = lm_resp.length;
+ ninfo.lm.data = lm_resp.data;
+
+ r.in.server_name = talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe));
+ ZERO_STRUCT(netr_auth2);
+ r.in.computer_name =
+ cli_credentials_get_workstation(wks_creds);
+ r.in.credential = &netr_auth;
+ r.in.return_authenticator = &netr_auth2;
+ r.in.logon_level = 2;
+ r.in.validation_level = i;
+ r.in.logon.network = &ninfo;
+ r.out.return_authenticator = NULL;
+
+ status = dcerpc_netr_LogonSamLogon(net_pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_LogonSamLogon failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ if ((r.out.return_authenticator == NULL) ||
+ (!creds_client_check(creds_state,
+ &r.out.return_authenticator->cred))) {
+ d_printf("Credentials check failed!\n");
+ goto done;
+ }
+
+ creds_client_authenticator(creds_state, &netr_auth);
+
+ pinfo.identity_info = ninfo.identity_info;
+ ZERO_STRUCT(pinfo.lmpassword.hash);
+ E_md4hash(cli_credentials_get_password(user_creds),
+ pinfo.ntpassword.hash);
+ session_key = data_blob_talloc(mem_ctx,
+ creds_state->session_key, 16);
+ arcfour_crypt_blob(pinfo.ntpassword.hash,
+ sizeof(pinfo.ntpassword.hash),
+ &session_key);
+
+ r.in.logon_level = 1;
+ r.in.logon.password = &pinfo;
+ r.out.return_authenticator = NULL;
+
+ status = dcerpc_netr_LogonSamLogon(net_pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_LogonSamLogon failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ if ((r.out.return_authenticator == NULL) ||
+ (!creds_client_check(creds_state,
+ &r.out.return_authenticator->cred))) {
+ d_printf("Credentials check failed!\n");
+ goto done;
+ }
+ }
+
+ {
+ struct netr_ServerPasswordSet s;
+ char *password = generate_random_str(wks_creds, 8);
+ struct creds_CredentialState *creds_state;
+
+ s.in.server_name = talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe));
+ s.in.computer_name = cli_credentials_get_workstation(wks_creds);
+ s.in.account_name = talloc_asprintf(
+ mem_ctx, "%s$", s.in.computer_name);
+ s.in.secure_channel_type = SEC_CHAN_WKSTA;
+ E_md4hash(password, s.in.new_password.hash);
+
+ creds_state = cli_credentials_get_netlogon_creds(wks_creds);
+ creds_des_encrypt(creds_state, &s.in.new_password);
+ creds_client_authenticator(creds_state, &s.in.credential);
+
+ status = dcerpc_netr_ServerPasswordSet(net_pipe, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ServerPasswordSet - %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ if (!creds_client_check(creds_state,
+ &s.out.return_authenticator.cred)) {
+ printf("Credential chaining failed\n");
+ }
+
+ cli_credentials_set_password(wks_creds, password,
+ CRED_SPECIFIED);
+ }
+
+ ret = true;
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Delete the wks account again
+ */
+
+static bool leave(struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *admin_creds,
+ struct cli_credentials *wks_creds)
+{
+ char *wks_name = talloc_asprintf(
+ NULL, "%s$", cli_credentials_get_workstation(wks_creds));
+ bool ret;
+
+ ret = delete_user(cli, lp_ctx, admin_creds, wks_name);
+ talloc_free(wks_name);
+ return ret;
+}
+
+/*
+ * Test the Samba3 DC code a bit. Join, do some schan netlogon ops, leave
+ */
+
+bool torture_netlogon_samba3(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_state *cli;
+ struct cli_credentials *anon_creds;
+ struct cli_credentials *wks_creds;
+ const char *wks_name;
+ int i;
+ struct smbcli_options options;
+
+ wks_name = torture_setting_string(torture, "wksname", NULL);
+ if (wks_name == NULL) {
+ wks_name = get_myname();
+ }
+
+ mem_ctx = talloc_init("torture_netlogon_samba3");
+
+ if (mem_ctx == NULL) {
+ d_printf("talloc_init failed\n");
+ return false;
+ }
+
+ if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) {
+ d_printf("create_anon_creds failed\n");
+ goto done;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL, anon_creds,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ wks_creds = cli_credentials_init(mem_ctx);
+ if (wks_creds == NULL) {
+ d_printf("cli_credentials_init failed\n");
+ goto done;
+ }
+
+ cli_credentials_set_conf(wks_creds, torture->lp_ctx);
+ cli_credentials_set_secure_channel_type(wks_creds, SEC_CHAN_WKSTA);
+ cli_credentials_set_username(wks_creds, wks_name, CRED_SPECIFIED);
+ cli_credentials_set_workstation(wks_creds, wks_name, CRED_SPECIFIED);
+ cli_credentials_set_password(wks_creds,
+ generate_random_str(wks_creds, 8),
+ CRED_SPECIFIED);
+
+ if (!join3(cli, torture->lp_ctx, false, cmdline_credentials, wks_creds)) {
+ d_printf("join failed\n");
+ goto done;
+ }
+
+ cli_credentials_set_domain(
+ cmdline_credentials, cli_credentials_get_domain(wks_creds),
+ CRED_SPECIFIED);
+
+ for (i=0; i<2; i++) {
+
+ /* Do this more than once, the routine "schan" changes
+ * the workstation password using the netlogon
+ * password change routine */
+
+ int j;
+
+ if (!auth2(cli, torture->lp_ctx, wks_creds)) {
+ d_printf("auth2 failed\n");
+ goto done;
+ }
+
+ for (j=0; j<2; j++) {
+ if (!schan(cli, torture->lp_ctx, wks_creds, cmdline_credentials)) {
+ d_printf("schan failed\n");
+ goto done;
+ }
+ }
+ }
+
+ if (!leave(cli, torture->lp_ctx, cmdline_credentials, wks_creds)) {
+ d_printf("leave failed\n");
+ goto done;
+ }
+
+ ret = true;
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Do a simple join, testjoin and leave using specified smb and samr
+ * credentials
+ */
+
+static bool test_join3(struct torture_context *tctx,
+ bool use_level25,
+ struct cli_credentials *smb_creds,
+ struct cli_credentials *samr_creds,
+ const char *wks_name)
+{
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_state *cli;
+ struct cli_credentials *wks_creds;
+ struct smbcli_options options;
+
+ lp_smbcli_options(tctx->lp_ctx, &options);
+
+ status = smbcli_full_connection(tctx, &cli,
+ torture_setting_string(tctx, "host", NULL),
+ lp_smb_ports(tctx->lp_ctx),
+ "IPC$", NULL, smb_creds,
+ lp_resolve_context(tctx->lp_ctx),
+ tctx->ev, &options);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ wks_creds = cli_credentials_init(cli);
+ if (wks_creds == NULL) {
+ d_printf("cli_credentials_init failed\n");
+ goto done;
+ }
+
+ cli_credentials_set_conf(wks_creds, tctx->lp_ctx);
+ cli_credentials_set_secure_channel_type(wks_creds, SEC_CHAN_WKSTA);
+ cli_credentials_set_username(wks_creds, wks_name, CRED_SPECIFIED);
+ cli_credentials_set_workstation(wks_creds, wks_name, CRED_SPECIFIED);
+ cli_credentials_set_password(wks_creds,
+ generate_random_str(wks_creds, 8),
+ CRED_SPECIFIED);
+
+ if (!join3(cli, tctx->lp_ctx, use_level25, samr_creds, wks_creds)) {
+ d_printf("join failed\n");
+ goto done;
+ }
+
+ cli_credentials_set_domain(
+ cmdline_credentials, cli_credentials_get_domain(wks_creds),
+ CRED_SPECIFIED);
+
+ if (!auth2(cli, tctx->lp_ctx, wks_creds)) {
+ d_printf("auth2 failed\n");
+ goto done;
+ }
+
+ if (!leave(cli, tctx->lp_ctx, samr_creds, wks_creds)) {
+ d_printf("leave failed\n");
+ goto done;
+ }
+
+ talloc_free(cli);
+
+ ret = true;
+
+ done:
+ return ret;
+}
+
+/*
+ * Test the different session key variants. Do it by joining, this uses the
+ * session key in the setpassword routine. Test the join by doing the auth2.
+ */
+
+bool torture_samba3_sessionkey(struct torture_context *torture)
+{
+ bool ret = false;
+ struct cli_credentials *anon_creds;
+ const char *wks_name;
+
+ wks_name = torture_setting_string(torture, "wksname", get_myname());
+
+ if (!(anon_creds = cli_credentials_init_anon(torture))) {
+ d_printf("create_anon_creds failed\n");
+ goto done;
+ }
+
+ cli_credentials_set_workstation(anon_creds, wks_name, CRED_SPECIFIED);
+
+ ret = true;
+
+ if (!torture_setting_bool(torture, "samba3", false)) {
+
+ /* Samba3 in the build farm right now does this happily. Need
+ * to fix :-) */
+
+ if (test_join3(torture, false, anon_creds, NULL, wks_name)) {
+ d_printf("join using anonymous bind on an anonymous smb "
+ "connection succeeded -- HUH??\n");
+ ret = false;
+ }
+ }
+
+ if (!test_join3(torture, false, anon_creds, cmdline_credentials,
+ wks_name)) {
+ d_printf("join using ntlmssp bind on an anonymous smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ if (!test_join3(torture, false, cmdline_credentials, NULL, wks_name)) {
+ d_printf("join using anonymous bind on an authenticated smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ if (!test_join3(torture, false, cmdline_credentials,
+ cmdline_credentials,
+ wks_name)) {
+ d_printf("join using ntlmssp bind on an authenticated smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ /*
+ * The following two are tests for setuserinfolevel 25
+ */
+
+ if (!test_join3(torture, true, anon_creds, cmdline_credentials,
+ wks_name)) {
+ d_printf("join using ntlmssp bind on an anonymous smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ if (!test_join3(torture, true, cmdline_credentials, NULL, wks_name)) {
+ d_printf("join using anonymous bind on an authenticated smb "
+ "connection failed\n");
+ ret = false;
+ }
+
+ done:
+
+ return ret;
+}
+
+/*
+ * open pipe and bind, given an IPC$ context
+ */
+
+static NTSTATUS pipe_bind_smb(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smbcli_tree *tree,
+ const char *pipe_name,
+ const struct ndr_interface_table *iface,
+ struct dcerpc_pipe **p)
+{
+ struct dcerpc_pipe *result;
+ NTSTATUS status;
+
+ if (!(result = dcerpc_pipe_init(
+ mem_ctx, tree->session->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx)))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_pipe_open_smb(result, tree, pipe_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ talloc_free(result);
+ return status;
+ }
+
+ status = dcerpc_bind_auth_none(result, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("schannel bind failed: %s\n", nt_errstr(status));
+ talloc_free(result);
+ return status;
+ }
+
+ *p = result;
+ return NT_STATUS_OK;
+}
+
+/*
+ * Sane wrapper around lsa_LookupNames
+ */
+
+static struct dom_sid *name2sid(TALLOC_CTX *mem_ctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ const char *domain)
+{
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+ struct lsa_Close c;
+ NTSTATUS status;
+ struct policy_handle handle;
+ struct lsa_LookupNames l;
+ struct lsa_TransSidArray sids;
+ struct lsa_String lsa_name;
+ uint32_t count = 0;
+ struct dom_sid *result;
+ TALLOC_CTX *tmp_ctx;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ return NULL;
+ }
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = "\\";
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ status = dcerpc_lsa_OpenPolicy2(p, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenPolicy2 failed - %s\n", nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ lsa_name.string = talloc_asprintf(tmp_ctx, "%s\\%s", domain, name);
+
+ l.in.handle = &handle;
+ l.in.num_names = 1;
+ l.in.names = &lsa_name;
+ l.in.sids = &sids;
+ l.in.level = 1;
+ l.in.count = &count;
+ l.out.count = &count;
+ l.out.sids = &sids;
+
+ status = dcerpc_lsa_LookupNames(p, tmp_ctx, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames of %s failed - %s\n", lsa_name.string,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ result = dom_sid_add_rid(mem_ctx, l.out.domains->domains[0].sid,
+ l.out.sids->sids[0].rid);
+
+ c.in.handle = &handle;
+ c.out.handle = &handle;
+
+ status = dcerpc_lsa_Close(p, tmp_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_lsa_Close failed - %s\n", nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+/*
+ * Find out the user SID on this connection
+ */
+
+static struct dom_sid *whoami(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smbcli_tree *tree)
+{
+ struct dcerpc_pipe *lsa;
+ struct lsa_GetUserName r;
+ NTSTATUS status;
+ struct lsa_StringPointer authority_name_p;
+ struct dom_sid *result;
+
+ status = pipe_bind_smb(mem_ctx, lp_ctx, tree, "\\pipe\\lsarpc",
+ &ndr_table_lsarpc, &lsa);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) Could not bind to LSA: %s\n",
+ __location__, nt_errstr(status));
+ return NULL;
+ }
+
+ r.in.system_name = "\\";
+ r.in.account_name = NULL;
+ authority_name_p.string = NULL;
+ r.in.authority_name = &authority_name_p;
+
+ status = dcerpc_lsa_GetUserName(lsa, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("(%s) GetUserName failed - %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(lsa);
+ return NULL;
+ }
+
+ result = name2sid(mem_ctx, lsa, r.out.account_name->string,
+ r.out.authority_name->string->string);
+
+ talloc_free(lsa);
+ return result;
+}
+
+static int destroy_tree(struct smbcli_tree *tree)
+{
+ smb_tree_disconnect(tree);
+ return 0;
+}
+
+/*
+ * Do a tcon, given a session
+ */
+
+NTSTATUS secondary_tcon(TALLOC_CTX *mem_ctx,
+ struct smbcli_session *session,
+ const char *sharename,
+ struct smbcli_tree **res)
+{
+ struct smbcli_tree *result;
+ TALLOC_CTX *tmp_ctx;
+ union smb_tcon tcon;
+ NTSTATUS status;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(result = smbcli_tree_init(session, mem_ctx, false))) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = 0;
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = sharename;
+ tcon.tconx.in.device = "?????";
+
+ status = smb_raw_tcon(result, tmp_ctx, &tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) smb_raw_tcon failed: %s\n", __location__,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ result->tid = tcon.tconx.out.tid;
+ result = talloc_steal(mem_ctx, result);
+ talloc_set_destructor(result, destroy_tree);
+ talloc_free(tmp_ctx);
+ *res = result;
+ return NT_STATUS_OK;
+}
+
+/*
+ * Test the getusername behaviour
+ */
+
+bool torture_samba3_rpc_getusername(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct smbcli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct dom_sid *user_sid;
+ struct dom_sid *created_sid;
+ struct cli_credentials *anon_creds;
+ struct cli_credentials *user_creds;
+ char *domain_name;
+ struct smbcli_options options;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ lp_smbcli_options(torture->lp_ctx, &options);
+
+ status = smbcli_full_connection(
+ mem_ctx, &cli, torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL, cmdline_credentials,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) smbcli_full_connection failed: %s\n",
+ __location__, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, cli->tree))) {
+ d_printf("(%s) whoami on auth'ed connection failed\n",
+ __location__);
+ ret = false;
+ }
+
+ talloc_free(cli);
+
+ if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) {
+ d_printf("(%s) create_anon_creds failed\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ status = smbcli_full_connection(
+ mem_ctx, &cli, torture_setting_string(torture, "host", NULL),
+ lp_smb_ports(torture->lp_ctx),
+ "IPC$", NULL, anon_creds,
+ lp_resolve_context(torture->lp_ctx),
+ torture->ev, &options);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) anon smbcli_full_connection failed: %s\n",
+ __location__, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, cli->tree))) {
+ d_printf("(%s) whoami on anon connection failed\n",
+ __location__);
+ ret = false;
+ goto done;
+ }
+
+ if (!dom_sid_equal(user_sid,
+ dom_sid_parse_talloc(mem_ctx, "s-1-5-7"))) {
+ d_printf("(%s) Anon lsa_GetUserName returned %s, expected "
+ "S-1-5-7", __location__,
+ dom_sid_string(mem_ctx, user_sid));
+ ret = false;
+ }
+
+ if (!(user_creds = cli_credentials_init(mem_ctx))) {
+ d_printf("(%s) cli_credentials_init failed\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ cli_credentials_set_conf(user_creds, torture->lp_ctx);
+ cli_credentials_set_username(user_creds, "torture_username",
+ CRED_SPECIFIED);
+ cli_credentials_set_password(user_creds,
+ generate_random_str(user_creds, 8),
+ CRED_SPECIFIED);
+
+ if (!create_user(mem_ctx, cli, torture->lp_ctx, cmdline_credentials,
+ cli_credentials_get_username(user_creds),
+ cli_credentials_get_password(user_creds),
+ &domain_name, &created_sid)) {
+ d_printf("(%s) create_user failed\n", __location__);
+ ret = false;
+ goto done;
+ }
+
+ cli_credentials_set_domain(user_creds, domain_name,
+ CRED_SPECIFIED);
+
+ {
+ struct smbcli_session *session2;
+ struct smb_composite_sesssetup setup;
+ struct smbcli_tree *tree;
+
+ session2 = smbcli_session_init(cli->transport, mem_ctx, false);
+ if (session2 == NULL) {
+ d_printf("(%s) smbcli_session_init failed\n",
+ __location__);
+ goto done;
+ }
+
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities;
+ setup.in.workgroup = "";
+ setup.in.credentials = user_creds;
+
+ status = smb_composite_sesssetup(session2, &setup);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) session setup with new user failed: "
+ "%s\n", __location__, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+ session2->vuid = setup.out.vuid;
+
+ if (!NT_STATUS_IS_OK(secondary_tcon(mem_ctx, session2,
+ "IPC$", &tree))) {
+ d_printf("(%s) secondary_tcon failed\n",
+ __location__);
+ ret = false;
+ goto done;
+ }
+
+ if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, tree))) {
+ d_printf("(%s) whoami on user connection failed\n",
+ __location__);
+ ret = false;
+ goto delete;
+ }
+
+ talloc_free(tree);
+ }
+
+ d_printf("Created %s, found %s\n",
+ dom_sid_string(mem_ctx, created_sid),
+ dom_sid_string(mem_ctx, user_sid));
+
+ if (!dom_sid_equal(created_sid, user_sid)) {
+ ret = false;
+ }
+
+ delete:
+ if (!delete_user(cli, torture->lp_ctx,
+ cmdline_credentials,
+ cli_credentials_get_username(user_creds))) {
+ d_printf("(%s) delete_user failed\n", __location__);
+ ret = false;
+ }
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool test_NetShareGetInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareGetInfo r;
+ uint32_t levels[] = { 0, 1, 2, 501, 502, 1004, 1005, 1006, 1007, 1501 };
+ int i;
+ bool ret = true;
+
+ r.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.share_name = sharename;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+
+ ZERO_STRUCT(r.out);
+
+ printf("testing NetShareGetInfo level %u on share '%s'\n",
+ r.in.level, r.in.share_name);
+
+ status = dcerpc_srvsvc_NetShareGetInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("NetShareGetInfo level %u on share '%s' failed"
+ " - %s\n", r.in.level, r.in.share_name,
+ nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("NetShareGetInfo level %u on share '%s' failed "
+ "- %s\n", r.in.level, r.in.share_name,
+ win_errstr(r.out.result));
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_NetShareEnum(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char **one_sharename)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareEnum r;
+ struct srvsvc_NetShareCtr0 c0;
+ uint32_t levels[] = { 0, 1, 2, 501, 502, 1004, 1005, 1006, 1007 };
+ int i;
+ bool ret = true;
+
+ r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.ctr.ctr0 = &c0;
+ r.in.ctr.ctr0->count = 0;
+ r.in.ctr.ctr0->array = NULL;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+
+ ZERO_STRUCT(r.out);
+
+ printf("testing NetShareEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetShareEnum(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("NetShareEnum level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("NetShareEnum level %u failed - %s\n",
+ r.in.level, win_errstr(r.out.result));
+ continue;
+ }
+ if (r.in.level == 0) {
+ struct srvsvc_NetShareCtr0 *ctr = r.out.ctr.ctr0;
+ if (ctr->count > 0) {
+ *one_sharename = ctr->array[0].name;
+ }
+ }
+ }
+
+ return ret;
+}
+
+bool torture_samba3_rpc_srvsvc(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ const char *sharename = NULL;
+ struct smbcli_state *cli;
+ NTSTATUS status;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree,
+ "\\pipe\\srvsvc", &ndr_table_srvsvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) could not bind to srvsvc pipe: %s\n",
+ __location__, nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ ret &= test_NetShareEnum(p, mem_ctx, &sharename);
+ if (sharename == NULL) {
+ printf("did not get sharename\n");
+ } else {
+ ret &= test_NetShareGetInfo(p, mem_ctx, sharename);
+ }
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Do a ReqChallenge/Auth2 with a random wks name, make sure it returns
+ * NT_STATUS_NO_SAM_ACCOUNT
+ */
+
+bool torture_samba3_rpc_randomauth2(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ struct dcerpc_pipe *net_pipe;
+ char *wksname;
+ bool result = false;
+ NTSTATUS status;
+ struct netr_ServerReqChallenge r;
+ struct netr_Credential netr_cli_creds;
+ struct netr_Credential netr_srv_creds;
+ uint32_t negotiate_flags;
+ struct netr_ServerAuthenticate2 a;
+ struct creds_CredentialState *creds_state;
+ struct netr_Credential netr_cred;
+ struct samr_Password mach_pw;
+ struct smbcli_state *cli;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ d_printf("talloc_new failed\n");
+ return false;
+ }
+
+ if (!(wksname = generate_random_str_list(
+ mem_ctx, 14, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))) {
+ d_printf("generate_random_str_list failed\n");
+ goto done;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli,
+ torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ d_printf("IPC$ connection failed\n");
+ goto done;
+ }
+
+ if (!(net_pipe = dcerpc_pipe_init(
+ mem_ctx, cli->transport->socket->event.ctx,
+ lp_iconv_convenience(torture->lp_ctx)))) {
+ d_printf("dcerpc_pipe_init failed\n");
+ goto done;
+ }
+
+ status = dcerpc_pipe_open_smb(net_pipe, cli->tree, "\\netlogon");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dcerpc_bind_auth_none(net_pipe, &ndr_table_netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ r.in.computer_name = wksname;
+ r.in.server_name = talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe));
+ if (r.in.server_name == NULL) {
+ d_printf("talloc_asprintf failed\n");
+ goto done;
+ }
+ generate_random_buffer(netr_cli_creds.data,
+ sizeof(netr_cli_creds.data));
+ r.in.credentials = &netr_cli_creds;
+ r.out.credentials = &netr_srv_creds;
+
+ status = dcerpc_netr_ServerReqChallenge(net_pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("netr_ServerReqChallenge failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ E_md4hash("foobar", mach_pw.hash);
+
+ creds_state = talloc(mem_ctx, struct creds_CredentialState);
+ creds_client_init(creds_state, r.in.credentials,
+ r.out.credentials, &mach_pw,
+ &netr_cred, negotiate_flags);
+
+ a.in.server_name = talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(net_pipe));
+ a.in.account_name = talloc_asprintf(
+ mem_ctx, "%s$", wksname);
+ a.in.computer_name = wksname;
+ a.in.secure_channel_type = SEC_CHAN_WKSTA;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &netr_cred;
+ a.out.credentials = &netr_cred;
+
+ status = dcerpc_netr_ServerAuthenticate2(net_pipe, mem_ctx, &a);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_TRUST_SAM_ACCOUNT)) {
+ d_printf("dcerpc_netr_ServerAuthenticate2 returned %s, "
+ "expected NT_STATUS_NO_TRUST_SAM_ACCOUNT\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ result = true;
+ done:
+ talloc_free(mem_ctx);
+ return result;
+}
+
+static struct security_descriptor *get_sharesec(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smbcli_session *sess,
+ const char *sharename)
+{
+ struct smbcli_tree *tree;
+ TALLOC_CTX *tmp_ctx;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ struct srvsvc_NetShareGetInfo r;
+ struct security_descriptor *result;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ d_printf("talloc_new failed\n");
+ return NULL;
+ }
+
+ if (!NT_STATUS_IS_OK(secondary_tcon(tmp_ctx, sess, "IPC$", &tree))) {
+ d_printf("secondary_tcon failed\n");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ status = pipe_bind_smb(mem_ctx, lp_ctx, tree, "\\pipe\\srvsvc",
+ &ndr_table_srvsvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) could not bind to srvsvc pipe: %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+#if 0
+ p->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT;
+#endif
+
+ r.in.server_unc = talloc_asprintf(tmp_ctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.share_name = sharename;
+ r.in.level = 502;
+
+ status = dcerpc_srvsvc_NetShareGetInfo(p, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("srvsvc_NetShareGetInfo failed: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ result = talloc_steal(mem_ctx, r.out.info.info502->sd);
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+static NTSTATUS set_sharesec(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smbcli_session *sess,
+ const char *sharename,
+ struct security_descriptor *sd)
+{
+ struct smbcli_tree *tree;
+ TALLOC_CTX *tmp_ctx;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ struct sec_desc_buf i;
+ struct srvsvc_NetShareSetInfo r;
+ uint32_t error = 0;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ d_printf("talloc_new failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(secondary_tcon(tmp_ctx, sess, "IPC$", &tree))) {
+ d_printf("secondary_tcon failed\n");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = pipe_bind_smb(mem_ctx, lp_ctx, tree, "\\pipe\\srvsvc",
+ &ndr_table_srvsvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) could not bind to srvsvc pipe: %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+#if 0
+ p->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT;
+#endif
+
+ r.in.server_unc = talloc_asprintf(tmp_ctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.share_name = sharename;
+ r.in.level = 1501;
+ i.sd = sd;
+ r.in.info.info1501 = &i;
+ r.in.parm_error = &error;
+
+ status = dcerpc_srvsvc_NetShareSetInfo(p, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("srvsvc_NetShareGetInfo failed: %s\n",
+ nt_errstr(status));
+ }
+
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+bool try_tcon(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct security_descriptor *orig_sd,
+ struct smbcli_session *session,
+ const char *sharename, const struct dom_sid *user_sid,
+ unsigned int access_mask, NTSTATUS expected_tcon,
+ NTSTATUS expected_mkdir)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct smbcli_tree *rmdir_tree, *tree;
+ struct dom_sid *domain_sid;
+ uint32_t rid;
+ struct security_descriptor *sd;
+ NTSTATUS status;
+ bool ret = true;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ d_printf("talloc_new failed\n");
+ return false;
+ }
+
+ status = secondary_tcon(tmp_ctx, session, sharename, &rmdir_tree);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("first tcon to delete dir failed\n");
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ smbcli_rmdir(rmdir_tree, "sharesec_testdir");
+
+ if (!NT_STATUS_IS_OK(dom_sid_split_rid(tmp_ctx, user_sid,
+ &domain_sid, &rid))) {
+ d_printf("dom_sid_split_rid failed\n");
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ sd = security_descriptor_dacl_create(
+ tmp_ctx, 0, "S-1-5-32-544",
+ dom_sid_string(mem_ctx, dom_sid_add_rid(mem_ctx, domain_sid,
+ DOMAIN_RID_USERS)),
+ dom_sid_string(mem_ctx, user_sid),
+ SEC_ACE_TYPE_ACCESS_ALLOWED, access_mask, 0, NULL);
+ if (sd == NULL) {
+ d_printf("security_descriptor_dacl_create failed\n");
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ status = set_sharesec(mem_ctx, lp_ctx, session, sharename, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("custom set_sharesec failed: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ status = secondary_tcon(tmp_ctx, session, sharename, &tree);
+ if (!NT_STATUS_EQUAL(status, expected_tcon)) {
+ d_printf("Expected %s, got %s\n", nt_errstr(expected_tcon),
+ nt_errstr(status));
+ ret = false;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* An expected non-access, no point in trying to write */
+ goto done;
+ }
+
+ status = smbcli_mkdir(tree, "sharesec_testdir");
+ if (!NT_STATUS_EQUAL(status, expected_mkdir)) {
+ d_printf("(%s) Expected %s, got %s\n", __location__,
+ nt_errstr(expected_mkdir), nt_errstr(status));
+ ret = false;
+ }
+
+ done:
+ smbcli_rmdir(rmdir_tree, "sharesec_testdir");
+
+ status = set_sharesec(mem_ctx, lp_ctx, session, sharename, orig_sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("custom set_sharesec failed: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+bool torture_samba3_rpc_sharesec(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct smbcli_state *cli;
+ struct security_descriptor *sd;
+ struct dom_sid *user_sid;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ d_printf("IPC$ connection failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (!(user_sid = whoami(mem_ctx, torture->lp_ctx, cli->tree))) {
+ d_printf("whoami failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ sd = get_sharesec(mem_ctx, torture->lp_ctx, cli->session,
+ torture_setting_string(torture, "share", NULL));
+
+ ret &= try_tcon(mem_ctx, torture->lp_ctx, sd, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ user_sid, 0, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK);
+
+ ret &= try_tcon(mem_ctx, torture->lp_ctx, sd, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ user_sid, SEC_FILE_READ_DATA, NT_STATUS_OK,
+ NT_STATUS_MEDIA_WRITE_PROTECTED);
+
+ ret &= try_tcon(mem_ctx, torture->lp_ctx, sd, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ user_sid, SEC_FILE_ALL, NT_STATUS_OK, NT_STATUS_OK);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+bool torture_samba3_rpc_lsa(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct smbcli_state *cli;
+ struct dcerpc_pipe *p;
+ struct policy_handle lsa_handle;
+ NTSTATUS status;
+ struct dom_sid *domain_sid;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ d_printf("IPC$ connection failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, "\\lsarpc",
+ &ndr_table_lsarpc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) pipe_bind_smb failed: %s\n", __location__,
+ nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ {
+ struct lsa_ObjectAttribute attr;
+ struct lsa_OpenPolicy2 o;
+ o.in.system_name = talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(p));
+ ZERO_STRUCT(attr);
+ o.in.attr = &attr;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.handle = &lsa_handle;
+ status = dcerpc_lsa_OpenPolicy2(p, mem_ctx, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+#if 0
+ p->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT;
+#endif
+
+ {
+ int i;
+ int levels[] = { 2,3,5,6 };
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+ struct lsa_QueryInfoPolicy r;
+ r.in.handle = &lsa_handle;
+ r.in.level = levels[i];
+ status = dcerpc_lsa_QueryInfoPolicy(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_lsa_QueryInfoPolicy %d "
+ "failed: %s\n", __location__,
+ levels[i], nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ if (levels[i] == 5) {
+ domain_sid = r.out.info->account_domain.sid;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static NTSTATUS get_servername(TALLOC_CTX *mem_ctx, struct smbcli_tree *tree,
+ struct smb_iconv_convenience *iconv_convenience,
+ char **name)
+{
+ struct rap_WserverGetInfo r;
+ NTSTATUS status;
+ char servername[17];
+
+ r.in.level = 0;
+ r.in.bufsize = 0xffff;
+
+ status = smbcli_rap_netservergetinfo(tree, iconv_convenience, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ memcpy(servername, r.out.info.info0.name, 16);
+ servername[16] = '\0';
+
+ if (pull_ascii_talloc(mem_ctx, iconv_convenience,
+ name, servername) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS find_printers(TALLOC_CTX *ctx, struct loadparm_context *lp_ctx,
+ struct smbcli_tree *tree,
+ const char ***printers, int *num_printers)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ struct srvsvc_NetShareEnum r;
+ struct srvsvc_NetShareCtr1 c1_in;
+ struct srvsvc_NetShareCtr1 *c1;
+ int i;
+
+ mem_ctx = talloc_new(ctx);
+ if (mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pipe_bind_smb(mem_ctx, lp_ctx,
+ tree, "\\srvsvc", &ndr_table_srvsvc,
+ &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("could not bind to srvsvc pipe\n");
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ r.in.server_unc = talloc_asprintf(
+ mem_ctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.level = 1;
+ ZERO_STRUCT(c1_in);
+ r.in.ctr.ctr1 = &c1_in;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+
+ status = dcerpc_srvsvc_NetShareEnum(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("NetShareEnum level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ *printers = NULL;
+ *num_printers = 0;
+ c1 = r.out.ctr.ctr1;
+ for (i=0; i<c1->count; i++) {
+ if (c1->array[i].type != STYPE_PRINTQ) {
+ continue;
+ }
+ if (!add_string_to_array(ctx, c1->array[i].name,
+ printers, num_printers)) {
+ talloc_free(ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+static bool enumprinters(TALLOC_CTX *mem_ctx, struct dcerpc_pipe *pipe,
+ const char *servername, int level, int *num_printers)
+{
+ struct spoolss_EnumPrinters r;
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = talloc_asprintf(mem_ctx, "\\\\%s", servername);
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ status = dcerpc_spoolss_EnumPrinters(pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_spoolss_EnumPrinters failed: %s\n",
+ __location__, nt_errstr(status));
+ return false;
+ }
+
+ if (!W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ d_printf("(%s) EnumPrinters unexpected return code %s, should "
+ "be WERR_INSUFFICIENT_BUFFER\n", __location__,
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ blob = data_blob_talloc_zero(mem_ctx, r.out.needed);
+ if (blob.data == NULL) {
+ d_printf("(%s) data_blob_talloc failed\n", __location__);
+ return false;
+ }
+
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumPrinters(pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_EnumPrinters failed: %s, "
+ "%s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ *num_printers = r.out.count;
+
+ return true;
+}
+
+static NTSTATUS getprinterinfo(TALLOC_CTX *ctx, struct dcerpc_pipe *pipe,
+ struct policy_handle *handle, int level,
+ union spoolss_PrinterInfo **res)
+{
+ TALLOC_CTX *mem_ctx;
+ struct spoolss_GetPrinter r;
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ mem_ctx = talloc_new(ctx);
+ if (mem_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ r.in.handle = handle;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ status = dcerpc_spoolss_GetPrinter(pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_spoolss_GetPrinter failed: %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ if (!W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ printf("GetPrinter unexpected return code %s, should "
+ "be WERR_INSUFFICIENT_BUFFER\n",
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ r.in.handle = handle;
+ r.in.level = level;
+ blob = data_blob_talloc(mem_ctx, NULL, r.out.needed);
+ if (blob.data == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ memset(blob.data, 0, blob.length);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_GetPrinter(pipe, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_GetPrinter failed: %s, "
+ "%s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return NT_STATUS_IS_OK(status) ?
+ NT_STATUS_UNSUCCESSFUL : status;
+ }
+
+ if (res != NULL) {
+ *res = talloc_steal(ctx, r.out.info);
+ }
+
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+}
+
+bool torture_samba3_rpc_spoolss(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct smbcli_state *cli;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ struct policy_handle server_handle, printer_handle;
+ const char **printers;
+ int num_printers;
+ struct spoolss_UserLevel1 userlevel1;
+ char *servername;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ d_printf("IPC$ connection failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = get_servername(mem_ctx, cli->tree, lp_iconv_convenience(torture->lp_ctx), &servername);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "(%s) get_servername returned %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(find_printers(mem_ctx, torture->lp_ctx, cli->tree,
+ &printers, &num_printers))) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (num_printers == 0) {
+ d_printf("Did not find printers\n");
+ talloc_free(mem_ctx);
+ return true;
+ }
+
+ status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, "\\spoolss",
+ &ndr_table_spoolss, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) pipe_bind_smb failed: %s\n", __location__,
+ nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ ZERO_STRUCT(userlevel1);
+ userlevel1.client = talloc_asprintf(
+ mem_ctx, "\\\\%s", lp_netbios_name(torture->lp_ctx));
+ userlevel1.user = cli_credentials_get_username(cmdline_credentials);
+ userlevel1.build = 2600;
+ userlevel1.major = 3;
+ userlevel1.minor = 0;
+ userlevel1.processor = 0;
+
+ {
+ struct spoolss_OpenPrinterEx r;
+
+ ZERO_STRUCT(r);
+ r.in.printername = talloc_asprintf(mem_ctx, "\\\\%s",
+ servername);
+ r.in.datatype = NULL;
+ r.in.access_mask = 0;
+ r.in.level = 1;
+ r.in.userlevel.level1 = &userlevel1;
+ r.out.handle = &server_handle;
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_OpenPrinterEx failed: "
+ "%s, %s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ struct spoolss_ClosePrinter r;
+
+ r.in.handle = &server_handle;
+ r.out.handle = &server_handle;
+
+ status = dcerpc_spoolss_ClosePrinter(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_ClosePrinter failed: "
+ "%s, %s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ struct spoolss_OpenPrinterEx r;
+
+ ZERO_STRUCT(r);
+ r.in.printername = talloc_asprintf(
+ mem_ctx, "\\\\%s\\%s", servername, printers[0]);
+ r.in.datatype = NULL;
+ r.in.access_mask = 0;
+ r.in.level = 1;
+ r.in.userlevel.level1 = &userlevel1;
+ r.out.handle = &printer_handle;
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_spoolss_OpenPrinterEx failed: "
+ "%s, %s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ int i;
+
+ for (i=0; i<8; i++) {
+ status = getprinterinfo(mem_ctx, p, &printer_handle,
+ i, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) getprinterinfo %d failed: %s\n",
+ __location__, i, nt_errstr(status));
+ ret = false;
+ }
+ }
+ }
+
+ {
+ struct spoolss_ClosePrinter r;
+
+ r.in.handle = &printer_handle;
+ r.out.handle = &printer_handle;
+
+ status = dcerpc_spoolss_ClosePrinter(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_spoolss_ClosePrinter failed: "
+ "%s\n", __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ int num_enumerated;
+ if (!enumprinters(mem_ctx, p, servername, 1,
+ &num_enumerated)) {
+ d_printf("(%s) enumprinters failed\n", __location__);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ if (num_printers != num_enumerated) {
+ d_printf("(%s) netshareenum gave %d printers, "
+ "enumprinters lvl 1 gave %d\n", __location__,
+ num_printers, num_enumerated);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ {
+ int num_enumerated;
+ if (!enumprinters(mem_ctx, p, servername, 2,
+ &num_enumerated)) {
+ d_printf("(%s) enumprinters failed\n", __location__);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ if (num_printers != num_enumerated) {
+ d_printf("(%s) netshareenum gave %d printers, "
+ "enumprinters lvl 2 gave %d\n", __location__,
+ num_printers, num_enumerated);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+bool torture_samba3_rpc_wkssvc(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ struct smbcli_state *cli;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ char *servername;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ return false;
+ }
+
+ if (!(torture_open_connection_share(
+ mem_ctx, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ d_printf("IPC$ connection failed\n");
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = get_servername(mem_ctx, cli->tree, lp_iconv_convenience(torture->lp_ctx), &servername);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "(%s) get_servername returned %s\n",
+ __location__, nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ status = pipe_bind_smb(mem_ctx, torture->lp_ctx, cli->tree, "\\wkssvc",
+ &ndr_table_wkssvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) pipe_bind_smb failed: %s\n", __location__,
+ nt_errstr(status));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ {
+ struct wkssvc_NetWkstaInfo100 wks100;
+ union wkssvc_NetWkstaInfo info;
+ struct wkssvc_NetWkstaGetInfo r;
+
+ r.in.server_name = "\\foo";
+ r.in.level = 100;
+ info.info100 = &wks100;
+ r.out.info = &info;
+
+ status = dcerpc_wkssvc_NetWkstaGetInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) dcerpc_wkssvc_NetWksGetInfo failed: "
+ "%s, %s\n", __location__, nt_errstr(status),
+ win_errstr(r.out.result));
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ if (strcmp(servername,
+ r.out.info->info100->server_name) != 0) {
+ d_printf("(%s) servername inconsistency: RAP=%s, "
+ "dcerpc_wkssvc_NetWksGetInfo=%s",
+ __location__, servername,
+ r.out.info->info100->server_name);
+ talloc_free(mem_ctx);
+ return false;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return true;
+}
+
+static NTSTATUS winreg_close(struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct winreg_CloseKey c;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+
+ c.in.handle = c.out.handle = handle;
+
+ if (!(mem_ctx = talloc_new(p))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_winreg_CloseKey(p, mem_ctx, &c);
+ talloc_free(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!W_ERROR_IS_OK(c.out.result)) {
+ return werror_to_ntstatus(c.out.result);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS enumvalues(struct dcerpc_pipe *p, struct policy_handle *handle,
+ TALLOC_CTX *mem_ctx)
+{
+ uint32_t enum_index = 0;
+
+ while (1) {
+ struct winreg_EnumValue r;
+ struct winreg_StringBuf name;
+ enum winreg_Type type = 0;
+ uint8_t buf8[1024];
+ NTSTATUS status;
+ uint32_t size, length;
+
+ r.in.handle = handle;
+ r.in.enum_index = enum_index;
+ name.name = "";
+ name.size = 1024;
+ r.in.name = r.out.name = &name;
+ size = 1024;
+ length = 5;
+ r.in.type = &type;
+ r.in.value = buf8;
+ r.in.size = &size;
+ r.in.length = &length;
+
+ status = dcerpc_winreg_EnumValue(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ return NT_STATUS_OK;
+ }
+ enum_index += 1;
+ }
+}
+
+static NTSTATUS enumkeys(struct dcerpc_pipe *p, struct policy_handle *handle,
+ TALLOC_CTX *mem_ctx, int depth)
+{
+ struct winreg_EnumKey r;
+ struct winreg_StringBuf class, name;
+ NTSTATUS status;
+ NTTIME t = 0;
+
+ if (depth <= 0) {
+ return NT_STATUS_OK;
+ }
+
+ class.name = "";
+ class.size = 1024;
+
+ r.in.handle = handle;
+ r.in.enum_index = 0;
+ r.in.name = &name;
+ r.in.keyclass = &class;
+ r.out.name = &name;
+ r.in.last_changed_time = &t;
+
+ do {
+ TALLOC_CTX *tmp_ctx;
+ struct winreg_OpenKey o;
+ struct policy_handle key_handle;
+ int i;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ name.name = NULL;
+ name.size = 1024;
+
+ status = dcerpc_winreg_EnumKey(p, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ /* We're done enumerating */
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ for (i=0; i<10-depth; i++)
+ printf(" ");
+ printf("%s\n", r.out.name->name);
+
+
+ o.in.parent_handle = handle;
+ o.in.keyname.name = r.out.name->name;
+ o.in.unknown = 0;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.handle = &key_handle;
+
+ status = dcerpc_winreg_OpenKey(p, tmp_ctx, &o);
+ if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(o.out.result)) {
+ enumkeys(p, &key_handle, tmp_ctx, depth-1);
+ enumvalues(p, &key_handle, tmp_ctx);
+ status = winreg_close(p, &key_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ r.in.enum_index += 1;
+ } while(true);
+
+ return NT_STATUS_OK;
+}
+
+typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_pipe *, TALLOC_CTX *, void *);
+
+static bool test_Open3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *name, winreg_open_fn open_fn)
+{
+ struct policy_handle handle;
+ struct winreg_OpenHKLM r;
+ NTSTATUS status;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ status = open_fn(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) %s failed: %s, %s\n", __location__, name,
+ nt_errstr(status), win_errstr(r.out.result));
+ return false;
+ }
+
+ enumkeys(p, &handle, mem_ctx, 4);
+
+ status = winreg_close(p, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("(%s) dcerpc_CloseKey failed: %s\n",
+ __location__, nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+bool torture_samba3_rpc_winreg(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct {
+ const char *name;
+ winreg_open_fn fn;
+ } open_fns[] = {
+ {"OpenHKLM", (winreg_open_fn)dcerpc_winreg_OpenHKLM },
+ {"OpenHKU", (winreg_open_fn)dcerpc_winreg_OpenHKU },
+ {"OpenHKPD", (winreg_open_fn)dcerpc_winreg_OpenHKPD },
+ {"OpenHKPT", (winreg_open_fn)dcerpc_winreg_OpenHKPT },
+ {"OpenHKCR", (winreg_open_fn)dcerpc_winreg_OpenHKCR }};
+#if 0
+ int i;
+#endif
+
+ mem_ctx = talloc_init("torture_rpc_winreg");
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_winreg);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+#if 1
+ ret = test_Open3(p, mem_ctx, open_fns[0].name, open_fns[0].fn);
+#else
+ for (i = 0; i < ARRAY_SIZE(open_fns); i++) {
+ if (!test_Open3(p, mem_ctx, open_fns[i].name, open_fns[i].fn))
+ ret = false;
+ }
+#endif
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+
+static NTSTATUS get_shareinfo(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smbcli_state *cli,
+ const char *share,
+ struct srvsvc_NetShareInfo502 **info)
+{
+ struct smbcli_tree *ipc;
+ struct dcerpc_pipe *p;
+ struct srvsvc_NetShareGetInfo r;
+ NTSTATUS status;
+
+ if (!(p = dcerpc_pipe_init(cli,
+ cli->transport->socket->event.ctx,
+ lp_iconv_convenience(lp_ctx)))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = secondary_tcon(p, cli->session, "IPC$", &ipc);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = dcerpc_pipe_open_smb(p, ipc, "\\pipe\\srvsvc");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = dcerpc_bind_auth_none(p, &ndr_table_srvsvc);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ r.in.server_unc = talloc_asprintf(mem_ctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.share_name = share;
+ r.in.level = 502;
+
+ status = dcerpc_srvsvc_NetShareGetInfo(p, p, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) OpenHKLM failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(r.out.result));
+ goto fail;
+ }
+
+ *info = talloc_move(mem_ctx, &r.out.info.info502);
+ return NT_STATUS_OK;
+
+ fail:
+ talloc_free(p);
+ return status;
+}
+
+/*
+ * Get us a handle on HKLM\
+ */
+
+static NTSTATUS get_hklm_handle(TALLOC_CTX *mem_ctx,
+ struct smbcli_state *cli,
+ struct smb_iconv_convenience *iconv_convenience,
+ struct dcerpc_pipe **pipe_p,
+ struct policy_handle **handle)
+{
+ struct smbcli_tree *ipc;
+ struct dcerpc_pipe *p;
+ struct winreg_OpenHKLM r;
+ NTSTATUS status;
+ struct policy_handle *result;
+
+ result = talloc(mem_ctx, struct policy_handle);
+
+ if (result == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(p = dcerpc_pipe_init(result,
+ cli->transport->socket->event.ctx,
+ iconv_convenience))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ status = secondary_tcon(p, cli->session, "IPC$", &ipc);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = dcerpc_pipe_open_smb(p, ipc, "\\winreg");
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ status = dcerpc_bind_auth_none(p, &ndr_table_winreg);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dcerpc_bind_auth_none failed: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = result;
+
+ status = dcerpc_winreg_OpenHKLM(p, p, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ d_printf("(%s) OpenHKLM failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(r.out.result));
+ goto fail;
+ }
+
+ *pipe_p = p;
+ *handle = result;
+ return NT_STATUS_OK;
+
+ fail:
+ talloc_free(result);
+ return status;
+}
+
+static NTSTATUS torture_samba3_createshare(struct smbcli_state *cli,
+ struct smb_iconv_convenience *iconv_convenience,
+ const char *sharename)
+{
+ struct dcerpc_pipe *p;
+ struct policy_handle *hklm = NULL;
+ struct policy_handle new_handle;
+ struct winreg_CreateKey c;
+ struct winreg_CloseKey cl;
+ enum winreg_CreateAction action_taken;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_new(cli);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ status = get_hklm_handle(mem_ctx, cli, iconv_convenience, &p, &hklm);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_hklm_handle failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ c.in.handle = hklm;
+ c.in.name.name = talloc_asprintf(
+ mem_ctx, "software\\samba\\smbconf\\%s", sharename);
+ if (c.in.name.name == NULL) {
+ d_printf("talloc_asprintf failed\n");
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ c.in.keyclass.name = "";
+ c.in.options = 0;
+ c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ c.in.secdesc = NULL;
+ c.in.action_taken = &action_taken;
+ c.out.new_handle = &new_handle;
+ c.out.action_taken = &action_taken;
+
+ status = dcerpc_winreg_CreateKey(p, p, &c);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(c.out.result)) {
+ d_printf("(%s) OpenKey failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(c.out.result));
+ goto fail;
+ }
+
+ cl.in.handle = &new_handle;
+ cl.out.handle = &new_handle;
+ status = dcerpc_winreg_CloseKey(p, p, &cl);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(cl.out.result)) {
+ d_printf("(%s) OpenKey failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(cl.out.result));
+ goto fail;
+ }
+
+
+ fail:
+ talloc_free(mem_ctx);
+ return status;
+}
+
+static NTSTATUS torture_samba3_deleteshare(struct torture_context *torture,
+ struct smbcli_state *cli,
+ const char *sharename)
+{
+ struct dcerpc_pipe *p;
+ struct policy_handle *hklm = NULL;
+ struct winreg_DeleteKey d;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_new(cli);
+ NT_STATUS_HAVE_NO_MEMORY(mem_ctx);
+
+ status = get_hklm_handle(cli, cli, lp_iconv_convenience(torture->lp_ctx),
+ &p, &hklm);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_hklm_handle failed: %s\n", nt_errstr(status));
+ goto fail;
+ }
+
+ d.in.handle = hklm;
+ d.in.key.name = talloc_asprintf(
+ mem_ctx, "software\\samba\\smbconf\\%s", sharename);
+ if (d.in.key.name == NULL) {
+ d_printf("talloc_asprintf failed\n");
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ status = dcerpc_winreg_DeleteKey(p, p, &d);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(d.out.result)) {
+ d_printf("(%s) OpenKey failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(d.out.result));
+ goto fail;
+ }
+
+ fail:
+ talloc_free(mem_ctx);
+ return status;
+}
+
+static NTSTATUS torture_samba3_setconfig(struct smbcli_state *cli,
+ struct loadparm_context *lp_ctx,
+ const char *sharename,
+ const char *parameter,
+ const char *value)
+{
+ struct dcerpc_pipe *p = NULL;
+ struct policy_handle *hklm = NULL, key_handle;
+ struct winreg_OpenKey o;
+ struct winreg_SetValue s;
+ uint32_t type;
+ DATA_BLOB val;
+ NTSTATUS status;
+
+ status = get_hklm_handle(cli, cli, lp_iconv_convenience(lp_ctx), &p, &hklm);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("get_hklm_handle failed: %s\n", nt_errstr(status));
+ return status;;
+ }
+
+ o.in.parent_handle = hklm;
+ o.in.keyname.name = talloc_asprintf(
+ hklm, "software\\samba\\smbconf\\%s", sharename);
+ if (o.in.keyname.name == NULL) {
+ d_printf("talloc_asprintf failed\n");
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ o.in.unknown = 0;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.handle = &key_handle;
+
+ status = dcerpc_winreg_OpenKey(p, p, &o);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(o.out.result)) {
+ d_printf("(%s) OpenKey failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(o.out.result));
+ goto done;
+ }
+
+ if (!reg_string_to_val(hklm, lp_iconv_convenience(lp_ctx), "REG_SZ",
+ value, &type, &val)) {
+ d_printf("(%s) reg_string_to_val failed\n", __location__);
+ goto done;
+ }
+
+ s.in.handle = &key_handle;
+ s.in.name.name = parameter;
+ s.in.type = type;
+ s.in.data = val.data;
+ s.in.size = val.length;
+
+ status = dcerpc_winreg_SetValue(p, p, &s);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(s.out.result)) {
+ d_printf("(%s) SetValue failed: %s, %s\n", __location__,
+ nt_errstr(status), win_errstr(s.out.result));
+ goto done;
+ }
+
+ done:
+ talloc_free(hklm);
+ return status;
+}
+
+bool torture_samba3_regconfig(struct torture_context *torture)
+{
+ struct smbcli_state *cli;
+ struct srvsvc_NetShareInfo502 *i = NULL;
+ NTSTATUS status;
+ bool ret = false;
+ const char *comment = "Dummer Kommentar";
+
+ if (!(torture_open_connection(&cli, torture, 0))) {
+ return false;
+ }
+
+ status = torture_samba3_createshare(cli, lp_iconv_convenience(torture->lp_ctx), "blubber");
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(torture, "torture_samba3_createshare failed: "
+ "%s\n", nt_errstr(status));
+ goto done;
+ }
+
+ status = torture_samba3_setconfig(cli, torture->lp_ctx, "blubber", "comment", comment);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(torture, "torture_samba3_setconfig failed: "
+ "%s\n", nt_errstr(status));
+ goto done;
+ }
+
+ status = get_shareinfo(torture, torture->lp_ctx, cli, "blubber", &i);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(torture, "get_shareinfo failed: "
+ "%s\n", nt_errstr(status));
+ goto done;
+ }
+
+ if (strcmp(comment, i->comment) != 0) {
+ torture_warning(torture, "Expected comment [%s], got [%s]\n",
+ comment, i->comment);
+ goto done;
+ }
+
+ status = torture_samba3_deleteshare(torture, cli, "blubber");
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(torture, "torture_samba3_deleteshare failed: "
+ "%s\n", nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+ done:
+ talloc_free(cli);
+ return ret;
+}
diff --git a/source4/torture/rpc/samlogon.c b/source4/torture/rpc/samlogon.c
new file mode 100644
index 0000000000..e2558ff0a9
--- /dev/null
+++ b/source4/torture/rpc/samlogon.c
@@ -0,0 +1,1855 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for netlogon SamLogon operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
+ Copyright (C) Tim Potter 2003
+
+ 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 "includes.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "auth/auth.h"
+#include "lib/crypto/crypto.h"
+#include "lib/cmdline/popt_common.h"
+#include "torture/rpc/rpc.h"
+#include "auth/gensec/schannel_proto.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/param.h"
+
+#define TEST_MACHINE_NAME "samlogontest"
+#define TEST_USER_NAME "samlogontestuser"
+#define TEST_USER_NAME_WRONG_WKS "samlogontest2"
+#define TEST_USER_NAME_WRONG_TIME "samlogontest3"
+
+enum ntlm_break {
+ BREAK_BOTH,
+ BREAK_NONE,
+ BREAK_LM,
+ BREAK_NT,
+ NO_LM,
+ NO_NT
+};
+
+struct samlogon_state {
+ TALLOC_CTX *mem_ctx;
+ const char *comment;
+ const char *account_name;
+ const char *account_domain;
+ const char *password;
+ struct dcerpc_pipe *p;
+ int function_level;
+ uint32_t parameter_control;
+ struct netr_LogonSamLogon r;
+ struct netr_LogonSamLogonEx r_ex;
+ struct netr_LogonSamLogonWithFlags r_flags;
+ struct netr_Authenticator auth, auth2;
+ struct creds_CredentialState *creds;
+ NTSTATUS expected_error;
+ bool old_password; /* Allow an old password to be accepted or rejected without error, as well as session key bugs */
+ DATA_BLOB chall;
+ struct smb_iconv_convenience *iconv_convenience;
+};
+
+/*
+ Authenticate a user with a challenge/response, checking session key
+ and valid authentication types
+*/
+static NTSTATUS check_samlogon(struct samlogon_state *samlogon_state,
+ enum ntlm_break break_which,
+ uint32_t parameter_control,
+ DATA_BLOB *chall,
+ DATA_BLOB *lm_response,
+ DATA_BLOB *nt_response,
+ uint8_t lm_key[8],
+ uint8_t user_session_key[16],
+ char **error_string)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogon *r = &samlogon_state->r;
+ struct netr_LogonSamLogonEx *r_ex = &samlogon_state->r_ex;
+ struct netr_LogonSamLogonWithFlags *r_flags = &samlogon_state->r_flags;
+ struct netr_NetworkInfo ninfo;
+ struct netr_SamBaseInfo *base = NULL;
+ uint16_t validation_level = 0;
+
+ samlogon_state->r.in.logon.network = &ninfo;
+ samlogon_state->r_ex.in.logon.network = &ninfo;
+ samlogon_state->r_flags.in.logon.network = &ninfo;
+
+ ninfo.identity_info.domain_name.string = samlogon_state->account_domain;
+ ninfo.identity_info.parameter_control = parameter_control;
+ ninfo.identity_info.logon_id_low = 0;
+ ninfo.identity_info.logon_id_high = 0;
+ ninfo.identity_info.account_name.string = samlogon_state->account_name;
+ ninfo.identity_info.workstation.string = TEST_MACHINE_NAME;
+
+ memcpy(ninfo.challenge, chall->data, 8);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ if (lm_response && lm_response->data) {
+ lm_response->data[0]++;
+ }
+ break;
+ case BREAK_NT:
+ if (nt_response && nt_response->data) {
+ nt_response->data[0]++;
+ }
+ break;
+ case BREAK_BOTH:
+ if (lm_response && lm_response->data) {
+ lm_response->data[0]++;
+ }
+ if (nt_response && nt_response->data) {
+ nt_response->data[0]++;
+ }
+ break;
+ case NO_LM:
+ data_blob_free(lm_response);
+ break;
+ case NO_NT:
+ data_blob_free(nt_response);
+ break;
+ }
+
+ if (nt_response) {
+ ninfo.nt.data = nt_response->data;
+ ninfo.nt.length = nt_response->length;
+ } else {
+ ninfo.nt.data = NULL;
+ ninfo.nt.length = 0;
+ }
+
+ if (lm_response) {
+ ninfo.lm.data = lm_response->data;
+ ninfo.lm.length = lm_response->length;
+ } else {
+ ninfo.lm.data = NULL;
+ ninfo.lm.length = 0;
+ }
+
+ switch (samlogon_state->function_level) {
+ case NDR_NETR_LOGONSAMLOGON:
+ ZERO_STRUCT(samlogon_state->auth2);
+ creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth);
+
+ r->out.return_authenticator = NULL;
+ status = dcerpc_netr_LogonSamLogon(samlogon_state->p, samlogon_state->mem_ctx, r);
+ if (!r->out.return_authenticator ||
+ !creds_client_check(samlogon_state->creds, &r->out.return_authenticator->cred)) {
+ d_printf("Credential chaining failed\n");
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+
+ validation_level = r->in.validation_level;
+
+ creds_decrypt_samlogon(samlogon_state->creds, validation_level, &r->out.validation);
+
+ switch (validation_level) {
+ case 2:
+ base = &r->out.validation.sam2->base;
+ break;
+ case 3:
+ base = &r->out.validation.sam3->base;
+ break;
+ case 6:
+ base = &r->out.validation.sam6->base;
+ break;
+ }
+ break;
+ case NDR_NETR_LOGONSAMLOGONEX:
+ status = dcerpc_netr_LogonSamLogonEx(samlogon_state->p, samlogon_state->mem_ctx, r_ex);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+
+ validation_level = r_ex->in.validation_level;
+
+ creds_decrypt_samlogon(samlogon_state->creds, validation_level, &r_ex->out.validation);
+
+ switch (validation_level) {
+ case 2:
+ base = &r_ex->out.validation.sam2->base;
+ break;
+ case 3:
+ base = &r_ex->out.validation.sam3->base;
+ break;
+ case 6:
+ base = &r_ex->out.validation.sam6->base;
+ break;
+ }
+ break;
+ case NDR_NETR_LOGONSAMLOGONWITHFLAGS:
+ ZERO_STRUCT(samlogon_state->auth2);
+ creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth);
+
+ r_flags->out.return_authenticator = NULL;
+ status = dcerpc_netr_LogonSamLogonWithFlags(samlogon_state->p, samlogon_state->mem_ctx, r_flags);
+ if (!r_flags->out.return_authenticator ||
+ !creds_client_check(samlogon_state->creds, &r_flags->out.return_authenticator->cred)) {
+ d_printf("Credential chaining failed\n");
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+
+ validation_level = r_flags->in.validation_level;
+
+ creds_decrypt_samlogon(samlogon_state->creds, validation_level, &r_flags->out.validation);
+
+ switch (validation_level) {
+ case 2:
+ base = &r_flags->out.validation.sam2->base;
+ break;
+ case 3:
+ base = &r_flags->out.validation.sam3->base;
+ break;
+ case 6:
+ base = &r_flags->out.validation.sam6->base;
+ break;
+ }
+ break;
+ default:
+ /* can't happen */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!base) {
+ d_printf("No user info returned from 'successful' SamLogon*() call!\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (user_session_key) {
+ memcpy(user_session_key, base->key.key, 16);
+ }
+ if (lm_key) {
+ memcpy(lm_key, base->LMSessKey.key, 8);
+ }
+
+ return status;
+}
+
+
+/*
+ * Test the normal 'LM and NTLM' combination
+ */
+
+static bool test_lm_ntlm_broken(struct samlogon_state *samlogon_state, enum ntlm_break break_which, char **error_string)
+{
+ bool pass = true;
+ bool lm_good;
+ NTSTATUS nt_status;
+ DATA_BLOB lm_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24);
+ DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24);
+ DATA_BLOB session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16);
+
+ uint8_t lm_key[8];
+ uint8_t user_session_key[16];
+ uint8_t lm_hash[16];
+ uint8_t nt_hash[16];
+
+ ZERO_STRUCT(lm_key);
+ ZERO_STRUCT(user_session_key);
+
+ lm_good = SMBencrypt(samlogon_state->password, samlogon_state->chall.data, lm_response.data);
+ if (!lm_good) {
+ ZERO_STRUCT(lm_hash);
+ } else {
+ E_deshash(samlogon_state->password, lm_hash);
+ }
+
+ SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, nt_response.data);
+
+ E_md4hash(samlogon_state->password, nt_hash);
+ SMBsesskeygen_ntv1(nt_hash, session_key.data);
+
+ nt_status = check_samlogon(samlogon_state,
+ break_which,
+ samlogon_state->parameter_control,
+ &samlogon_state->chall,
+ &lm_response,
+ &nt_response,
+ lm_key,
+ user_session_key,
+ error_string);
+
+ data_blob_free(&lm_response);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) {
+ /* for 'long' passwords, the LM password is invalid */
+ if (break_which == NO_NT && !lm_good) {
+ return true;
+ }
+ /* for 'old' passwords, we allow the server to be OK or wrong password */
+ if (samlogon_state->old_password) {
+ return true;
+ }
+ return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH));
+ } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) {
+ return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH) || (break_which == NO_NT));
+ } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ return false;
+ } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) {
+ return true;
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ return false;
+ }
+
+ if (break_which == NO_NT && !lm_good) {
+ *error_string = strdup("LM password is 'long' (> 14 chars and therefore invalid) but login did not fail!");
+ return false;
+ }
+
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+
+ switch (break_which) {
+ case NO_NT:
+ {
+ uint8_t lm_key_expected[16];
+ memcpy(lm_key_expected, lm_hash, 8);
+ memset(lm_key_expected+8, '\0', 8);
+ if (memcmp(lm_key_expected, user_session_key,
+ 16) != 0) {
+ *error_string = strdup("NT Session Key does not match expectations (should be first-8 LM hash)!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ d_printf("expected:\n");
+ dump_data(1, lm_key_expected, sizeof(lm_key_expected));
+ pass = false;
+ }
+ break;
+ }
+ default:
+ if (memcmp(session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ *error_string = strdup("NT Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, session_key.data, session_key.length);
+ pass = false;
+ }
+ }
+ return pass;
+}
+
+/*
+ * Test LM authentication, no NT response supplied
+ */
+
+static bool test_lm(struct samlogon_state *samlogon_state, char **error_string)
+{
+
+ return test_lm_ntlm_broken(samlogon_state, NO_NT, error_string);
+}
+
+/*
+ * Test the NTLM response only, no LM.
+ */
+
+static bool test_ntlm(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lm_ntlm_broken(samlogon_state, NO_LM, error_string);
+}
+
+/*
+ * Test the NTLM response only, but in the LM field.
+ */
+
+static bool test_ntlm_in_lm(struct samlogon_state *samlogon_state, char **error_string)
+{
+ bool lm_good;
+ bool pass = true;
+ NTSTATUS nt_status;
+ DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24);
+ DATA_BLOB session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16);
+
+ uint8_t lm_key[8];
+ uint8_t lm_hash[16];
+ uint8_t user_session_key[16];
+ uint8_t nt_hash[16];
+
+ ZERO_STRUCT(lm_key);
+ ZERO_STRUCT(user_session_key);
+
+ SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data,
+ nt_response.data);
+ E_md4hash(samlogon_state->password, nt_hash);
+ SMBsesskeygen_ntv1(nt_hash,
+ session_key.data);
+
+ lm_good = E_deshash(samlogon_state->password, lm_hash);
+ if (!lm_good) {
+ ZERO_STRUCT(lm_hash);
+ }
+ nt_status = check_samlogon(samlogon_state,
+ BREAK_NONE,
+ samlogon_state->parameter_control,
+ &samlogon_state->chall,
+ &nt_response,
+ NULL,
+ lm_key,
+ user_session_key,
+ error_string);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) {
+ /* for 'old' passwords, we allow the server to be OK or wrong password */
+ if (samlogon_state->old_password) {
+ return true;
+ }
+ return false;
+ } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ return false;
+ } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) {
+ return true;
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ return false;
+ }
+
+ if (lm_good) {
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+#if 0
+ } else {
+ if (memcmp(session_key.data, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations (first 8 session key)!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, session_key.data, 8);
+ pass = false;
+ }
+#endif
+ }
+ if (lm_good && memcmp(lm_hash, user_session_key, 8) != 0) {
+ uint8_t lm_key_expected[16];
+ memcpy(lm_key_expected, lm_hash, 8);
+ memset(lm_key_expected+8, '\0', 8);
+ if (memcmp(lm_key_expected, user_session_key,
+ 16) != 0) {
+ d_printf("NT Session Key does not match expectations (should be first-8 LM hash)!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ d_printf("expected:\n");
+ dump_data(1, lm_key_expected, sizeof(lm_key_expected));
+ pass = false;
+ }
+ }
+ return pass;
+}
+
+/*
+ * Test the NTLM response only, but in the both the NT and LM fields.
+ */
+
+static bool test_ntlm_in_both(struct samlogon_state *samlogon_state, char **error_string)
+{
+ bool pass = true;
+ bool lm_good;
+ NTSTATUS nt_status;
+ DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24);
+ DATA_BLOB session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16);
+
+ uint8_t lm_key[8];
+ uint8_t lm_hash[16];
+ uint8_t user_session_key[16];
+ uint8_t nt_hash[16];
+
+ ZERO_STRUCT(lm_key);
+ ZERO_STRUCT(user_session_key);
+
+ SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data,
+ nt_response.data);
+ E_md4hash(samlogon_state->password, nt_hash);
+ SMBsesskeygen_ntv1(nt_hash,
+ session_key.data);
+
+ lm_good = E_deshash(samlogon_state->password, lm_hash);
+ if (!lm_good) {
+ ZERO_STRUCT(lm_hash);
+ }
+
+ nt_status = check_samlogon(samlogon_state,
+ BREAK_NONE,
+ samlogon_state->parameter_control,
+ &samlogon_state->chall,
+ NULL,
+ &nt_response,
+ lm_key,
+ user_session_key,
+ error_string);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) {
+ /* for 'old' passwords, we allow the server to be OK or wrong password */
+ if (samlogon_state->old_password) {
+ return true;
+ }
+ return false;
+ } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ return false;
+ } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) {
+ return true;
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return false;
+ }
+
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+ if (memcmp(session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ d_printf("NT Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, session_key.data, session_key.length);
+ pass = false;
+ }
+
+
+ return pass;
+}
+
+/*
+ * Test the NTLMv2 and LMv2 responses
+ */
+
+enum ntlmv2_domain {
+ UPPER_DOMAIN,
+ NO_DOMAIN
+};
+
+static bool test_lmv2_ntlmv2_broken(struct samlogon_state *samlogon_state,
+ enum ntlm_break break_which,
+ enum ntlmv2_domain ntlmv2_domain,
+ char **error_string)
+{
+ bool pass = true;
+ NTSTATUS nt_status;
+ DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
+ DATA_BLOB lmv2_response = data_blob(NULL, 0);
+ DATA_BLOB lmv2_session_key = data_blob(NULL, 0);
+ DATA_BLOB ntlmv2_session_key = data_blob(NULL, 0);
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(samlogon_state->mem_ctx, samlogon_state->iconv_convenience, TEST_MACHINE_NAME, lp_workgroup(global_loadparm));
+
+ uint8_t lm_session_key[8];
+ uint8_t user_session_key[16];
+
+ ZERO_STRUCT(lm_session_key);
+ ZERO_STRUCT(user_session_key);
+
+ switch (ntlmv2_domain) {
+ case UPPER_DOMAIN:
+ if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx,
+ samlogon_state->account_name, samlogon_state->account_domain,
+ samlogon_state->password, &samlogon_state->chall,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response,
+ &lmv2_session_key, &ntlmv2_session_key)) {
+ data_blob_free(&names_blob);
+ return false;
+ }
+ break;
+ case NO_DOMAIN:
+ if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx,
+ samlogon_state->account_name, "",
+ samlogon_state->password, &samlogon_state->chall,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response,
+ &lmv2_session_key, &ntlmv2_session_key)) {
+ data_blob_free(&names_blob);
+ return false;
+ }
+ break;
+ }
+ data_blob_free(&names_blob);
+
+ nt_status = check_samlogon(samlogon_state,
+ break_which,
+ samlogon_state->parameter_control,
+ &samlogon_state->chall,
+ &lmv2_response,
+ &ntlmv2_response,
+ lm_session_key,
+ user_session_key,
+ error_string);
+
+ data_blob_free(&lmv2_response);
+ data_blob_free(&ntlmv2_response);
+
+
+ if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) {
+ /* for 'old' passwords, we allow the server to be OK or wrong password */
+ if (samlogon_state->old_password) {
+ return true;
+ }
+ return break_which == BREAK_BOTH;
+ } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) {
+ return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH) || (break_which == NO_NT));
+ } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ return false;
+ } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) {
+ return true;
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ return false;
+ }
+
+
+ switch (break_which) {
+ case NO_NT:
+ if (memcmp(lmv2_session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ d_printf("USER (LMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, lmv2_session_key.data, ntlmv2_session_key.length);
+ pass = false;
+ }
+ if (memcmp(lmv2_session_key.data, lm_session_key,
+ sizeof(lm_session_key)) != 0) {
+ d_printf("LM (LMv2) Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lmv2_session_key.data, 8);
+ pass = false;
+ }
+ break;
+ default:
+ if (memcmp(ntlmv2_session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ if (memcmp(lmv2_session_key.data, user_session_key,
+ sizeof(user_session_key)) == 0) {
+ d_printf("USER (NTLMv2) Session Key expected, got LMv2 sessesion key instead:\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length);
+ pass = false;
+
+ } else {
+ d_printf("USER (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length);
+ pass = false;
+ }
+ }
+ if (memcmp(ntlmv2_session_key.data, lm_session_key,
+ sizeof(lm_session_key)) != 0) {
+ if (memcmp(lmv2_session_key.data, lm_session_key,
+ sizeof(lm_session_key)) == 0) {
+ d_printf("LM (NTLMv2) Session Key expected, got LMv2 sessesion key instead:\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, ntlmv2_session_key.data, 8);
+ pass = false;
+ } else {
+ d_printf("LM (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, ntlmv2_session_key.data, 8);
+ pass = false;
+ }
+ }
+ }
+
+ return pass;
+}
+
+/*
+ * Test the NTLM and LMv2 responses
+ */
+
+static bool test_lmv2_ntlm_broken(struct samlogon_state *samlogon_state,
+ enum ntlm_break break_which,
+ enum ntlmv2_domain ntlmv2_domain,
+ char **error_string)
+{
+ bool pass = true;
+ NTSTATUS nt_status;
+ DATA_BLOB ntlmv2_response = data_blob(NULL, 0);
+ DATA_BLOB lmv2_response = data_blob(NULL, 0);
+ DATA_BLOB lmv2_session_key = data_blob(NULL, 0);
+ DATA_BLOB ntlmv2_session_key = data_blob(NULL, 0);
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(samlogon_state->mem_ctx, samlogon_state->iconv_convenience, lp_netbios_name(global_loadparm), lp_workgroup(global_loadparm));
+
+ DATA_BLOB ntlm_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24);
+ DATA_BLOB ntlm_session_key = data_blob_talloc(samlogon_state->mem_ctx, NULL, 16);
+
+ bool lm_good;
+ uint8_t lm_hash[16];
+ uint8_t lm_session_key[8];
+ uint8_t user_session_key[16];
+ uint8_t nt_hash[16];
+
+ SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data,
+ ntlm_response.data);
+ E_md4hash(samlogon_state->password, nt_hash);
+ SMBsesskeygen_ntv1(nt_hash,
+ ntlm_session_key.data);
+
+ lm_good = E_deshash(samlogon_state->password, lm_hash);
+ if (!lm_good) {
+ ZERO_STRUCT(lm_hash);
+ }
+
+ ZERO_STRUCT(lm_session_key);
+ ZERO_STRUCT(user_session_key);
+
+ switch (ntlmv2_domain) {
+ case UPPER_DOMAIN:
+ /* TODO - test with various domain cases, and without domain */
+ if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx,
+ samlogon_state->account_name, samlogon_state->account_domain,
+ samlogon_state->password, &samlogon_state->chall,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response,
+ &lmv2_session_key, &ntlmv2_session_key)) {
+ data_blob_free(&names_blob);
+ return false;
+ }
+ break;
+ case NO_DOMAIN:
+ /* TODO - test with various domain cases, and without domain */
+ if (!SMBNTLMv2encrypt(samlogon_state->mem_ctx,
+ samlogon_state->account_name, "",
+ samlogon_state->password, &samlogon_state->chall,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response,
+ &lmv2_session_key, &ntlmv2_session_key)) {
+ data_blob_free(&names_blob);
+ return false;
+ }
+ break;
+ }
+
+ data_blob_free(&names_blob);
+
+ nt_status = check_samlogon(samlogon_state,
+ break_which,
+ samlogon_state->parameter_control,
+ &samlogon_state->chall,
+ &lmv2_response,
+ &ntlm_response,
+ lm_session_key,
+ user_session_key,
+ error_string);
+
+ data_blob_free(&lmv2_response);
+ data_blob_free(&ntlmv2_response);
+
+
+ if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) {
+ /* for 'old' passwords, we allow the server to be OK or wrong password */
+ if (samlogon_state->old_password) {
+ return true;
+ }
+ return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH));
+ } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) {
+ return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH));
+ } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ return false;
+ } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) {
+ return true;
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ return false;
+ }
+
+ switch (break_which) {
+ case NO_NT:
+ if (memcmp(lmv2_session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ d_printf("USER (LMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, lmv2_session_key.data, ntlmv2_session_key.length);
+ pass = false;
+ }
+ if (memcmp(lmv2_session_key.data, lm_session_key,
+ sizeof(lm_session_key)) != 0) {
+ d_printf("LM (LMv2) Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lmv2_session_key.data, 8);
+ pass = false;
+ }
+ break;
+ case BREAK_LM:
+ if (memcmp(ntlm_session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ d_printf("USER (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, ntlm_session_key.data, ntlm_session_key.length);
+ pass = false;
+ }
+ if (lm_good) {
+ if (memcmp(lm_hash, lm_session_key,
+ sizeof(lm_session_key)) != 0) {
+ d_printf("LM Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+ } else {
+ static const uint8_t zeros[8];
+ if (memcmp(zeros, lm_session_key,
+ sizeof(lm_session_key)) != 0) {
+ d_printf("LM Session Key does not match expectations (zeros)!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, zeros, 8);
+ pass = false;
+ }
+ }
+ break;
+ default:
+ if (memcmp(ntlm_session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ d_printf("USER (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ d_printf("expected:\n");
+ dump_data(1, ntlm_session_key.data, ntlm_session_key.length);
+ pass = false;
+ }
+ if (memcmp(ntlm_session_key.data, lm_session_key,
+ sizeof(lm_session_key)) != 0) {
+ d_printf("LM (NTLMv2) Session Key does not match expectations!\n");
+ d_printf("lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, ntlm_session_key.data, 8);
+ pass = false;
+ }
+ }
+
+ return pass;
+}
+
+/*
+ * Test the NTLMv2 and LMv2 responses
+ */
+
+static bool test_lmv2_ntlmv2(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NONE, UPPER_DOMAIN, error_string);
+}
+
+#if 0
+static bool test_lmv2_ntlmv2_no_dom(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NONE, NO_DOMAIN, error_string);
+}
+#endif
+
+/*
+ * Test the LMv2 response only
+ */
+
+static bool test_lmv2(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, NO_NT, UPPER_DOMAIN, error_string);
+}
+
+static bool test_lmv2_no_dom(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, NO_NT, NO_DOMAIN, error_string);
+}
+
+/*
+ * Test the NTLMv2 response only
+ */
+
+static bool test_ntlmv2(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, NO_LM, UPPER_DOMAIN, error_string);
+}
+
+static bool test_ntlmv2_no_dom(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, NO_LM, NO_DOMAIN, error_string);
+}
+
+static bool test_lm_ntlm(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lm_ntlm_broken(samlogon_state, BREAK_NONE, error_string);
+}
+
+static bool test_ntlm_lm_broken(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lm_ntlm_broken(samlogon_state, BREAK_LM, error_string);
+}
+
+static bool test_ntlm_ntlm_broken(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lm_ntlm_broken(samlogon_state, BREAK_NT, error_string);
+}
+
+static bool test_lm_ntlm_both_broken(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lm_ntlm_broken(samlogon_state, BREAK_BOTH, error_string);
+}
+static bool test_ntlmv2_lmv2_broken(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_LM, UPPER_DOMAIN, error_string);
+}
+
+static bool test_ntlmv2_lmv2_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_LM, NO_DOMAIN, error_string);
+}
+
+static bool test_ntlmv2_ntlmv2_broken(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NT, UPPER_DOMAIN, error_string);
+}
+
+#if 0
+static bool test_ntlmv2_ntlmv2_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_NT, NO_DOMAIN, error_string);
+}
+#endif
+
+static bool test_ntlmv2_both_broken(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_BOTH, UPPER_DOMAIN, error_string);
+}
+
+static bool test_ntlmv2_both_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlmv2_broken(samlogon_state, BREAK_BOTH, NO_DOMAIN, error_string);
+}
+
+static bool test_lmv2_ntlm_both_broken(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlm_broken(samlogon_state, BREAK_BOTH, UPPER_DOMAIN, error_string);
+}
+
+static bool test_lmv2_ntlm_both_broken_no_dom(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlm_broken(samlogon_state, BREAK_BOTH, NO_DOMAIN, error_string);
+}
+
+static bool test_lmv2_ntlm_break_ntlm(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlm_broken(samlogon_state, BREAK_NT, UPPER_DOMAIN, error_string);
+}
+
+static bool test_lmv2_ntlm_break_ntlm_no_dom(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlm_broken(samlogon_state, BREAK_NT, NO_DOMAIN, error_string);
+}
+
+static bool test_lmv2_ntlm_break_lm(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlm_broken(samlogon_state, BREAK_LM, UPPER_DOMAIN, error_string);
+}
+
+static bool test_lmv2_ntlm_break_lm_no_dom(struct samlogon_state *samlogon_state, char **error_string)
+{
+ return test_lmv2_ntlm_broken(samlogon_state, BREAK_LM, NO_DOMAIN, error_string);
+}
+
+/*
+ * Test the NTLM2 response (extra challenge in LM feild)
+ *
+ * This test is the same as the 'break LM' test, but checks that the
+ * server implements NTLM2 session security in the right place
+ * (NETLOGON is the wrong place).
+ */
+
+static bool test_ntlm2(struct samlogon_state *samlogon_state, char **error_string)
+{
+ bool pass = true;
+ NTSTATUS nt_status;
+ DATA_BLOB lm_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24);
+ DATA_BLOB nt_response = data_blob_talloc(samlogon_state->mem_ctx, NULL, 24);
+
+ bool lm_good;
+ uint8_t lm_key[8];
+ uint8_t nt_hash[16];
+ uint8_t lm_hash[16];
+ uint8_t nt_key[16];
+ uint8_t user_session_key[16];
+ uint8_t expected_user_session_key[16];
+ uint8_t session_nonce_hash[16];
+ uint8_t client_chall[8];
+
+ struct MD5Context md5_session_nonce_ctx;
+ HMACMD5Context hmac_ctx;
+
+ ZERO_STRUCT(user_session_key);
+ ZERO_STRUCT(lm_key);
+ generate_random_buffer(client_chall, 8);
+
+ MD5Init(&md5_session_nonce_ctx);
+ MD5Update(&md5_session_nonce_ctx, samlogon_state->chall.data, 8);
+ MD5Update(&md5_session_nonce_ctx, client_chall, 8);
+ MD5Final(session_nonce_hash, &md5_session_nonce_ctx);
+
+ E_md4hash(samlogon_state->password, (uint8_t *)nt_hash);
+ lm_good = E_deshash(samlogon_state->password, (uint8_t *)lm_hash);
+ SMBsesskeygen_ntv1((const uint8_t *)nt_hash,
+ nt_key);
+
+ SMBNTencrypt(samlogon_state->password, samlogon_state->chall.data, nt_response.data);
+
+ memcpy(lm_response.data, session_nonce_hash, 8);
+ memset(lm_response.data + 8, 0, 16);
+
+ hmac_md5_init_rfc2104(nt_key, 16, &hmac_ctx);
+ hmac_md5_update(samlogon_state->chall.data, 8, &hmac_ctx);
+ hmac_md5_update(client_chall, 8, &hmac_ctx);
+ hmac_md5_final(expected_user_session_key, &hmac_ctx);
+
+ nt_status = check_samlogon(samlogon_state,
+ BREAK_NONE,
+ samlogon_state->parameter_control,
+ &samlogon_state->chall,
+ &lm_response,
+ &nt_response,
+ lm_key,
+ user_session_key,
+ error_string);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) {
+ /* for 'old' passwords, we allow the server to be OK or wrong password */
+ if (samlogon_state->old_password) {
+ return true;
+ }
+ return false;
+ } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ return false;
+ } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) {
+ return true;
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ return false;
+ }
+
+ if (lm_good) {
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Key does not match expectations!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+ } else {
+ static const uint8_t zeros[8];
+ if (memcmp(zeros, lm_key,
+ sizeof(lm_key)) != 0) {
+ d_printf("LM Session Key does not match expectations (zeros)!\n");
+ d_printf("lm_key:\n");
+ dump_data(1, lm_key, 8);
+ d_printf("expected:\n");
+ dump_data(1, zeros, 8);
+ pass = false;
+ }
+ }
+ if (memcmp(nt_key, user_session_key, 16) != 0) {
+ d_printf("NT Session Key does not match expectations (should be NT Key)!\n");
+ d_printf("user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ d_printf("expected:\n");
+ dump_data(1, nt_key, sizeof(nt_key));
+ pass = false;
+ }
+ return pass;
+}
+
+static bool test_plaintext(struct samlogon_state *samlogon_state, enum ntlm_break break_which, char **error_string)
+{
+ NTSTATUS nt_status;
+ DATA_BLOB nt_response = data_blob(NULL, 0);
+ DATA_BLOB lm_response = data_blob(NULL, 0);
+ char *password;
+ char *dospw;
+ void *unicodepw;
+
+ uint8_t user_session_key[16];
+ uint8_t lm_key[16];
+ uint8_t lm_hash[16];
+ static const uint8_t zeros[8];
+ DATA_BLOB chall = data_blob_talloc(samlogon_state->mem_ctx, zeros, sizeof(zeros));
+ bool lm_good = E_deshash(samlogon_state->password, lm_hash);
+
+ ZERO_STRUCT(user_session_key);
+
+ if ((push_ucs2_talloc(samlogon_state->mem_ctx,
+ samlogon_state->iconv_convenience,
+ &unicodepw, samlogon_state->password)) == -1) {
+ DEBUG(0, ("push_ucs2_allocate failed!\n"));
+ exit(1);
+ }
+
+ nt_response = data_blob_talloc(samlogon_state->mem_ctx, unicodepw, strlen_m(samlogon_state->password)*2);
+
+ password = strupper_talloc(samlogon_state->mem_ctx, samlogon_state->password);
+
+ if ((convert_string_talloc(samlogon_state->mem_ctx,
+ samlogon_state->iconv_convenience,
+ CH_UNIX, CH_DOS,
+ password, strlen(password)+1,
+ (void**)&dospw)) == -1) {
+ DEBUG(0, ("convert_string_talloc failed!\n"));
+ exit(1);
+ }
+
+ lm_response = data_blob_talloc(samlogon_state->mem_ctx, dospw, strlen(dospw));
+
+ nt_status = check_samlogon(samlogon_state,
+ break_which,
+ samlogon_state->parameter_control | MSV1_0_CLEARTEXT_PASSWORD_ALLOWED,
+ &chall,
+ &lm_response,
+ &nt_response,
+ lm_key,
+ user_session_key,
+ error_string);
+
+ if (NT_STATUS_EQUAL(NT_STATUS_WRONG_PASSWORD, nt_status)) {
+ /* for 'old' passwords, we allow the server to be OK or wrong password */
+ if (samlogon_state->old_password) {
+ return true;
+ }
+ /* for 'long' passwords, the LM password is invalid */
+ if (break_which == NO_NT && !lm_good) {
+ return true;
+ }
+ return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH));
+ } else if (NT_STATUS_EQUAL(NT_STATUS_NOT_FOUND, nt_status) && strchr_m(samlogon_state->account_name, '@')) {
+ return ((break_which == BREAK_NT) || (break_which == BREAK_BOTH) || (break_which == NO_NT));
+ } else if (!NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status)) {
+ SAFE_FREE(*error_string);
+ asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ return false;
+ } else if (NT_STATUS_EQUAL(samlogon_state->expected_error, nt_status) && !NT_STATUS_IS_OK(nt_status)) {
+ return true;
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ return false;
+ }
+
+ if (break_which == NO_NT && !lm_good) {
+ *error_string = strdup("LM password is 'long' (> 14 chars and therefore invalid) but login did not fail!");
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_plaintext_none_broken(struct samlogon_state *samlogon_state,
+ char **error_string) {
+ return test_plaintext(samlogon_state, BREAK_NONE, error_string);
+}
+
+static bool test_plaintext_lm_broken(struct samlogon_state *samlogon_state,
+ char **error_string) {
+ return test_plaintext(samlogon_state, BREAK_LM, error_string);
+}
+
+static bool test_plaintext_nt_broken(struct samlogon_state *samlogon_state,
+ char **error_string) {
+ return test_plaintext(samlogon_state, BREAK_NT, error_string);
+}
+
+static bool test_plaintext_nt_only(struct samlogon_state *samlogon_state,
+ char **error_string) {
+ return test_plaintext(samlogon_state, NO_LM, error_string);
+}
+
+static bool test_plaintext_lm_only(struct samlogon_state *samlogon_state,
+ char **error_string) {
+ return test_plaintext(samlogon_state, NO_NT, error_string);
+}
+
+/*
+ Tests:
+
+ - LM only
+ - NT and LM
+ - NT
+ - NT in LM field
+ - NT in both fields
+ - NTLMv2
+ - NTLMv2 and LMv2
+ - LMv2
+ - plaintext tests (in challenge-response fields)
+
+ check we get the correct session key in each case
+ check what values we get for the LM session key
+
+*/
+
+static const struct ntlm_tests {
+ bool (*fn)(struct samlogon_state *, char **);
+ const char *name;
+ bool expect_fail;
+} test_table[] = {
+ {test_lmv2_ntlmv2, "NTLMv2 and LMv2", false},
+#if 0
+ {test_lmv2_ntlmv2_no_dom, "NTLMv2 and LMv2 (no domain)", false},
+#endif
+ {test_lm, "LM", false},
+ {test_lm_ntlm, "LM and NTLM", false},
+ {test_lm_ntlm_both_broken, "LM and NTLM, both broken", false},
+ {test_ntlm, "NTLM", false},
+ {test_ntlm_in_lm, "NTLM in LM", false},
+ {test_ntlm_in_both, "NTLM in both", false},
+ {test_ntlmv2, "NTLMv2", false},
+ {test_ntlmv2_no_dom, "NTLMv2 (no domain)", false},
+ {test_lmv2, "LMv2", false},
+ {test_lmv2_no_dom, "LMv2 (no domain)", false},
+ {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken", false},
+ {test_ntlmv2_lmv2_broken_no_dom, "NTLMv2 and LMv2, LMv2 broken (no domain)", false},
+ {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken", false},
+#if 0
+ {test_ntlmv2_ntlmv2_broken_no_dom, "NTLMv2 and LMv2, NTLMv2 broken (no domain)", false},
+#endif
+ {test_ntlmv2_both_broken, "NTLMv2 and LMv2, both broken", false},
+ {test_ntlmv2_both_broken_no_dom, "NTLMv2 and LMv2, both broken (no domain)", false},
+ {test_ntlm_lm_broken, "NTLM and LM, LM broken", false},
+ {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken", false},
+ {test_ntlm2, "NTLM2 (NTLMv2 session security)", false},
+ {test_lmv2_ntlm_both_broken, "LMv2 and NTLM, both broken", false},
+ {test_lmv2_ntlm_both_broken_no_dom, "LMv2 and NTLM, both broken (no domain)", false},
+ {test_lmv2_ntlm_break_ntlm, "LMv2 and NTLM, NTLM broken", false},
+ {test_lmv2_ntlm_break_ntlm_no_dom, "LMv2 and NTLM, NTLM broken (no domain)", false},
+ {test_lmv2_ntlm_break_lm, "LMv2 and NTLM, LMv2 broken", false},
+ {test_lmv2_ntlm_break_lm_no_dom, "LMv2 and NTLM, LMv2 broken (no domain)", false},
+ {test_plaintext_none_broken, "Plaintext", false},
+ {test_plaintext_lm_broken, "Plaintext LM broken", false},
+ {test_plaintext_nt_broken, "Plaintext NT broken", false},
+ {test_plaintext_nt_only, "Plaintext NT only", false},
+ {test_plaintext_lm_only, "Plaintext LM only", false},
+ {NULL, NULL}
+};
+
+/*
+ try a netlogon SamLogon
+*/
+static bool test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ struct creds_CredentialState *creds,
+ const char *comment,
+ const char *account_domain, const char *account_name,
+ const char *plain_pass, uint32_t parameter_control,
+ NTSTATUS expected_error, bool old_password,
+ int n_subtests)
+{
+ TALLOC_CTX *fn_ctx = talloc_named(mem_ctx, 0, "test_SamLogon function-level context");
+ int i, v, l, f;
+ bool ret = true;
+ int validation_levels[] = {2,3,6};
+ int logon_levels[] = { 2, 6 };
+ int function_levels[] = {
+ NDR_NETR_LOGONSAMLOGON,
+ NDR_NETR_LOGONSAMLOGONEX,
+ NDR_NETR_LOGONSAMLOGONWITHFLAGS };
+ struct samlogon_state samlogon_state;
+
+ d_printf("testing netr_LogonSamLogon and netr_LogonSamLogonWithFlags\n");
+
+ samlogon_state.comment = comment;
+ samlogon_state.account_name = account_name;
+ samlogon_state.account_domain = account_domain;
+ samlogon_state.password = plain_pass;
+ samlogon_state.p = p;
+ samlogon_state.creds = creds;
+ samlogon_state.expected_error = expected_error;
+ samlogon_state.chall = data_blob_talloc(fn_ctx, NULL, 8);
+ samlogon_state.parameter_control = parameter_control;
+ samlogon_state.old_password = old_password;
+ samlogon_state.iconv_convenience = lp_iconv_convenience(tctx->lp_ctx);
+
+ generate_random_buffer(samlogon_state.chall.data, 8);
+ samlogon_state.r_flags.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p));
+ samlogon_state.r_flags.in.computer_name = TEST_MACHINE_NAME;
+ samlogon_state.r_flags.in.credential = &samlogon_state.auth;
+ samlogon_state.r_flags.in.return_authenticator = &samlogon_state.auth2;
+ samlogon_state.r_flags.in.flags = 0;
+
+ samlogon_state.r_ex.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p));
+ samlogon_state.r_ex.in.computer_name = TEST_MACHINE_NAME;
+ samlogon_state.r_ex.in.flags = 0;
+
+ samlogon_state.r.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p));
+ samlogon_state.r.in.computer_name = TEST_MACHINE_NAME;
+ samlogon_state.r.in.credential = &samlogon_state.auth;
+ samlogon_state.r.in.return_authenticator = &samlogon_state.auth2;
+
+ for (f=0;f<ARRAY_SIZE(function_levels);f++) {
+ for (i=0; test_table[i].fn; i++) {
+ if (n_subtests && (i > n_subtests)) {
+ continue;
+ }
+ for (v=0;v<ARRAY_SIZE(validation_levels);v++) {
+ for (l=0;l<ARRAY_SIZE(logon_levels);l++) {
+ char *error_string = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_named(fn_ctx, 0, "test_SamLogon inner loop");
+ samlogon_state.mem_ctx = tmp_ctx;
+ samlogon_state.function_level = function_levels[f];
+ samlogon_state.r.in.validation_level = validation_levels[v];
+ samlogon_state.r.in.logon_level = logon_levels[l];
+ samlogon_state.r_ex.in.validation_level = validation_levels[v];
+ samlogon_state.r_ex.in.logon_level = logon_levels[l];
+ samlogon_state.r_flags.in.validation_level = validation_levels[v];
+ samlogon_state.r_flags.in.logon_level = logon_levels[l];
+ if (!test_table[i].fn(&samlogon_state, &error_string)) {
+ d_printf("Testing '%s' [%s]\\[%s] '%s' at validation level %d, logon level %d, function %d: \n",
+ samlogon_state.comment,
+ samlogon_state.account_domain,
+ samlogon_state.account_name,
+ test_table[i].name, validation_levels[v],
+ logon_levels[l], function_levels[f]);
+
+ if (test_table[i].expect_fail) {
+ d_printf(" failed (expected, test incomplete): %s\n", error_string);
+ } else {
+ d_printf(" failed: %s\n", error_string);
+ ret = false;
+ }
+ SAFE_FREE(error_string);
+ }
+ talloc_free(tmp_ctx);
+ }
+ }
+ }
+ }
+ talloc_free(fn_ctx);
+ return ret;
+}
+
+/*
+ test an ADS style interactive domain logon
+*/
+bool test_InteractiveLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState *creds,
+ const char *comment,
+ const char *workstation_name,
+ const char *account_domain, const char *account_name,
+ const char *plain_pass, uint32_t parameter_control,
+ NTSTATUS expected_error)
+{
+ NTSTATUS status;
+ TALLOC_CTX *fn_ctx = talloc_named(mem_ctx, 0, "test_InteractiveLogon function-level context");
+ struct netr_LogonSamLogonWithFlags r;
+ struct netr_Authenticator a, ra;
+ struct netr_PasswordInfo pinfo;
+
+ ZERO_STRUCT(a);
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(ra);
+
+ creds_client_authenticator(creds, &a);
+
+ r.in.server_name = talloc_asprintf(fn_ctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &a;
+ r.in.return_authenticator = &ra;
+ r.in.logon_level = 5;
+ r.in.logon.password = &pinfo;
+ r.in.validation_level = 6;
+ r.in.flags = 0;
+
+ pinfo.identity_info.domain_name.string = account_domain;
+ pinfo.identity_info.parameter_control = parameter_control;
+ pinfo.identity_info.logon_id_low = 0;
+ pinfo.identity_info.logon_id_high = 0;
+ pinfo.identity_info.account_name.string = account_name;
+ pinfo.identity_info.workstation.string = workstation_name;
+
+ if (!E_deshash(plain_pass, pinfo.lmpassword.hash)) {
+ ZERO_STRUCT(pinfo.lmpassword.hash);
+ }
+ E_md4hash(plain_pass, pinfo.ntpassword.hash);
+
+ if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ creds_arcfour_crypt(creds, pinfo.lmpassword.hash, 16);
+ creds_arcfour_crypt(creds, pinfo.ntpassword.hash, 16);
+ } else {
+ creds_des_encrypt(creds, &pinfo.lmpassword);
+ creds_des_encrypt(creds, &pinfo.ntpassword);
+ }
+
+ d_printf("Testing netr_LogonSamLogonWithFlags '%s' (Interactive Logon)\n", comment);
+
+ status = dcerpc_netr_LogonSamLogonWithFlags(p, fn_ctx, &r);
+ if (!r.out.return_authenticator
+ || !creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ d_printf("Credential chaining failed\n");
+ talloc_free(fn_ctx);
+ return false;
+ }
+
+ talloc_free(fn_ctx);
+
+ if (!NT_STATUS_EQUAL(expected_error, status)) {
+ d_printf("[%s]\\[%s] netr_LogonSamLogonWithFlags - expected %s got %s\n",
+ account_domain, account_name, nt_errstr(expected_error), nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+
+bool torture_rpc_samlogon(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding *b;
+ struct cli_credentials *machine_credentials;
+ TALLOC_CTX *mem_ctx = talloc_init("torture_rpc_netlogon");
+ bool ret = true;
+ struct test_join *join_ctx = NULL;
+ struct test_join *user_ctx = NULL, *user_ctx_wrong_wks = NULL, *user_ctx_wrong_time = NULL;
+ char *user_password, *user_password_wrong_wks, *user_password_wrong_time;
+ const char *old_user_password;
+ char *test_machine_account;
+ const char *userdomain;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ int i;
+ int ci;
+
+ unsigned int credential_flags[] = {
+ NETLOGON_NEG_AUTH2_FLAGS,
+ NETLOGON_NEG_ARCFOUR,
+ NETLOGON_NEG_ARCFOUR | NETLOGON_NEG_128BIT,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS,
+ 0 /* yes, this is a valid flag, causes the use of DES */
+ };
+
+ struct creds_CredentialState *creds;
+
+ test_machine_account = talloc_asprintf(mem_ctx, "%s$", TEST_MACHINE_NAME);
+ /* We only need to join as a workstation here, and in future,
+ * if we wish to test against trusted domains, we must be a
+ * workstation here */
+ join_ctx = torture_join_domain(torture, TEST_MACHINE_NAME, ACB_WSTRUST,
+ &machine_credentials);
+ if (!join_ctx) {
+ d_printf("Failed to join as Workstation\n");
+ return false;
+ }
+
+ userdomain = torture_setting_string(torture, "userdomain", lp_workgroup(torture->lp_ctx));
+
+ user_ctx = torture_create_testuser(torture,
+ TEST_USER_NAME,
+ userdomain,
+ ACB_NORMAL,
+ (const char **)&user_password);
+ if (!user_ctx) {
+ d_printf("Failed to create a test user\n");
+ return false;
+ }
+
+ old_user_password = user_password;
+
+ test_ChangePasswordUser3(torture_join_samr_pipe(user_ctx), mem_ctx,
+ TEST_USER_NAME, 16 /* > 14 */, &user_password,
+ NULL, 0, false);
+
+ user_ctx_wrong_wks = torture_create_testuser(torture,
+ TEST_USER_NAME_WRONG_WKS,
+ userdomain,
+ ACB_NORMAL,
+ (const char **)&user_password_wrong_wks);
+ if (!user_ctx_wrong_wks) {
+ d_printf("Failed to create a test user (wrong workstation test)\n");
+ return false;
+ }
+
+ ZERO_STRUCT(u);
+ s.in.user_handle = torture_join_samr_user_policy(user_ctx_wrong_wks);
+ s.in.info = &u;
+ s.in.level = 21;
+
+ u.info21.fields_present = SAMR_FIELD_WORKSTATIONS;
+ u.info21.workstations.string = "not" TEST_MACHINE_NAME;
+
+ status = dcerpc_samr_SetUserInfo(torture_join_samr_pipe(user_ctx_wrong_wks), mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo (list of workstations) failed - %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ user_ctx_wrong_time
+ = torture_create_testuser(torture, TEST_USER_NAME_WRONG_TIME,
+ userdomain,
+ ACB_NORMAL,
+ (const char **)&user_password_wrong_time);
+ if (!user_ctx_wrong_time) {
+ d_printf("Failed to create a test user (wrong workstation test)\n");
+ return false;
+ }
+
+ ZERO_STRUCT(u);
+ s.in.user_handle = torture_join_samr_user_policy(user_ctx_wrong_time);
+ s.in.info = &u;
+ s.in.level = 21;
+
+ u.info21.fields_present = SAMR_FIELD_WORKSTATIONS | SAMR_FIELD_LOGON_HOURS;
+ u.info21.workstations.string = TEST_MACHINE_NAME;
+ u.info21.logon_hours.units_per_week = 168;
+ u.info21.logon_hours.bits = talloc_zero_array(mem_ctx, uint8_t, 168);
+
+ status = dcerpc_samr_SetUserInfo(torture_join_samr_pipe(user_ctx_wrong_time), mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo (logon times and list of workstations) failed - %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = torture_rpc_binding(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ /* We have to use schannel, otherwise the SamLogonEx fails
+ * with INTERNAL_ERROR */
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128;
+
+ status = dcerpc_pipe_connect_b(mem_ctx, &p, b,
+ &ndr_table_netlogon,
+ machine_credentials, torture->ev, torture->lp_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("RPC pipe connect as domain member failed: %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = dcerpc_schannel_creds(p->conn->security_state.generic_state, mem_ctx, &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ {
+
+ struct {
+ const char *comment;
+ const char *domain;
+ const char *username;
+ const char *password;
+ bool network_login;
+ NTSTATUS expected_interactive_error;
+ NTSTATUS expected_network_error;
+ uint32_t parameter_control;
+ bool old_password; /* Allow an old password to be accepted or rejected without error, as well as session key bugs */
+ } usercreds[] = {
+ {
+ .comment = "domain\\user",
+ .domain = cli_credentials_get_domain(cmdline_credentials),
+ .username = cli_credentials_get_username(cmdline_credentials),
+ .password = cli_credentials_get_password(cmdline_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .comment = "realm\\user",
+ .domain = cli_credentials_get_realm(cmdline_credentials),
+ .username = cli_credentials_get_username(cmdline_credentials),
+ .password = cli_credentials_get_password(cmdline_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .comment = "user@domain",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ cli_credentials_get_username(cmdline_credentials),
+ cli_credentials_get_domain(cmdline_credentials)
+ ),
+ .password = cli_credentials_get_password(cmdline_credentials),
+ .network_login = false, /* works for some things, but not NTLMv2. Odd */
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .comment = "user@realm",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ cli_credentials_get_username(cmdline_credentials),
+ cli_credentials_get_realm(cmdline_credentials)
+ ),
+ .password = cli_credentials_get_password(cmdline_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .comment = "machine domain\\user",
+ .domain = cli_credentials_get_domain(machine_credentials),
+ .username = cli_credentials_get_username(machine_credentials),
+ .password = cli_credentials_get_password(machine_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_NO_SUCH_USER,
+ .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT
+ },
+ {
+ .comment = "machine domain\\user",
+ .domain = cli_credentials_get_domain(machine_credentials),
+ .username = cli_credentials_get_username(machine_credentials),
+ .password = cli_credentials_get_password(machine_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_NO_SUCH_USER,
+ .expected_network_error = NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT
+ },
+ {
+ .comment = "machine realm\\user",
+ .domain = cli_credentials_get_realm(machine_credentials),
+ .username = cli_credentials_get_username(machine_credentials),
+ .password = cli_credentials_get_password(machine_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_NO_SUCH_USER,
+ .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT
+ },
+ {
+ .comment = "machine user@domain",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ cli_credentials_get_username(machine_credentials),
+ cli_credentials_get_domain(machine_credentials)
+ ),
+ .password = cli_credentials_get_password(machine_credentials),
+ .network_login = false, /* works for some things, but not NTLMv2. Odd */
+ .expected_interactive_error = NT_STATUS_NO_SUCH_USER,
+ .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT
+ },
+ {
+ .comment = "machine user@realm",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ cli_credentials_get_username(machine_credentials),
+ cli_credentials_get_realm(machine_credentials)
+ ),
+ .password = cli_credentials_get_password(machine_credentials),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_NO_SUCH_USER,
+ .parameter_control = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT
+ },
+ {
+ .comment = "test user (long pw): domain\\user",
+ .domain = userdomain,
+ .username = TEST_USER_NAME,
+ .password = user_password,
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .comment = "test user (long pw): user@realm",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ TEST_USER_NAME,
+ lp_realm(torture->lp_ctx)),
+ .password = user_password,
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ {
+ .comment = "test user (long pw): user@domain",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ TEST_USER_NAME,
+ userdomain),
+ .password = user_password,
+ .network_login = false, /* works for some things, but not NTLMv2. Odd */
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK
+ },
+ /* Oddball, can we use the old password ? */
+ {
+ .comment = "test user: user\\domain OLD PASSWORD",
+ .domain = userdomain,
+ .username = TEST_USER_NAME,
+ .password = old_user_password,
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_WRONG_PASSWORD,
+ .expected_network_error = NT_STATUS_OK,
+ .old_password = true
+ },
+ {
+ .comment = "test user (wong workstation): domain\\user",
+ .domain = userdomain,
+ .username = TEST_USER_NAME_WRONG_WKS,
+ .password = user_password_wrong_wks,
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_INVALID_WORKSTATION,
+ .expected_network_error = NT_STATUS_INVALID_WORKSTATION
+ }
+ };
+
+ /* Try all the tests for different username forms */
+ for (ci = 0; ci < ARRAY_SIZE(usercreds); ci++) {
+
+ if (!test_InteractiveLogon(p, mem_ctx, creds,
+ usercreds[ci].comment,
+ TEST_MACHINE_NAME,
+ usercreds[ci].domain,
+ usercreds[ci].username,
+ usercreds[ci].password,
+ usercreds[ci].parameter_control,
+ usercreds[ci].expected_interactive_error)) {
+ ret = false;
+ }
+
+ if (usercreds[ci].network_login) {
+ if (!test_SamLogon(p, mem_ctx, torture, creds,
+ usercreds[ci].comment,
+ usercreds[ci].domain,
+ usercreds[ci].username,
+ usercreds[ci].password,
+ usercreds[ci].parameter_control,
+ usercreds[ci].expected_network_error,
+ usercreds[ci].old_password,
+ 0)) {
+ ret = false;
+ }
+ }
+ }
+
+ /* Using the first username form, try the different
+ * credentials flag setups, on only one of the tests (checks
+ * session key encryption) */
+
+ for (i=0; i < ARRAY_SIZE(credential_flags); i++) {
+ /* TODO: Somehow we lost setting up the different credential flags here! */
+
+ if (!test_InteractiveLogon(p, mem_ctx, creds,
+ usercreds[0].comment,
+ TEST_MACHINE_NAME,
+ usercreds[0].domain,
+ usercreds[0].username,
+ usercreds[0].password,
+ usercreds[0].parameter_control,
+ usercreds[0].expected_interactive_error)) {
+ ret = false;
+ }
+
+ if (usercreds[0].network_login) {
+ if (!test_SamLogon(p, mem_ctx, torture, creds,
+ usercreds[0].comment,
+ usercreds[0].domain,
+ usercreds[0].username,
+ usercreds[0].password,
+ usercreds[0].parameter_control,
+ usercreds[0].expected_network_error,
+ usercreds[0].old_password,
+ 1)) {
+ ret = false;
+ }
+ }
+ }
+
+ }
+failed:
+ talloc_free(mem_ctx);
+
+ torture_leave_domain(torture, join_ctx);
+ torture_leave_domain(torture, user_ctx);
+ torture_leave_domain(torture, user_ctx_wrong_wks);
+ torture_leave_domain(torture, user_ctx_wrong_time);
+ return ret;
+}
diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c
new file mode 100644
index 0000000000..6afda6e9b5
--- /dev/null
+++ b/source4/torture/rpc/samr.c
@@ -0,0 +1,4648 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for samr rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "system/time.h"
+#include "librpc/gen_ndr/lsa.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/security/security.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+
+#define TEST_ACCOUNT_NAME "samrtorturetest"
+#define TEST_ALIASNAME "samrtorturetestalias"
+#define TEST_GROUPNAME "samrtorturetestgroup"
+#define TEST_MACHINENAME "samrtestmach$"
+#define TEST_DOMAINNAME "samrtestdom$"
+
+enum torture_samr_choice {
+ TORTURE_SAMR_PASSWORDS,
+ TORTURE_SAMR_USER_ATTRIBUTES,
+ TORTURE_SAMR_OTHER
+};
+
+static bool test_QueryUserInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle);
+
+static bool test_QueryUserInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle);
+
+static bool test_QueryAliasInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle);
+
+static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *acct_name,
+ struct policy_handle *domain_handle, char **password);
+
+static void init_lsa_String(struct lsa_String *string, const char *s)
+{
+ string->string = s;
+}
+
+bool test_samr_handle_Close(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_Close r;
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+
+ status = dcerpc_samr_Close(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close handle failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_Shutdown(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_Shutdown r;
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ printf("samr_Shutdown disabled - enable dangerous tests to use\n");
+ return true;
+ }
+
+ r.in.connect_handle = handle;
+
+ printf("testing samr_Shutdown\n");
+
+ status = dcerpc_samr_Shutdown(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("samr_Shutdown failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_SetDsrmPassword(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_SetDsrmPassword r;
+ struct lsa_String string;
+ struct samr_Password hash;
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ printf("samr_SetDsrmPassword disabled - enable dangerous tests to use\n");
+ return true;
+ }
+
+ E_md4hash("TeSTDSRM123", hash.hash);
+
+ init_lsa_String(&string, "Administrator");
+
+ r.in.name = &string;
+ r.in.unknown = 0;
+ r.in.hash = &hash;
+
+ printf("testing samr_SetDsrmPassword\n");
+
+ status = dcerpc_samr_SetDsrmPassword(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ printf("samr_SetDsrmPassword failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_QuerySecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QuerySecurity r;
+ struct samr_SetSecurity s;
+
+ r.in.handle = handle;
+ r.in.sec_info = 7;
+
+ status = dcerpc_samr_QuerySecurity(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (r.out.sdbuf == NULL) {
+ return false;
+ }
+
+ s.in.handle = handle;
+ s.in.sec_info = 7;
+ s.in.sdbuf = r.out.sdbuf;
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping SetSecurity test against Samba4\n");
+ return true;
+ }
+
+ status = dcerpc_samr_SetSecurity(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetSecurity failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_samr_QuerySecurity(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t base_acct_flags,
+ const char *base_account_name)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo s;
+ struct samr_SetUserInfo2 s2;
+ struct samr_QueryUserInfo q;
+ struct samr_QueryUserInfo q0;
+ union samr_UserInfo u;
+ bool ret = true;
+ const char *test_account_name;
+
+ uint32_t user_extra_flags = 0;
+ if (base_acct_flags == ACB_NORMAL) {
+ /* When created, accounts are expired by default */
+ user_extra_flags = ACB_PW_EXPIRED;
+ }
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+
+ s2.in.user_handle = handle;
+ s2.in.info = &u;
+
+ q.in.user_handle = handle;
+ q.out.info = &u;
+ q0 = q;
+
+#define TESTCALL(call, r) \
+ status = dcerpc_samr_ ##call(p, tctx, &r); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ printf(#call " level %u failed - %s (%s)\n", \
+ r.in.level, nt_errstr(status), __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define STRING_EQUAL(s1, s2, field) \
+ if ((s1 && !s2) || (s2 && !s1) || strcmp(s1, s2)) { \
+ printf("Failed to set %s to '%s' (%s)\n", \
+ #field, s2, __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define INT_EQUAL(i1, i2, field) \
+ if (i1 != i2) { \
+ printf("Failed to set %s to 0x%llx - got 0x%llx (%s)\n", \
+ #field, (unsigned long long)i2, (unsigned long long)i1, __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define TEST_USERINFO_STRING(lvl1, field1, lvl2, field2, value, fpval) do { \
+ printf("field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
+ q.in.level = lvl1; \
+ TESTCALL(QueryUserInfo, q) \
+ s.in.level = lvl1; \
+ s2.in.level = lvl1; \
+ u = *q.out.info; \
+ if (lvl1 == 21) { \
+ ZERO_STRUCT(u.info21); \
+ u.info21.fields_present = fpval; \
+ } \
+ init_lsa_String(&u.info ## lvl1.field1, value); \
+ TESTCALL(SetUserInfo, s) \
+ TESTCALL(SetUserInfo2, s2) \
+ init_lsa_String(&u.info ## lvl1.field1, ""); \
+ TESTCALL(QueryUserInfo, q); \
+ u = *q.out.info; \
+ STRING_EQUAL(u.info ## lvl1.field1.string, value, field1); \
+ q.in.level = lvl2; \
+ TESTCALL(QueryUserInfo, q) \
+ u = *q.out.info; \
+ STRING_EQUAL(u.info ## lvl2.field2.string, value, field2); \
+ } while (0)
+
+#define TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value, fpval) do { \
+ printf("field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
+ q.in.level = lvl1; \
+ TESTCALL(QueryUserInfo, q) \
+ s.in.level = lvl1; \
+ s2.in.level = lvl1; \
+ u = *q.out.info; \
+ if (lvl1 == 21) { \
+ uint8_t *bits = u.info21.logon_hours.bits; \
+ ZERO_STRUCT(u.info21); \
+ if (fpval == SAMR_FIELD_LOGON_HOURS) { \
+ u.info21.logon_hours.units_per_week = 168; \
+ u.info21.logon_hours.bits = bits; \
+ } \
+ u.info21.fields_present = fpval; \
+ } \
+ u.info ## lvl1.field1 = value; \
+ TESTCALL(SetUserInfo, s) \
+ TESTCALL(SetUserInfo2, s2) \
+ u.info ## lvl1.field1 = 0; \
+ TESTCALL(QueryUserInfo, q); \
+ u = *q.out.info; \
+ INT_EQUAL(u.info ## lvl1.field1, exp_value, field1); \
+ q.in.level = lvl2; \
+ TESTCALL(QueryUserInfo, q) \
+ u = *q.out.info; \
+ INT_EQUAL(u.info ## lvl2.field2, exp_value, field1); \
+ } while (0)
+
+#define TEST_USERINFO_INT(lvl1, field1, lvl2, field2, value, fpval) do { \
+ TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, value, fpval); \
+ } while (0)
+
+ q0.in.level = 12;
+ do { TESTCALL(QueryUserInfo, q0) } while (0);
+
+ TEST_USERINFO_STRING(2, comment, 1, comment, "xx2-1 comment", 0);
+ TEST_USERINFO_STRING(2, comment, 21, comment, "xx2-21 comment", 0);
+ TEST_USERINFO_STRING(21, comment, 21, comment, "xx21-21 comment",
+ SAMR_FIELD_COMMENT);
+
+ test_account_name = talloc_asprintf(tctx, "%sxx7-1", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 1, account_name, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-3", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 3, account_name, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-5", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 5, account_name, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-6", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 6, account_name, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-7", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 7, account_name, base_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-21", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 21, account_name, base_account_name, 0);
+ test_account_name = base_account_name;
+ TEST_USERINFO_STRING(21, account_name, 21, account_name, base_account_name,
+ SAMR_FIELD_ACCOUNT_NAME);
+
+ TEST_USERINFO_STRING(6, full_name, 1, full_name, "xx6-1 full_name", 0);
+ TEST_USERINFO_STRING(6, full_name, 3, full_name, "xx6-3 full_name", 0);
+ TEST_USERINFO_STRING(6, full_name, 5, full_name, "xx6-5 full_name", 0);
+ TEST_USERINFO_STRING(6, full_name, 6, full_name, "xx6-6 full_name", 0);
+ TEST_USERINFO_STRING(6, full_name, 8, full_name, "xx6-8 full_name", 0);
+ TEST_USERINFO_STRING(6, full_name, 21, full_name, "xx6-21 full_name", 0);
+ TEST_USERINFO_STRING(8, full_name, 21, full_name, "xx8-21 full_name", 0);
+ TEST_USERINFO_STRING(21, full_name, 21, full_name, "xx21-21 full_name",
+ SAMR_FIELD_FULL_NAME);
+
+ TEST_USERINFO_STRING(6, full_name, 1, full_name, "", 0);
+ TEST_USERINFO_STRING(6, full_name, 3, full_name, "", 0);
+ TEST_USERINFO_STRING(6, full_name, 5, full_name, "", 0);
+ TEST_USERINFO_STRING(6, full_name, 6, full_name, "", 0);
+ TEST_USERINFO_STRING(6, full_name, 8, full_name, "", 0);
+ TEST_USERINFO_STRING(6, full_name, 21, full_name, "", 0);
+ TEST_USERINFO_STRING(8, full_name, 21, full_name, "", 0);
+ TEST_USERINFO_STRING(21, full_name, 21, full_name, "",
+ SAMR_FIELD_FULL_NAME);
+
+ TEST_USERINFO_STRING(11, logon_script, 3, logon_script, "xx11-3 logon_script", 0);
+ TEST_USERINFO_STRING(11, logon_script, 5, logon_script, "xx11-5 logon_script", 0);
+ TEST_USERINFO_STRING(11, logon_script, 21, logon_script, "xx11-21 logon_script", 0);
+ TEST_USERINFO_STRING(21, logon_script, 21, logon_script, "xx21-21 logon_script",
+ SAMR_FIELD_LOGON_SCRIPT);
+
+ TEST_USERINFO_STRING(12, profile_path, 3, profile_path, "xx12-3 profile_path", 0);
+ TEST_USERINFO_STRING(12, profile_path, 5, profile_path, "xx12-5 profile_path", 0);
+ TEST_USERINFO_STRING(12, profile_path, 21, profile_path, "xx12-21 profile_path", 0);
+ TEST_USERINFO_STRING(21, profile_path, 21, profile_path, "xx21-21 profile_path",
+ SAMR_FIELD_PROFILE_PATH);
+
+ TEST_USERINFO_STRING(10, home_directory, 3, home_directory, "xx10-3 home_directory", 0);
+ TEST_USERINFO_STRING(10, home_directory, 5, home_directory, "xx10-5 home_directory", 0);
+ TEST_USERINFO_STRING(10, home_directory, 21, home_directory, "xx10-21 home_directory", 0);
+ TEST_USERINFO_STRING(21, home_directory, 21, home_directory, "xx21-21 home_directory",
+ SAMR_FIELD_HOME_DIRECTORY);
+ TEST_USERINFO_STRING(21, home_directory, 10, home_directory, "xx21-10 home_directory",
+ SAMR_FIELD_HOME_DIRECTORY);
+
+ TEST_USERINFO_STRING(10, home_drive, 3, home_drive, "xx10-3 home_drive", 0);
+ TEST_USERINFO_STRING(10, home_drive, 5, home_drive, "xx10-5 home_drive", 0);
+ TEST_USERINFO_STRING(10, home_drive, 21, home_drive, "xx10-21 home_drive", 0);
+ TEST_USERINFO_STRING(21, home_drive, 21, home_drive, "xx21-21 home_drive",
+ SAMR_FIELD_HOME_DRIVE);
+ TEST_USERINFO_STRING(21, home_drive, 10, home_drive, "xx21-10 home_drive",
+ SAMR_FIELD_HOME_DRIVE);
+
+ TEST_USERINFO_STRING(13, description, 1, description, "xx13-1 description", 0);
+ TEST_USERINFO_STRING(13, description, 5, description, "xx13-5 description", 0);
+ TEST_USERINFO_STRING(13, description, 21, description, "xx13-21 description", 0);
+ TEST_USERINFO_STRING(21, description, 21, description, "xx21-21 description",
+ SAMR_FIELD_DESCRIPTION);
+
+ TEST_USERINFO_STRING(14, workstations, 3, workstations, "14workstation3", 0);
+ TEST_USERINFO_STRING(14, workstations, 5, workstations, "14workstation4", 0);
+ TEST_USERINFO_STRING(14, workstations, 21, workstations, "14workstation21", 0);
+ TEST_USERINFO_STRING(21, workstations, 21, workstations, "21workstation21",
+ SAMR_FIELD_WORKSTATIONS);
+ TEST_USERINFO_STRING(21, workstations, 3, workstations, "21workstation3",
+ SAMR_FIELD_WORKSTATIONS);
+ TEST_USERINFO_STRING(21, workstations, 5, workstations, "21workstation5",
+ SAMR_FIELD_WORKSTATIONS);
+ TEST_USERINFO_STRING(21, workstations, 14, workstations, "21workstation14",
+ SAMR_FIELD_WORKSTATIONS);
+
+ TEST_USERINFO_STRING(20, parameters, 21, parameters, "xx20-21 parameters", 0);
+ TEST_USERINFO_STRING(21, parameters, 21, parameters, "xx21-21 parameters",
+ SAMR_FIELD_PARAMETERS);
+ TEST_USERINFO_STRING(21, parameters, 20, parameters, "xx21-20 parameters",
+ SAMR_FIELD_PARAMETERS);
+
+ TEST_USERINFO_INT(2, country_code, 2, country_code, __LINE__, 0);
+ TEST_USERINFO_INT(2, country_code, 21, country_code, __LINE__, 0);
+ TEST_USERINFO_INT(21, country_code, 21, country_code, __LINE__,
+ SAMR_FIELD_COUNTRY_CODE);
+ TEST_USERINFO_INT(21, country_code, 2, country_code, __LINE__,
+ SAMR_FIELD_COUNTRY_CODE);
+
+ TEST_USERINFO_INT(2, code_page, 21, code_page, __LINE__, 0);
+ TEST_USERINFO_INT(21, code_page, 21, code_page, __LINE__,
+ SAMR_FIELD_CODE_PAGE);
+ TEST_USERINFO_INT(21, code_page, 2, code_page, __LINE__,
+ SAMR_FIELD_CODE_PAGE);
+
+ TEST_USERINFO_INT(17, acct_expiry, 21, acct_expiry, __LINE__, 0);
+ TEST_USERINFO_INT(17, acct_expiry, 5, acct_expiry, __LINE__, 0);
+ TEST_USERINFO_INT(21, acct_expiry, 21, acct_expiry, __LINE__,
+ SAMR_FIELD_ACCT_EXPIRY);
+ TEST_USERINFO_INT(21, acct_expiry, 5, acct_expiry, __LINE__,
+ SAMR_FIELD_ACCT_EXPIRY);
+ TEST_USERINFO_INT(21, acct_expiry, 17, acct_expiry, __LINE__,
+ SAMR_FIELD_ACCT_EXPIRY);
+
+ TEST_USERINFO_INT(4, logon_hours.bits[3], 3, logon_hours.bits[3], 1, 0);
+ TEST_USERINFO_INT(4, logon_hours.bits[3], 5, logon_hours.bits[3], 2, 0);
+ TEST_USERINFO_INT(4, logon_hours.bits[3], 21, logon_hours.bits[3], 3, 0);
+ TEST_USERINFO_INT(21, logon_hours.bits[3], 21, logon_hours.bits[3], 4,
+ SAMR_FIELD_LOGON_HOURS);
+
+ TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags,
+ (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ),
+ (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ | user_extra_flags),
+ 0);
+ TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags,
+ (base_acct_flags | ACB_DISABLED),
+ (base_acct_flags | ACB_DISABLED | user_extra_flags),
+ 0);
+
+ /* Setting PWNOEXP clears the magic ACB_PW_EXPIRED flag */
+ TEST_USERINFO_INT_EXP(16, acct_flags, 5, acct_flags,
+ (base_acct_flags | ACB_DISABLED | ACB_PWNOEXP),
+ (base_acct_flags | ACB_DISABLED | ACB_PWNOEXP),
+ 0);
+ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags,
+ (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ),
+ (base_acct_flags | ACB_DISABLED | ACB_HOMDIRREQ | user_extra_flags),
+ 0);
+
+
+ /* The 'autolock' flag doesn't stick - check this */
+ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags,
+ (base_acct_flags | ACB_DISABLED | ACB_AUTOLOCK),
+ (base_acct_flags | ACB_DISABLED | user_extra_flags),
+ 0);
+#if 0
+ /* Removing the 'disabled' flag doesn't stick - check this */
+ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags,
+ (base_acct_flags),
+ (base_acct_flags | ACB_DISABLED | user_extra_flags),
+ 0);
+#endif
+ /* The 'store plaintext' flag does stick */
+ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags,
+ (base_acct_flags | ACB_DISABLED | ACB_ENC_TXT_PWD_ALLOWED),
+ (base_acct_flags | ACB_DISABLED | ACB_ENC_TXT_PWD_ALLOWED | user_extra_flags),
+ 0);
+ /* The 'use DES' flag does stick */
+ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags,
+ (base_acct_flags | ACB_DISABLED | ACB_USE_DES_KEY_ONLY),
+ (base_acct_flags | ACB_DISABLED | ACB_USE_DES_KEY_ONLY | user_extra_flags),
+ 0);
+ /* The 'don't require kerberos pre-authentication flag does stick */
+ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags,
+ (base_acct_flags | ACB_DISABLED | ACB_DONT_REQUIRE_PREAUTH),
+ (base_acct_flags | ACB_DISABLED | ACB_DONT_REQUIRE_PREAUTH | user_extra_flags),
+ 0);
+ /* The 'no kerberos PAC required' flag sticks */
+ TEST_USERINFO_INT_EXP(16, acct_flags, 21, acct_flags,
+ (base_acct_flags | ACB_DISABLED | ACB_NO_AUTH_DATA_REQD),
+ (base_acct_flags | ACB_DISABLED | ACB_NO_AUTH_DATA_REQD | user_extra_flags),
+ 0);
+
+ TEST_USERINFO_INT_EXP(21, acct_flags, 21, acct_flags,
+ (base_acct_flags | ACB_DISABLED),
+ (base_acct_flags | ACB_DISABLED | user_extra_flags),
+ SAMR_FIELD_ACCT_FLAGS);
+
+#if 0
+ /* these fail with win2003 - it appears you can't set the primary gid?
+ the set succeeds, but the gid isn't changed. Very weird! */
+ TEST_USERINFO_INT(9, primary_gid, 1, primary_gid, 513);
+ TEST_USERINFO_INT(9, primary_gid, 3, primary_gid, 513);
+ TEST_USERINFO_INT(9, primary_gid, 5, primary_gid, 513);
+ TEST_USERINFO_INT(9, primary_gid, 21, primary_gid, 513);
+#endif
+
+ return ret;
+}
+
+/*
+ generate a random password for password change tests
+*/
+static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len)
+{
+ size_t len = MAX(8, min_len) + (random() % 6);
+ char *s = generate_random_str(mem_ctx, len);
+ printf("Generated password '%s'\n", s);
+ return s;
+}
+
+/*
+ generate a random password for password change tests (fixed length)
+*/
+static char *samr_rand_pass_fixed_len(TALLOC_CTX *mem_ctx, int len)
+{
+ char *s = generate_random_str(mem_ctx, len);
+ printf("Generated password '%s'\n", s);
+ return s;
+}
+
+static bool test_SetUserPass(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, char **password)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ bool ret = true;
+ DATA_BLOB session_key;
+ char *newpass;
+ struct samr_GetUserPwInfo pwp;
+ int policy_min_pw_len = 0;
+ pwp.in.user_handle = handle;
+
+ status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = pwp.out.info.min_password_length;
+ }
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 24;
+
+ encode_pw_buffer(u.info24.password.data, newpass, STR_UNICODE);
+ /* w2k3 ignores this length */
+ u.info24.pw_len = strlen_m(newpass) * 2;
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ arcfour_crypt_blob(u.info24.password.data, 516, &session_key);
+
+ printf("Testing SetUserInfo level 24 (set password)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+
+static bool test_SetUserPass_23(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, uint32_t fields_present,
+ char **password)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ bool ret = true;
+ DATA_BLOB session_key;
+ char *newpass;
+ struct samr_GetUserPwInfo pwp;
+ int policy_min_pw_len = 0;
+ pwp.in.user_handle = handle;
+
+ status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = pwp.out.info.min_password_length;
+ }
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 23;
+
+ ZERO_STRUCT(u);
+
+ u.info23.info.fields_present = fields_present;
+
+ encode_pw_buffer(u.info23.password.data, newpass, STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ arcfour_crypt_blob(u.info23.password.data, 516, &session_key);
+
+ printf("Testing SetUserInfo level 23 (set password)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ encode_pw_buffer(u.info23.password.data, newpass, STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ /* This should break the key nicely */
+ session_key.length--;
+ arcfour_crypt_blob(u.info23.password.data, 516, &session_key);
+
+ printf("Testing SetUserInfo level 23 (set password) with wrong password\n");
+
+ status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_SetUserPassEx(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, bool makeshort,
+ char **password)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ bool ret = true;
+ DATA_BLOB session_key;
+ DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ uint8_t confounder[16];
+ char *newpass;
+ struct MD5Context ctx;
+ struct samr_GetUserPwInfo pwp;
+ int policy_min_pw_len = 0;
+ pwp.in.user_handle = handle;
+
+ status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = pwp.out.info.min_password_length;
+ }
+ if (makeshort && policy_min_pw_len) {
+ newpass = samr_rand_pass_fixed_len(mem_ctx, policy_min_pw_len - 1);
+ } else {
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+ }
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 26;
+
+ encode_pw_buffer(u.info26.password.data, newpass, STR_UNICODE);
+ u.info26.pw_len = strlen(newpass);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, confounder, 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(u.info26.password.data, 516, &confounded_session_key);
+ memcpy(&u.info26.password.data[516], confounder, 16);
+
+ printf("Testing SetUserInfo level 26 (set password ex)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* This should break the key nicely */
+ confounded_session_key.data[0]++;
+
+ arcfour_crypt_blob(u.info26.password.data, 516, &confounded_session_key);
+ memcpy(&u.info26.password.data[516], confounder, 16);
+
+ printf("Testing SetUserInfo level 26 (set password ex) with wrong session key\n");
+
+ status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+static bool test_SetUserPass_25(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, uint32_t fields_present,
+ char **password)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ bool ret = true;
+ DATA_BLOB session_key;
+ DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+ struct MD5Context ctx;
+ uint8_t confounder[16];
+ char *newpass;
+ struct samr_GetUserPwInfo pwp;
+ int policy_min_pw_len = 0;
+ pwp.in.user_handle = handle;
+
+ status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = pwp.out.info.min_password_length;
+ }
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 25;
+
+ ZERO_STRUCT(u);
+
+ u.info25.info.fields_present = fields_present;
+
+ encode_pw_buffer(u.info25.password.data, newpass, STR_UNICODE);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_random_buffer((uint8_t *)confounder, 16);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, confounder, 16);
+ MD5Update(&ctx, session_key.data, session_key.length);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key);
+ memcpy(&u.info25.password.data[516], confounder, 16);
+
+ printf("Testing SetUserInfo level 25 (set password ex)\n");
+
+ status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* This should break the key nicely */
+ confounded_session_key.data[0]++;
+
+ arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key);
+ memcpy(&u.info25.password.data[516], confounder, 16);
+
+ printf("Testing SetUserInfo level 25 (set password ex) with wrong session key\n");
+
+ status = dcerpc_samr_SetUserInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_SetAliasInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_SetAliasInfo r;
+ struct samr_QueryAliasInfo q;
+ uint16_t levels[] = {2, 3};
+ int i;
+ bool ret = true;
+
+ /* Ignoring switch level 1, as that includes the number of members for the alias
+ * and setting this to a wrong value might have negative consequences
+ */
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing SetAliasInfo level %u\n", levels[i]);
+
+ r.in.alias_handle = handle;
+ r.in.level = levels[i];
+ r.in.info = talloc(tctx, union samr_AliasInfo);
+ switch (r.in.level) {
+ case ALIASINFONAME: init_lsa_String(&r.in.info->name,TEST_ALIASNAME); break;
+ case ALIASINFODESCRIPTION: init_lsa_String(&r.in.info->description,
+ "Test Description, should test I18N as well"); break;
+ case ALIASINFOALL: printf("ALIASINFOALL ignored\n"); break;
+ }
+
+ status = dcerpc_samr_SetAliasInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetAliasInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+
+ q.in.alias_handle = handle;
+ q.in.level = levels[i];
+
+ status = dcerpc_samr_QueryAliasInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryAliasInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetGroupsForUser(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *user_handle)
+{
+ struct samr_GetGroupsForUser r;
+ NTSTATUS status;
+ bool ret = true;
+
+ printf("testing GetGroupsForUser\n");
+
+ r.in.user_handle = user_handle;
+
+ status = dcerpc_samr_GetGroupsForUser(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetGroupsForUser failed - %s\n",nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+
+}
+
+static bool test_GetDomPwInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct lsa_String *domain_name)
+{
+ NTSTATUS status;
+ struct samr_GetDomPwInfo r;
+ bool ret = true;
+
+ r.in.domain_name = domain_name;
+ printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetDomPwInfo failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ r.in.domain_name->string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetDomPwInfo failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ r.in.domain_name->string = "\\\\__NONAME__";
+ printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetDomPwInfo failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ r.in.domain_name->string = "\\\\Builtin";
+ printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetDomPwInfo failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+
+ return ret;
+}
+
+static bool test_GetUserPwInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_GetUserPwInfo r;
+ bool ret = true;
+
+ printf("Testing GetUserPwInfo\n");
+
+ r.in.user_handle = handle;
+
+ status = dcerpc_samr_GetUserPwInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetUserPwInfo failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static NTSTATUS test_LookupName(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *domain_handle, const char *name,
+ uint32_t *rid)
+{
+ NTSTATUS status;
+ struct samr_LookupNames n;
+ struct lsa_String sname[2];
+
+ init_lsa_String(&sname[0], name);
+
+ n.in.domain_handle = domain_handle;
+ n.in.num_names = 1;
+ n.in.names = sname;
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (NT_STATUS_IS_OK(status)) {
+ *rid = n.out.rids.ids[0];
+ } else {
+ return status;
+ }
+
+ init_lsa_String(&sname[1], "xxNONAMExx");
+ n.in.num_names = 2;
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED)) {
+ printf("LookupNames[2] failed - %s\n", nt_errstr(status));
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return status;
+ }
+
+ n.in.num_names = 0;
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames[0] failed - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ init_lsa_String(&sname[0], "xxNONAMExx");
+ n.in.num_names = 1;
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ printf("LookupNames[1 bad name] failed - %s\n", nt_errstr(status));
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return status;
+ }
+
+ init_lsa_String(&sname[0], "xxNONAMExx");
+ init_lsa_String(&sname[1], "xxNONAME2xx");
+ n.in.num_names = 2;
+ status = dcerpc_samr_LookupNames(p, tctx, &n);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
+ printf("LookupNames[2 bad names] failed - %s\n", nt_errstr(status));
+ if (NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS test_OpenUser_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle,
+ const char *name, struct policy_handle *user_handle)
+{
+ NTSTATUS status;
+ struct samr_OpenUser r;
+ uint32_t rid;
+
+ status = test_LookupName(p, mem_ctx, domain_handle, name, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ r.in.domain_handle = domain_handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.user_handle = user_handle;
+ status = dcerpc_samr_OpenUser(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser_byname(%s -> %d) failed - %s\n", name, rid, nt_errstr(status));
+ }
+
+ return status;
+}
+
+#if 0
+static bool test_ChangePasswordNT3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_ChangePasswordUser r;
+ bool ret = true;
+ struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6;
+ struct policy_handle user_handle;
+ char *oldpass = "test";
+ char *newpass = "test2";
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+
+ status = test_OpenUser_byname(p, mem_ctx, handle, "testuser", &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ printf("Testing ChangePasswordUser for user 'testuser'\n");
+
+ printf("old password: %s\n", oldpass);
+ printf("new password: %s\n", newpass);
+
+ E_md4hash(oldpass, old_nt_hash);
+ E_md4hash(newpass, new_nt_hash);
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+ E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash);
+ E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash);
+ E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash);
+ E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash);
+ E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash);
+
+ r.in.handle = &user_handle;
+ r.in.lm_present = 1;
+ r.in.old_lm_crypted = &hash1;
+ r.in.new_lm_crypted = &hash2;
+ r.in.nt_present = 1;
+ r.in.old_nt_crypted = &hash3;
+ r.in.new_nt_crypted = &hash4;
+ r.in.cross1_present = 1;
+ r.in.nt_cross = &hash5;
+ r.in.cross2_present = 1;
+ r.in.lm_cross = &hash6;
+
+ status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+#endif
+
+static bool test_ChangePasswordUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *acct_name,
+ struct policy_handle *handle, char **password)
+{
+ NTSTATUS status;
+ struct samr_ChangePasswordUser r;
+ bool ret = true;
+ struct samr_Password hash1, hash2, hash3, hash4, hash5, hash6;
+ struct policy_handle user_handle;
+ char *oldpass;
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+ bool changed = true;
+
+ char *newpass;
+ struct samr_GetUserPwInfo pwp;
+ int policy_min_pw_len = 0;
+
+ status = test_OpenUser_byname(p, mem_ctx, handle, acct_name, &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ pwp.in.user_handle = &user_handle;
+
+ status = dcerpc_samr_GetUserPwInfo(p, mem_ctx, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = pwp.out.info.min_password_length;
+ }
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+
+ printf("Testing ChangePasswordUser\n");
+
+ if (!*password) {
+ printf("Failing ChangePasswordUser as old password was NULL. Previous test failed?\n");
+ return false;
+ }
+
+ oldpass = *password;
+
+ E_md4hash(oldpass, old_nt_hash);
+ E_md4hash(newpass, new_nt_hash);
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+ E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash);
+ E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash);
+ E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash);
+ E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash);
+ E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash);
+
+ r.in.user_handle = &user_handle;
+ r.in.lm_present = 1;
+ /* Break the LM hash */
+ hash1.hash[0]++;
+ r.in.old_lm_crypted = &hash1;
+ r.in.new_lm_crypted = &hash2;
+ r.in.nt_present = 1;
+ r.in.old_nt_crypted = &hash3;
+ r.in.new_nt_crypted = &hash4;
+ r.in.cross1_present = 1;
+ r.in.nt_cross = &hash5;
+ r.in.cross2_present = 1;
+ r.in.lm_cross = &hash6;
+
+ status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM hash, got %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ /* Unbreak the LM hash */
+ hash1.hash[0]--;
+
+ r.in.user_handle = &user_handle;
+ r.in.lm_present = 1;
+ r.in.old_lm_crypted = &hash1;
+ r.in.new_lm_crypted = &hash2;
+ /* Break the NT hash */
+ hash3.hash[0]--;
+ r.in.nt_present = 1;
+ r.in.old_nt_crypted = &hash3;
+ r.in.new_nt_crypted = &hash4;
+ r.in.cross1_present = 1;
+ r.in.nt_cross = &hash5;
+ r.in.cross2_present = 1;
+ r.in.lm_cross = &hash6;
+
+ status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the NT hash, got %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ /* Unbreak the NT hash */
+ hash3.hash[0]--;
+
+ r.in.user_handle = &user_handle;
+ r.in.lm_present = 1;
+ r.in.old_lm_crypted = &hash1;
+ r.in.new_lm_crypted = &hash2;
+ r.in.nt_present = 1;
+ r.in.old_nt_crypted = &hash3;
+ r.in.new_nt_crypted = &hash4;
+ r.in.cross1_present = 1;
+ r.in.nt_cross = &hash5;
+ r.in.cross2_present = 1;
+ /* Break the LM cross */
+ hash6.hash[0]++;
+ r.in.lm_cross = &hash6;
+
+ status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM cross-hash, got %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ /* Unbreak the LM cross */
+ hash6.hash[0]--;
+
+ r.in.user_handle = &user_handle;
+ r.in.lm_present = 1;
+ r.in.old_lm_crypted = &hash1;
+ r.in.new_lm_crypted = &hash2;
+ r.in.nt_present = 1;
+ r.in.old_nt_crypted = &hash3;
+ r.in.new_nt_crypted = &hash4;
+ r.in.cross1_present = 1;
+ /* Break the NT cross */
+ hash5.hash[0]++;
+ r.in.nt_cross = &hash5;
+ r.in.cross2_present = 1;
+ r.in.lm_cross = &hash6;
+
+ status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the NT cross-hash, got %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ /* Unbreak the NT cross */
+ hash5.hash[0]--;
+
+
+ /* Reset the hashes to not broken values */
+ E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash);
+ E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash);
+ E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash);
+ E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash);
+ E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash);
+
+ r.in.user_handle = &user_handle;
+ r.in.lm_present = 1;
+ r.in.old_lm_crypted = &hash1;
+ r.in.new_lm_crypted = &hash2;
+ r.in.nt_present = 1;
+ r.in.old_nt_crypted = &hash3;
+ r.in.new_nt_crypted = &hash4;
+ r.in.cross1_present = 1;
+ r.in.nt_cross = &hash5;
+ r.in.cross2_present = 0;
+ r.in.lm_cross = NULL;
+
+ status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
+ if (NT_STATUS_IS_OK(status)) {
+ changed = true;
+ *password = newpass;
+ } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, status)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ oldpass = newpass;
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+
+ E_md4hash(oldpass, old_nt_hash);
+ E_md4hash(newpass, new_nt_hash);
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+
+ /* Reset the hashes to not broken values */
+ E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash);
+ E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash);
+ E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash);
+ E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash);
+ E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash);
+
+ r.in.user_handle = &user_handle;
+ r.in.lm_present = 1;
+ r.in.old_lm_crypted = &hash1;
+ r.in.new_lm_crypted = &hash2;
+ r.in.nt_present = 1;
+ r.in.old_nt_crypted = &hash3;
+ r.in.new_nt_crypted = &hash4;
+ r.in.cross1_present = 0;
+ r.in.nt_cross = NULL;
+ r.in.cross2_present = 1;
+ r.in.lm_cross = &hash6;
+
+ status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
+ if (NT_STATUS_IS_OK(status)) {
+ changed = true;
+ *password = newpass;
+ } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, status)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED, got %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ oldpass = newpass;
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+
+ E_md4hash(oldpass, old_nt_hash);
+ E_md4hash(newpass, new_nt_hash);
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+
+ /* Reset the hashes to not broken values */
+ E_old_pw_hash(new_lm_hash, old_lm_hash, hash1.hash);
+ E_old_pw_hash(old_lm_hash, new_lm_hash, hash2.hash);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, hash3.hash);
+ E_old_pw_hash(old_nt_hash, new_nt_hash, hash4.hash);
+ E_old_pw_hash(old_lm_hash, new_nt_hash, hash5.hash);
+ E_old_pw_hash(old_nt_hash, new_lm_hash, hash6.hash);
+
+ r.in.user_handle = &user_handle;
+ r.in.lm_present = 1;
+ r.in.old_lm_crypted = &hash1;
+ r.in.new_lm_crypted = &hash2;
+ r.in.nt_present = 1;
+ r.in.old_nt_crypted = &hash3;
+ r.in.new_nt_crypted = &hash4;
+ r.in.cross1_present = 1;
+ r.in.nt_cross = &hash5;
+ r.in.cross2_present = 1;
+ r.in.lm_cross = &hash6;
+
+ status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ printf("ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ changed = true;
+ *password = newpass;
+ }
+
+ r.in.user_handle = &user_handle;
+ r.in.lm_present = 1;
+ r.in.old_lm_crypted = &hash1;
+ r.in.new_lm_crypted = &hash2;
+ r.in.nt_present = 1;
+ r.in.old_nt_crypted = &hash3;
+ r.in.new_nt_crypted = &hash4;
+ r.in.cross1_present = 1;
+ r.in.nt_cross = &hash5;
+ r.in.cross2_present = 1;
+ r.in.lm_cross = &hash6;
+
+ if (changed) {
+ status = dcerpc_samr_ChangePasswordUser(p, mem_ctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ printf("ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+ } else if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we already changed the password, got %s\n", nt_errstr(status));
+ ret = false;
+ }
+ }
+
+
+ if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_OemChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *acct_name,
+ struct policy_handle *handle, char **password)
+{
+ NTSTATUS status;
+ struct samr_OemChangePasswordUser2 r;
+ bool ret = true;
+ struct samr_Password lm_verifier;
+ struct samr_CryptPassword lm_pass;
+ struct lsa_AsciiString server, account, account_bad;
+ char *oldpass;
+ char *newpass;
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+
+ struct samr_GetDomPwInfo dom_pw_info;
+ int policy_min_pw_len = 0;
+
+ struct lsa_String domain_name;
+
+ domain_name.string = "";
+ dom_pw_info.in.domain_name = &domain_name;
+
+ printf("Testing OemChangePasswordUser2\n");
+
+ if (!*password) {
+ printf("Failing OemChangePasswordUser2 as old password was NULL. Previous test failed?\n");
+ return false;
+ }
+
+ oldpass = *password;
+
+ status = dcerpc_samr_GetDomPwInfo(p, mem_ctx, &dom_pw_info);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = dom_pw_info.out.info.min_password_length;
+ }
+
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+
+ server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
+ account.string = acct_name;
+
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.password = &lm_pass;
+ r.in.hash = &lm_verifier;
+
+ /* Break the verification */
+ lm_verifier.hash[0]++;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ /* Break the old password */
+ old_lm_hash[0]++;
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ /* unbreak it for the next operation */
+ old_lm_hash[0]--;
+ E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.password = &lm_pass;
+ r.in.hash = &lm_verifier;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrpted password - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.password = &lm_pass;
+ r.in.hash = NULL;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)
+ && !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("ChangePasswordUser3 failed, should have returned INVALID_PARAMETER (or at least 'PASSWORD_RESTRICTON') for no supplied validation hash - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ /* This shouldn't be a valid name */
+ account_bad.string = TEST_ACCOUNT_NAME "XX";
+ r.in.account = &account_bad;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("ChangePasswordUser3 failed, should have returned INVALID_PARAMETER for no supplied validation hash and invalid user - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ /* This shouldn't be a valid name */
+ account_bad.string = TEST_ACCOUNT_NAME "XX";
+ r.in.account = &account_bad;
+ r.in.password = &lm_pass;
+ r.in.hash = &lm_verifier;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD for invalid user - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ /* This shouldn't be a valid name */
+ account_bad.string = TEST_ACCOUNT_NAME "XX";
+ r.in.account = &account_bad;
+ r.in.password = NULL;
+ r.in.hash = &lm_verifier;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
+ printf("ChangePasswordUser3 failed, should have returned INVALID_PARAMETER for no supplied password and invalid user - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ E_old_pw_hash(new_lm_hash, old_lm_hash, lm_verifier.hash);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.password = &lm_pass;
+ r.in.hash = &lm_verifier;
+
+ status = dcerpc_samr_OemChangePasswordUser2(p, mem_ctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ printf("OemChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("OemChangePasswordUser2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+
+static bool test_ChangePasswordUser2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *acct_name,
+ char **password,
+ char *newpass, bool allow_password_restriction)
+{
+ NTSTATUS status;
+ struct samr_ChangePasswordUser2 r;
+ bool ret = true;
+ struct lsa_String server, account;
+ struct samr_CryptPassword nt_pass, lm_pass;
+ struct samr_Password nt_verifier, lm_verifier;
+ char *oldpass;
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+
+ struct samr_GetDomPwInfo dom_pw_info;
+
+ struct lsa_String domain_name;
+
+ domain_name.string = "";
+ dom_pw_info.in.domain_name = &domain_name;
+
+ printf("Testing ChangePasswordUser2 on %s\n", acct_name);
+
+ if (!*password) {
+ printf("Failing ChangePasswordUser3 as old password was NULL. Previous test failed?\n");
+ return false;
+ }
+ oldpass = *password;
+
+ if (!newpass) {
+ int policy_min_pw_len = 0;
+ status = dcerpc_samr_GetDomPwInfo(p, mem_ctx, &dom_pw_info);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = dom_pw_info.out.info.min_password_length;
+ }
+
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+ }
+
+ server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
+ init_lsa_String(&account, acct_name);
+
+ E_md4hash(oldpass, old_nt_hash);
+ E_md4hash(newpass, new_nt_hash);
+
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII|STR_TERMINATE);
+ arcfour_crypt(lm_pass.data, old_lm_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.nt_password = &nt_pass;
+ r.in.nt_verifier = &nt_verifier;
+ r.in.lm_change = 1;
+ r.in.lm_password = &lm_pass;
+ r.in.lm_verifier = &lm_verifier;
+
+ status = dcerpc_samr_ChangePasswordUser2(p, mem_ctx, &r);
+ if (allow_password_restriction && NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ printf("ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(status));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+
+bool test_ChangePasswordUser3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *account_string,
+ int policy_min_pw_len,
+ char **password,
+ const char *newpass,
+ NTTIME last_password_change,
+ bool handle_reject_reason)
+{
+ NTSTATUS status;
+ struct samr_ChangePasswordUser3 r;
+ bool ret = true;
+ struct lsa_String server, account, account_bad;
+ struct samr_CryptPassword nt_pass, lm_pass;
+ struct samr_Password nt_verifier, lm_verifier;
+ char *oldpass;
+ uint8_t old_nt_hash[16], new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+ NTTIME t;
+
+ printf("Testing ChangePasswordUser3\n");
+
+ if (newpass == NULL) {
+ do {
+ if (policy_min_pw_len == 0) {
+ newpass = samr_rand_pass(mem_ctx, policy_min_pw_len);
+ } else {
+ newpass = samr_rand_pass_fixed_len(mem_ctx, policy_min_pw_len);
+ }
+ } while (check_password_quality(newpass) == false);
+ } else {
+ printf("Using password '%s'\n", newpass);
+ }
+
+ if (!*password) {
+ printf("Failing ChangePasswordUser3 as old password was NULL. Previous test failed?\n");
+ return false;
+ }
+
+ oldpass = *password;
+ server.string = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
+ init_lsa_String(&account, account_string);
+
+ E_md4hash(oldpass, old_nt_hash);
+ E_md4hash(newpass, new_nt_hash);
+
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(lm_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+
+ /* Break the verification */
+ nt_verifier.hash[0]++;
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.nt_password = &nt_pass;
+ r.in.nt_verifier = &nt_verifier;
+ r.in.lm_change = 1;
+ r.in.lm_password = &lm_pass;
+ r.in.lm_verifier = &lm_verifier;
+ r.in.password3 = NULL;
+
+ status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) &&
+ (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD))) {
+ printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(lm_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ /* Break the NT hash */
+ old_nt_hash[0]++;
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ /* Unbreak it again */
+ old_nt_hash[0]--;
+ E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.nt_password = &nt_pass;
+ r.in.nt_verifier = &nt_verifier;
+ r.in.lm_change = 1;
+ r.in.lm_password = &lm_pass;
+ r.in.lm_verifier = &lm_verifier;
+ r.in.password3 = NULL;
+
+ status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) &&
+ (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD))) {
+ printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrpted password - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ /* This shouldn't be a valid name */
+ init_lsa_String(&account_bad, talloc_asprintf(mem_ctx, "%sXX", account_string));
+
+ r.in.account = &account_bad;
+ status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("ChangePasswordUser3 failed, should have returned WRONG_PASSWORD for invalid username - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ E_md4hash(oldpass, old_nt_hash);
+ E_md4hash(newpass, new_nt_hash);
+
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(lm_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ encode_pw_buffer(nt_pass.data, newpass, STR_UNICODE);
+ arcfour_crypt(nt_pass.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_nt_hash, nt_verifier.hash);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.nt_password = &nt_pass;
+ r.in.nt_verifier = &nt_verifier;
+ r.in.lm_change = 1;
+ r.in.lm_password = &lm_pass;
+ r.in.lm_verifier = &lm_verifier;
+ r.in.password3 = NULL;
+
+ unix_to_nt_time(&t, time(NULL));
+
+ status = dcerpc_samr_ChangePasswordUser3(p, mem_ctx, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)
+ && r.out.dominfo
+ && r.out.reject
+ && handle_reject_reason
+ && (!null_nttime(last_password_change) || !r.out.dominfo->min_password_age)) {
+ if (r.out.dominfo->password_properties & DOMAIN_REFUSE_PASSWORD_CHANGE ) {
+
+ if (r.out.reject && (r.out.reject->reason != SAMR_REJECT_OTHER)) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, r.out.reject->reason);
+ return false;
+ }
+ }
+
+ /* We tested the order of precendence which is as follows:
+
+ * pwd min_age
+ * pwd length
+ * pwd complexity
+ * pwd history
+
+ Guenther */
+
+ if ((r.out.dominfo->min_password_age > 0) && !null_nttime(last_password_change) &&
+ (last_password_change + r.out.dominfo->min_password_age > t)) {
+
+ if (r.out.reject->reason != SAMR_REJECT_OTHER) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, r.out.reject->reason);
+ return false;
+ }
+
+ } else if ((r.out.dominfo->min_password_length > 0) &&
+ (strlen(newpass) < r.out.dominfo->min_password_length)) {
+
+ if (r.out.reject->reason != SAMR_REJECT_TOO_SHORT) {
+ printf("expected SAMR_REJECT_TOO_SHORT (%d), got %d\n",
+ SAMR_REJECT_TOO_SHORT, r.out.reject->reason);
+ return false;
+ }
+
+ } else if ((r.out.dominfo->password_history_length > 0) &&
+ strequal(oldpass, newpass)) {
+
+ if (r.out.reject->reason != SAMR_REJECT_IN_HISTORY) {
+ printf("expected SAMR_REJECT_IN_HISTORY (%d), got %d\n",
+ SAMR_REJECT_IN_HISTORY, r.out.reject->reason);
+ return false;
+ }
+ } else if (r.out.dominfo->password_properties & DOMAIN_PASSWORD_COMPLEX) {
+
+ if (r.out.reject->reason != SAMR_REJECT_COMPLEXITY) {
+ printf("expected SAMR_REJECT_COMPLEXITY (%d), got %d\n",
+ SAMR_REJECT_COMPLEXITY, r.out.reject->reason);
+ return false;
+ }
+
+ }
+
+ if (r.out.reject->reason == SAMR_REJECT_TOO_SHORT) {
+ /* retry with adjusted size */
+ return test_ChangePasswordUser3(p, mem_ctx, account_string,
+ r.out.dominfo->min_password_length,
+ password, NULL, 0, false);
+
+ }
+
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) {
+ if (r.out.reject && r.out.reject->reason != SAMR_REJECT_OTHER) {
+ printf("expected SAMR_REJECT_OTHER (%d), got %d\n",
+ SAMR_REJECT_OTHER, r.out.reject->reason);
+ return false;
+ }
+ /* Perhaps the server has a 'min password age' set? */
+
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("ChangePasswordUser3 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ *password = talloc_strdup(mem_ctx, newpass);
+ }
+
+ return ret;
+}
+
+
+static bool test_GetMembersInAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *alias_handle)
+{
+ struct samr_GetMembersInAlias r;
+ struct lsa_SidArray sids;
+ NTSTATUS status;
+ bool ret = true;
+
+ printf("Testing GetMembersInAlias\n");
+
+ r.in.alias_handle = alias_handle;
+ r.out.sids = &sids;
+
+ status = dcerpc_samr_GetMembersInAlias(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetMembersInAlias failed - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_AddMemberToAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *alias_handle,
+ const struct dom_sid *domain_sid)
+{
+ struct samr_AddAliasMember r;
+ struct samr_DeleteAliasMember d;
+ NTSTATUS status;
+ bool ret = true;
+ struct dom_sid *sid;
+
+ sid = dom_sid_add_rid(mem_ctx, domain_sid, 512);
+
+ printf("testing AddAliasMember\n");
+ r.in.alias_handle = alias_handle;
+ r.in.sid = sid;
+
+ status = dcerpc_samr_AddAliasMember(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddAliasMember failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ d.in.alias_handle = alias_handle;
+ d.in.sid = sid;
+
+ status = dcerpc_samr_DeleteAliasMember(p, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DelAliasMember failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_AddMultipleMembersToAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *alias_handle)
+{
+ struct samr_AddMultipleMembersToAlias a;
+ struct samr_RemoveMultipleMembersFromAlias r;
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_SidArray sids;
+
+ printf("testing AddMultipleMembersToAlias\n");
+ a.in.alias_handle = alias_handle;
+ a.in.sids = &sids;
+
+ sids.num_sids = 3;
+ sids.sids = talloc_array(mem_ctx, struct lsa_SidPtr, 3);
+
+ sids.sids[0].sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-1-2-3-1");
+ sids.sids[1].sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-1-2-3-2");
+ sids.sids[2].sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-1-2-3-3");
+
+ status = dcerpc_samr_AddMultipleMembersToAlias(p, mem_ctx, &a);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddMultipleMembersToAlias failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+
+ printf("testing RemoveMultipleMembersFromAlias\n");
+ r.in.alias_handle = alias_handle;
+ r.in.sids = &sids;
+
+ status = dcerpc_samr_RemoveMultipleMembersFromAlias(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("RemoveMultipleMembersFromAlias failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ /* strange! removing twice doesn't give any error */
+ status = dcerpc_samr_RemoveMultipleMembersFromAlias(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("RemoveMultipleMembersFromAlias failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ /* but removing an alias that isn't there does */
+ sids.sids[2].sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-1-2-3-4");
+
+ status = dcerpc_samr_RemoveMultipleMembersFromAlias(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
+ printf("RemoveMultipleMembersFromAlias failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_TestPrivateFunctionsUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *user_handle)
+{
+ struct samr_TestPrivateFunctionsUser r;
+ NTSTATUS status;
+ bool ret = true;
+
+ printf("Testing TestPrivateFunctionsUser\n");
+
+ r.in.user_handle = user_handle;
+
+ status = dcerpc_samr_TestPrivateFunctionsUser(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(NT_STATUS_NOT_IMPLEMENTED, status)) {
+ printf("TestPrivateFunctionsUser failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_user_ops(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *user_handle,
+ struct policy_handle *domain_handle,
+ uint32_t base_acct_flags,
+ const char *base_acct_name, enum torture_samr_choice which_ops)
+{
+ char *password = NULL;
+ struct samr_QueryUserInfo q;
+ NTSTATUS status;
+
+ bool ret = true;
+ int i;
+ uint32_t rid;
+ const uint32_t password_fields[] = {
+ SAMR_FIELD_PASSWORD,
+ SAMR_FIELD_PASSWORD2,
+ SAMR_FIELD_PASSWORD | SAMR_FIELD_PASSWORD2,
+ 0
+ };
+
+ status = test_LookupName(p, tctx, domain_handle, base_acct_name, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ }
+
+ switch (which_ops) {
+ case TORTURE_SAMR_USER_ATTRIBUTES:
+ if (!test_QuerySecurity(p, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo(p, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo2(p, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_SetUserInfo(p, tctx, user_handle, base_acct_flags,
+ base_acct_name)) {
+ ret = false;
+ }
+
+ if (!test_GetUserPwInfo(p, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_TestPrivateFunctionsUser(p, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_SetUserPass(p, tctx, user_handle, &password)) {
+ ret = false;
+ }
+ break;
+ case TORTURE_SAMR_PASSWORDS:
+ if (base_acct_flags & (ACB_WSTRUST|ACB_DOMTRUST|ACB_SVRTRUST)) {
+ char simple_pass[9];
+ char *v = generate_random_str(tctx, 1);
+
+ ZERO_STRUCT(simple_pass);
+ memset(simple_pass, *v, sizeof(simple_pass) - 1);
+
+ printf("Testing machine account password policy rules\n");
+
+ /* Workstation trust accounts don't seem to need to honour password quality policy */
+ if (!test_SetUserPassEx(p, tctx, user_handle, true, &password)) {
+ ret = false;
+ }
+
+ if (!test_ChangePasswordUser2(p, tctx, base_acct_name, &password, simple_pass, false)) {
+ ret = false;
+ }
+
+ /* reset again, to allow another 'user' password change */
+ if (!test_SetUserPassEx(p, tctx, user_handle, true, &password)) {
+ ret = false;
+ }
+
+ /* Try a 'short' password */
+ if (!test_ChangePasswordUser2(p, tctx, base_acct_name, &password, samr_rand_pass(tctx, 4), false)) {
+ ret = false;
+ }
+
+ }
+
+ for (i = 0; password_fields[i]; i++) {
+ if (!test_SetUserPass_23(p, tctx, user_handle, password_fields[i], &password)) {
+ ret = false;
+ }
+
+ /* check it was set right */
+ if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) {
+ ret = false;
+ }
+ }
+
+ for (i = 0; password_fields[i]; i++) {
+ if (!test_SetUserPass_25(p, tctx, user_handle, password_fields[i], &password)) {
+ ret = false;
+ }
+
+ /* check it was set right */
+ if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) {
+ ret = false;
+ }
+ }
+
+ if (!test_SetUserPassEx(p, tctx, user_handle, false, &password)) {
+ ret = false;
+ }
+
+ if (!test_ChangePassword(p, tctx, base_acct_name, domain_handle, &password)) {
+ ret = false;
+ }
+
+ q.in.user_handle = user_handle;
+ q.in.level = 5;
+
+ status = dcerpc_samr_QueryUserInfo(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ uint32_t expected_flags = (base_acct_flags | ACB_PWNOTREQ | ACB_DISABLED);
+ if ((q.out.info->info5.acct_flags) != expected_flags) {
+ printf("QuerUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n",
+ q.out.info->info5.acct_flags,
+ expected_flags);
+ ret = false;
+ }
+ if (q.out.info->info5.rid != rid) {
+ printf("QuerUserInfo level 5 failed, it returned %u when we expected rid of %u\n",
+ q.out.info->info5.rid, rid);
+
+ }
+ }
+
+ break;
+ case TORTURE_SAMR_OTHER:
+ /* We just need the account to exist */
+ break;
+ }
+ return ret;
+}
+
+static bool test_alias_ops(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *alias_handle,
+ const struct dom_sid *domain_sid)
+{
+ bool ret = true;
+
+ if (!test_QuerySecurity(p, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryAliasInfo(p, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_SetAliasInfo(p, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_AddMemberToAlias(p, tctx, alias_handle, domain_sid)) {
+ ret = false;
+ }
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping MultipleMembers Alias tests against Samba4\n");
+ return ret;
+ }
+
+ if (!test_AddMultipleMembersToAlias(p, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_DeleteUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *user_handle)
+{
+ struct samr_DeleteUser d;
+ NTSTATUS status;
+ bool ret = true;
+ printf("Testing DeleteUser\n");
+
+ d.in.user_handle = user_handle;
+ d.out.user_handle = user_handle;
+
+ status = dcerpc_samr_DeleteUser(p, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteUser failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool test_DeleteUser_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char *name)
+{
+ NTSTATUS status;
+ struct samr_DeleteUser d;
+ struct policy_handle user_handle;
+ uint32_t rid;
+
+ status = test_LookupName(p, mem_ctx, handle, name, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = test_OpenUser_byname(p, mem_ctx, handle, name, &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+ status = dcerpc_samr_DeleteUser(p, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ return true;
+
+failed:
+ printf("DeleteUser_byname(%s) failed - %s\n", name, nt_errstr(status));
+ return false;
+}
+
+
+static bool test_DeleteGroup_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char *name)
+{
+ NTSTATUS status;
+ struct samr_OpenGroup r;
+ struct samr_DeleteDomainGroup d;
+ struct policy_handle group_handle;
+ uint32_t rid;
+
+ status = test_LookupName(p, mem_ctx, handle, name, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ r.in.domain_handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.group_handle = &group_handle;
+ status = dcerpc_samr_OpenGroup(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ d.in.group_handle = &group_handle;
+ d.out.group_handle = &group_handle;
+ status = dcerpc_samr_DeleteDomainGroup(p, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ return true;
+
+failed:
+ printf("DeleteGroup_byname(%s) failed - %s\n", name, nt_errstr(status));
+ return false;
+}
+
+
+static bool test_DeleteAlias_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle, const char *name)
+{
+ NTSTATUS status;
+ struct samr_OpenAlias r;
+ struct samr_DeleteDomAlias d;
+ struct policy_handle alias_handle;
+ uint32_t rid;
+
+ printf("testing DeleteAlias_byname\n");
+
+ status = test_LookupName(p, mem_ctx, domain_handle, name, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ r.in.domain_handle = domain_handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.alias_handle = &alias_handle;
+ status = dcerpc_samr_OpenAlias(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ d.in.alias_handle = &alias_handle;
+ d.out.alias_handle = &alias_handle;
+ status = dcerpc_samr_DeleteDomAlias(p, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ return true;
+
+failed:
+ printf("DeleteAlias_byname(%s) failed - %s\n", name, nt_errstr(status));
+ return false;
+}
+
+static bool test_DeleteAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *alias_handle)
+{
+ struct samr_DeleteDomAlias d;
+ NTSTATUS status;
+ bool ret = true;
+ printf("Testing DeleteAlias\n");
+
+ d.in.alias_handle = alias_handle;
+ d.out.alias_handle = alias_handle;
+
+ status = dcerpc_samr_DeleteDomAlias(p, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteAlias failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_CreateAlias(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ struct policy_handle *alias_handle,
+ const struct dom_sid *domain_sid)
+{
+ NTSTATUS status;
+ struct samr_CreateDomAlias r;
+ struct lsa_String name;
+ uint32_t rid;
+ bool ret = true;
+
+ init_lsa_String(&name, TEST_ALIASNAME);
+ r.in.domain_handle = domain_handle;
+ r.in.alias_name = &name;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.alias_handle = alias_handle;
+ r.out.rid = &rid;
+
+ printf("Testing CreateAlias (%s)\n", r.in.alias_name->string);
+
+ status = dcerpc_samr_CreateDomAlias(p, tctx, &r);
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Server correctly refused create of '%s'\n", r.in.alias_name->string);
+ return true;
+ } else {
+ printf("Server should have refused create of '%s', got %s instead\n", r.in.alias_name->string,
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ALIAS_EXISTS)) {
+ if (!test_DeleteAlias_byname(p, tctx, domain_handle, r.in.alias_name->string)) {
+ return false;
+ }
+ status = dcerpc_samr_CreateDomAlias(p, tctx, &r);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateAlias failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_alias_ops(p, tctx, alias_handle, domain_sid)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *acct_name,
+ struct policy_handle *domain_handle, char **password)
+{
+ bool ret = true;
+
+ if (!*password) {
+ return false;
+ }
+
+ if (!test_ChangePasswordUser(p, mem_ctx, acct_name, domain_handle, password)) {
+ ret = false;
+ }
+
+ if (!test_ChangePasswordUser2(p, mem_ctx, acct_name, password, 0, true)) {
+ ret = false;
+ }
+
+ if (!test_OemChangePasswordUser2(p, mem_ctx, acct_name, domain_handle, password)) {
+ ret = false;
+ }
+
+ /* test what happens when setting the old password again */
+ if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, *password, 0, true)) {
+ ret = false;
+ }
+
+ {
+ char simple_pass[9];
+ char *v = generate_random_str(mem_ctx, 1);
+
+ ZERO_STRUCT(simple_pass);
+ memset(simple_pass, *v, sizeof(simple_pass) - 1);
+
+ /* test what happens when picking a simple password */
+ if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, simple_pass, 0, true)) {
+ ret = false;
+ }
+ }
+
+ /* set samr_SetDomainInfo level 1 with min_length 5 */
+ {
+ struct samr_QueryDomainInfo r;
+ struct samr_SetDomainInfo s;
+ uint16_t len_old, len;
+ uint32_t pwd_prop_old;
+ int64_t min_pwd_age_old;
+ NTSTATUS status;
+
+ len = 5;
+
+ r.in.domain_handle = domain_handle;
+ r.in.level = 1;
+
+ printf("testing samr_QueryDomainInfo level 1\n");
+ status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ s.in.domain_handle = domain_handle;
+ s.in.level = 1;
+ s.in.info = r.out.info;
+
+ /* remember the old min length, so we can reset it */
+ len_old = s.in.info->info1.min_password_length;
+ s.in.info->info1.min_password_length = len;
+ pwd_prop_old = s.in.info->info1.password_properties;
+ /* turn off password complexity checks for this test */
+ s.in.info->info1.password_properties &= ~DOMAIN_PASSWORD_COMPLEX;
+
+ min_pwd_age_old = s.in.info->info1.min_password_age;
+ s.in.info->info1.min_password_age = 0;
+
+ printf("testing samr_SetDomainInfo level 1\n");
+ status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ printf("calling test_ChangePasswordUser3 with too short password\n");
+
+ if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, len - 1, password, NULL, 0, true)) {
+ ret = false;
+ }
+
+ s.in.info->info1.min_password_length = len_old;
+ s.in.info->info1.password_properties = pwd_prop_old;
+ s.in.info->info1.min_password_age = min_pwd_age_old;
+
+ printf("testing samr_SetDomainInfo level 1\n");
+ status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ }
+
+ {
+ NTSTATUS status;
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ struct samr_LookupNames n;
+ struct policy_handle user_handle;
+
+ n.in.domain_handle = domain_handle;
+ n.in.num_names = 1;
+ n.in.names = talloc_array(mem_ctx, struct lsa_String, 1);
+ n.in.names[0].string = acct_name;
+
+ status = dcerpc_samr_LookupNames(p, mem_ctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r.in.domain_handle = domain_handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = n.out.rids.ids[0];
+ r.out.user_handle = &user_handle;
+
+ status = dcerpc_samr_OpenUser(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%u) failed - %s\n", n.out.rids.ids[0], nt_errstr(status));
+ return false;
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 5;
+
+ status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("calling test_ChangePasswordUser3 with too early password change\n");
+
+ if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL,
+ q.out.info->info5.last_password_change, true)) {
+ ret = false;
+ }
+ }
+
+ /* we change passwords twice - this has the effect of verifying
+ they were changed correctly for the final call */
+ if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL, 0, true)) {
+ ret = false;
+ }
+
+ if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL, 0, true)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ struct policy_handle *user_handle_out,
+ struct dom_sid *domain_sid,
+ enum torture_samr_choice which_ops)
+{
+
+ TALLOC_CTX *user_ctx;
+
+ NTSTATUS status;
+ struct samr_CreateUser r;
+ struct samr_QueryUserInfo q;
+ struct samr_DeleteUser d;
+ uint32_t rid;
+
+ /* This call creates a 'normal' account - check that it really does */
+ const uint32_t acct_flags = ACB_NORMAL;
+ struct lsa_String name;
+ bool ret = true;
+
+ struct policy_handle user_handle;
+ user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context");
+ init_lsa_String(&name, TEST_ACCOUNT_NAME);
+
+ r.in.domain_handle = domain_handle;
+ r.in.account_name = &name;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.user_handle = &user_handle;
+ r.out.rid = &rid;
+
+ printf("Testing CreateUser(%s)\n", r.in.account_name->string);
+
+ status = dcerpc_samr_CreateUser(p, user_ctx, &r);
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Server correctly refused create of '%s'\n", r.in.account_name->string);
+ return true;
+ } else {
+ printf("Server should have refused create of '%s', got %s instead\n", r.in.account_name->string,
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ if (!test_DeleteUser_byname(p, user_ctx, domain_handle, r.in.account_name->string)) {
+ talloc_free(user_ctx);
+ return false;
+ }
+ status = dcerpc_samr_CreateUser(p, user_ctx, &r);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(user_ctx);
+ printf("CreateUser failed - %s\n", nt_errstr(status));
+ return false;
+ } else {
+ q.in.user_handle = &user_handle;
+ q.in.level = 16;
+
+ status = dcerpc_samr_QueryUserInfo(p, user_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ if ((q.out.info->info16.acct_flags & acct_flags) != acct_flags) {
+ printf("QuerUserInfo level 16 failed, it returned 0x%08x when we expected flags of 0x%08x\n",
+ q.out.info->info16.acct_flags,
+ acct_flags);
+ ret = false;
+ }
+ }
+
+ if (!test_user_ops(p, tctx, &user_handle, domain_handle,
+ acct_flags, name.string, which_ops)) {
+ ret = false;
+ }
+
+ if (user_handle_out) {
+ *user_handle_out = user_handle;
+ } else {
+ printf("Testing DeleteUser (createuser test)\n");
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+
+ status = dcerpc_samr_DeleteUser(p, user_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteUser failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ }
+
+ talloc_free(user_ctx);
+
+ return ret;
+}
+
+
+static bool test_CreateUser2(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ struct dom_sid *domain_sid,
+ enum torture_samr_choice which_ops)
+{
+ NTSTATUS status;
+ struct samr_CreateUser2 r;
+ struct samr_QueryUserInfo q;
+ struct samr_DeleteUser d;
+ struct policy_handle user_handle;
+ uint32_t rid;
+ struct lsa_String name;
+ bool ret = true;
+ int i;
+
+ struct {
+ uint32_t acct_flags;
+ const char *account_name;
+ NTSTATUS nt_status;
+ } account_types[] = {
+ { ACB_NORMAL, TEST_ACCOUNT_NAME, NT_STATUS_OK },
+ { ACB_NORMAL | ACB_DISABLED, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER },
+ { ACB_NORMAL | ACB_PWNOEXP, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER },
+ { ACB_WSTRUST, TEST_MACHINENAME, NT_STATUS_OK },
+ { ACB_WSTRUST | ACB_DISABLED, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER },
+ { ACB_WSTRUST | ACB_PWNOEXP, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER },
+ { ACB_SVRTRUST, TEST_MACHINENAME, NT_STATUS_OK },
+ { ACB_SVRTRUST | ACB_DISABLED, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER },
+ { ACB_SVRTRUST | ACB_PWNOEXP, TEST_MACHINENAME, NT_STATUS_INVALID_PARAMETER },
+ { ACB_DOMTRUST, TEST_DOMAINNAME, NT_STATUS_OK },
+ { ACB_DOMTRUST | ACB_DISABLED, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER },
+ { ACB_DOMTRUST | ACB_PWNOEXP, TEST_DOMAINNAME, NT_STATUS_INVALID_PARAMETER },
+ { 0, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER },
+ { ACB_DISABLED, TEST_ACCOUNT_NAME, NT_STATUS_INVALID_PARAMETER },
+ { 0, NULL, NT_STATUS_INVALID_PARAMETER }
+ };
+
+ for (i = 0; account_types[i].account_name; i++) {
+ TALLOC_CTX *user_ctx;
+ uint32_t acct_flags = account_types[i].acct_flags;
+ uint32_t access_granted;
+ user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context");
+ init_lsa_String(&name, account_types[i].account_name);
+
+ r.in.domain_handle = domain_handle;
+ r.in.account_name = &name;
+ r.in.acct_flags = acct_flags;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.user_handle = &user_handle;
+ r.out.access_granted = &access_granted;
+ r.out.rid = &rid;
+
+ printf("Testing CreateUser2(%s, 0x%x)\n", r.in.account_name->string, acct_flags);
+
+ status = dcerpc_samr_CreateUser2(p, user_ctx, &r);
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Server correctly refused create of '%s'\n", r.in.account_name->string);
+ continue;
+ } else {
+ printf("Server should have refused create of '%s', got %s instead\n", r.in.account_name->string,
+ nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ if (!test_DeleteUser_byname(p, user_ctx, domain_handle, r.in.account_name->string)) {
+ talloc_free(user_ctx);
+ ret = false;
+ continue;
+ }
+ status = dcerpc_samr_CreateUser2(p, user_ctx, &r);
+
+ }
+ if (!NT_STATUS_EQUAL(status, account_types[i].nt_status)) {
+ printf("CreateUser2 failed gave incorrect error return - %s (should be %s)\n",
+ nt_errstr(status), nt_errstr(account_types[i].nt_status));
+ ret = false;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ q.in.user_handle = &user_handle;
+ q.in.level = 5;
+
+ status = dcerpc_samr_QueryUserInfo(p, user_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(status));
+ ret = false;
+ } else {
+ uint32_t expected_flags = (acct_flags | ACB_PWNOTREQ | ACB_DISABLED);
+ if (acct_flags == ACB_NORMAL) {
+ expected_flags |= ACB_PW_EXPIRED;
+ }
+ if ((q.out.info->info5.acct_flags) != expected_flags) {
+ printf("QuerUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n",
+ q.out.info->info5.acct_flags,
+ expected_flags);
+ ret = false;
+ }
+ switch (acct_flags) {
+ case ACB_SVRTRUST:
+ if (q.out.info->info5.primary_gid != DOMAIN_RID_DCS) {
+ printf("QuerUserInfo level 5: DC should have had Primary Group %d, got %d\n",
+ DOMAIN_RID_DCS, q.out.info->info5.primary_gid);
+ ret = false;
+ }
+ break;
+ case ACB_WSTRUST:
+ if (q.out.info->info5.primary_gid != DOMAIN_RID_DOMAIN_MEMBERS) {
+ printf("QuerUserInfo level 5: Domain Member should have had Primary Group %d, got %d\n",
+ DOMAIN_RID_DOMAIN_MEMBERS, q.out.info->info5.primary_gid);
+ ret = false;
+ }
+ break;
+ case ACB_NORMAL:
+ if (q.out.info->info5.primary_gid != DOMAIN_RID_USERS) {
+ printf("QuerUserInfo level 5: Users should have had Primary Group %d, got %d\n",
+ DOMAIN_RID_USERS, q.out.info->info5.primary_gid);
+ ret = false;
+ }
+ break;
+ }
+ }
+
+ if (!test_user_ops(p, tctx, &user_handle, domain_handle,
+ acct_flags, name.string, which_ops)) {
+ ret = false;
+ }
+
+ printf("Testing DeleteUser (createuser2 test)\n");
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+
+ status = dcerpc_samr_DeleteUser(p, user_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteUser failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ }
+ talloc_free(user_ctx);
+ }
+
+ return ret;
+}
+
+static bool test_QueryAliasInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryAliasInfo r;
+ uint16_t levels[] = {1, 2, 3};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryAliasInfo level %u\n", levels[i]);
+
+ r.in.alias_handle = handle;
+ r.in.level = levels[i];
+
+ status = dcerpc_samr_QueryAliasInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryAliasInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryGroupInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryGroupInfo r;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryGroupInfo level %u\n", levels[i]);
+
+ r.in.group_handle = handle;
+ r.in.level = levels[i];
+
+ status = dcerpc_samr_QueryGroupInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryGroupInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryGroupMember(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryGroupMember r;
+ bool ret = true;
+
+ printf("Testing QueryGroupMember\n");
+
+ r.in.group_handle = handle;
+
+ status = dcerpc_samr_QueryGroupMember(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryGroupInfo failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_SetGroupInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryGroupInfo r;
+ struct samr_SetGroupInfo s;
+ uint16_t levels[] = {1, 2, 3, 4};
+ uint16_t set_ok[] = {0, 1, 1, 1};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryGroupInfo level %u\n", levels[i]);
+
+ r.in.group_handle = handle;
+ r.in.level = levels[i];
+
+ status = dcerpc_samr_QueryGroupInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryGroupInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+
+ printf("Testing SetGroupInfo level %u\n", levels[i]);
+
+ s.in.group_handle = handle;
+ s.in.level = levels[i];
+ s.in.info = r.out.info;
+
+#if 0
+ /* disabled this, as it changes the name only from the point of view of samr,
+ but leaves the name from the point of view of w2k3 internals (and ldap). This means
+ the name is still reserved, so creating the old name fails, but deleting by the old name
+ also fails */
+ if (s.in.level == 2) {
+ init_lsa_String(&s.in.info->string, "NewName");
+ }
+#endif
+
+ if (s.in.level == 4) {
+ init_lsa_String(&s.in.info->description, "test description");
+ }
+
+ status = dcerpc_samr_SetGroupInfo(p, mem_ctx, &s);
+ if (set_ok[i]) {
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetGroupInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ } else {
+ if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status)) {
+ printf("SetGroupInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryUserInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryUserInfo r;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 16, 17, 20, 21};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryUserInfo level %u\n", levels[i]);
+
+ r.in.user_handle = handle;
+ r.in.level = levels[i];
+
+ status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryUserInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryUserInfo2 r;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 16, 17, 20, 21};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryUserInfo2 level %u\n", levels[i]);
+
+ r.in.user_handle = handle;
+ r.in.level = levels[i];
+
+ status = dcerpc_samr_QueryUserInfo2(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo2 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_OpenUser(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, uint32_t rid)
+{
+ NTSTATUS status;
+ struct samr_OpenUser r;
+ struct policy_handle user_handle;
+ bool ret = true;
+
+ printf("Testing OpenUser(%u)\n", rid);
+
+ r.in.domain_handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.user_handle = &user_handle;
+
+ status = dcerpc_samr_OpenUser(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(status));
+ return false;
+ }
+
+ if (!test_QuerySecurity(p, mem_ctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo(p, mem_ctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo2(p, mem_ctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_GetUserPwInfo(p, mem_ctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_GetGroupsForUser(p,mem_ctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_OpenGroup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, uint32_t rid)
+{
+ NTSTATUS status;
+ struct samr_OpenGroup r;
+ struct policy_handle group_handle;
+ bool ret = true;
+
+ printf("Testing OpenGroup(%u)\n", rid);
+
+ r.in.domain_handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.group_handle = &group_handle;
+
+ status = dcerpc_samr_OpenGroup(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenGroup(%u) failed - %s\n", rid, nt_errstr(status));
+ return false;
+ }
+
+ if (!test_QuerySecurity(p, mem_ctx, &group_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryGroupInfo(p, mem_ctx, &group_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryGroupMember(p, mem_ctx, &group_handle)) {
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(p, mem_ctx, &group_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_OpenAlias(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, uint32_t rid)
+{
+ NTSTATUS status;
+ struct samr_OpenAlias r;
+ struct policy_handle alias_handle;
+ bool ret = true;
+
+ printf("Testing OpenAlias(%u)\n", rid);
+
+ r.in.domain_handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.alias_handle = &alias_handle;
+
+ status = dcerpc_samr_OpenAlias(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenAlias(%u) failed - %s\n", rid, nt_errstr(status));
+ return false;
+ }
+
+ if (!test_QuerySecurity(p, mem_ctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryAliasInfo(p, mem_ctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_GetMembersInAlias(p, mem_ctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(p, mem_ctx, &alias_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool check_mask(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, uint32_t rid,
+ uint32_t acct_flag_mask)
+{
+ NTSTATUS status;
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ struct policy_handle user_handle;
+ bool ret = true;
+
+ printf("Testing OpenUser(%u)\n", rid);
+
+ r.in.domain_handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.user_handle = &user_handle;
+
+ status = dcerpc_samr_OpenUser(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(status));
+ return false;
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 16;
+
+ status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo level 16 failed - %s\n",
+ nt_errstr(status));
+ ret = false;
+ } else {
+ if ((acct_flag_mask & q.out.info->info16.acct_flags) == 0) {
+ printf("Server failed to filter for 0x%x, allowed 0x%x (%d) on EnumDomainUsers\n",
+ acct_flag_mask, q.out.info->info16.acct_flags, rid);
+ ret = false;
+ }
+ }
+
+ if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumDomainUsers(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status = STATUS_MORE_ENTRIES;
+ struct samr_EnumDomainUsers r;
+ uint32_t mask, resume_handle=0;
+ int i, mask_idx;
+ bool ret = true;
+ struct samr_LookupNames n;
+ struct samr_LookupRids lr ;
+ uint32_t masks[] = {ACB_NORMAL, ACB_DOMTRUST, ACB_WSTRUST,
+ ACB_DISABLED, ACB_NORMAL | ACB_DISABLED,
+ ACB_SVRTRUST | ACB_DOMTRUST | ACB_WSTRUST,
+ ACB_PWNOEXP, 0};
+
+ printf("Testing EnumDomainUsers\n");
+
+ for (mask_idx=0;mask_idx<ARRAY_SIZE(masks);mask_idx++) {
+ r.in.domain_handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.acct_flags = mask = masks[mask_idx];
+ r.in.max_size = (uint32_t)-1;
+ r.out.resume_handle = &resume_handle;
+
+ status = dcerpc_samr_EnumDomainUsers(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_IS_OK(status)) {
+ printf("EnumDomainUsers failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!r.out.sam) {
+ printf("EnumDomainUsers failed: r.out.sam unexpectedly NULL\n");
+ return false;
+ }
+
+ if (r.out.sam->count == 0) {
+ continue;
+ }
+
+ for (i=0;i<r.out.sam->count;i++) {
+ if (mask) {
+ if (!check_mask(p, mem_ctx, handle, r.out.sam->entries[i].idx, mask)) {
+ ret = false;
+ }
+ } else if (!test_OpenUser(p, mem_ctx, handle, r.out.sam->entries[i].idx)) {
+ ret = false;
+ }
+ }
+ }
+
+ printf("Testing LookupNames\n");
+ n.in.domain_handle = handle;
+ n.in.num_names = r.out.sam->count;
+ n.in.names = talloc_array(mem_ctx, struct lsa_String, r.out.sam->count);
+ for (i=0;i<r.out.sam->count;i++) {
+ n.in.names[i].string = r.out.sam->entries[i].name.string;
+ }
+ status = dcerpc_samr_LookupNames(p, mem_ctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupNames failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+
+ printf("Testing LookupRids\n");
+ lr.in.domain_handle = handle;
+ lr.in.num_rids = r.out.sam->count;
+ lr.in.rids = talloc_array(mem_ctx, uint32_t, r.out.sam->count);
+ for (i=0;i<r.out.sam->count;i++) {
+ lr.in.rids[i] = r.out.sam->entries[i].idx;
+ }
+ status = dcerpc_samr_LookupRids(p, mem_ctx, &lr);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupRids failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+/*
+ try blasting the server with a bunch of sync requests
+*/
+static bool test_EnumDomainUsers_async(struct dcerpc_pipe *p, TALLOC_CTX *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_EnumDomainUsers r;
+ uint32_t resume_handle=0;
+ int i;
+#define ASYNC_COUNT 100
+ struct rpc_request *req[ASYNC_COUNT];
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ printf("samr async test disabled - enable dangerous tests to use\n");
+ return true;
+ }
+
+ printf("Testing EnumDomainUsers_async\n");
+
+ r.in.domain_handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.acct_flags = 0;
+ r.in.max_size = (uint32_t)-1;
+ r.out.resume_handle = &resume_handle;
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ req[i] = dcerpc_samr_EnumDomainUsers_send(p, tctx, &r);
+ }
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ status = dcerpc_ndr_request_recv(req[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomainUsers[%d] failed - %s\n",
+ i, nt_errstr(status));
+ return false;
+ }
+ }
+
+ printf("%d async requests OK\n", i);
+
+ return true;
+}
+
+static bool test_EnumDomainGroups(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_EnumDomainGroups r;
+ uint32_t resume_handle=0;
+ int i;
+ bool ret = true;
+
+ printf("Testing EnumDomainGroups\n");
+
+ r.in.domain_handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = (uint32_t)-1;
+ r.out.resume_handle = &resume_handle;
+
+ status = dcerpc_samr_EnumDomainGroups(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomainGroups failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!r.out.sam) {
+ return false;
+ }
+
+ for (i=0;i<r.out.sam->count;i++) {
+ if (!test_OpenGroup(p, mem_ctx, handle, r.out.sam->entries[i].idx)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_EnumDomainAliases(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_EnumDomainAliases r;
+ uint32_t resume_handle=0;
+ int i;
+ bool ret = true;
+
+ printf("Testing EnumDomainAliases\n");
+
+ r.in.domain_handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.acct_flags = (uint32_t)-1;
+ r.out.resume_handle = &resume_handle;
+
+ status = dcerpc_samr_EnumDomainAliases(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomainAliases failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!r.out.sam) {
+ return false;
+ }
+
+ for (i=0;i<r.out.sam->count;i++) {
+ if (!test_OpenAlias(p, mem_ctx, handle, r.out.sam->entries[i].idx)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetDisplayEnumerationIndex(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_GetDisplayEnumerationIndex r;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ uint16_t ok_lvl[] = {1, 1, 1, 0, 0};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing GetDisplayEnumerationIndex level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ init_lsa_String(&r.in.name, TEST_ACCOUNT_NAME);
+
+ status = dcerpc_samr_GetDisplayEnumerationIndex(p, mem_ctx, &r);
+
+ if (ok_lvl[i] &&
+ !NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) {
+ printf("GetDisplayEnumerationIndex level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+
+ init_lsa_String(&r.in.name, "zzzzzzzz");
+
+ status = dcerpc_samr_GetDisplayEnumerationIndex(p, mem_ctx, &r);
+
+ if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) {
+ printf("GetDisplayEnumerationIndex level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetDisplayEnumerationIndex2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_GetDisplayEnumerationIndex2 r;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ uint16_t ok_lvl[] = {1, 1, 1, 0, 0};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing GetDisplayEnumerationIndex2 level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ init_lsa_String(&r.in.name, TEST_ACCOUNT_NAME);
+
+ status = dcerpc_samr_GetDisplayEnumerationIndex2(p, mem_ctx, &r);
+ if (ok_lvl[i] &&
+ !NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) {
+ printf("GetDisplayEnumerationIndex2 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+
+ init_lsa_String(&r.in.name, "zzzzzzzz");
+
+ status = dcerpc_samr_GetDisplayEnumerationIndex2(p, mem_ctx, &r);
+ if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) {
+ printf("GetDisplayEnumerationIndex2 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+#define STRING_EQUAL_QUERY(s1, s2, user) \
+ if (s1.string == NULL && s2.string != NULL && s2.string[0] == '\0') { \
+ /* odd, but valid */ \
+ } else if ((s1.string && !s2.string) || (s2.string && !s1.string) || strcmp(s1.string, s2.string)) { \
+ printf("%s mismatch for %s: %s != %s (%s)\n", \
+ #s1, user.string, s1.string, s2.string, __location__); \
+ ret = false; \
+ }
+#define INT_EQUAL_QUERY(s1, s2, user) \
+ if (s1 != s2) { \
+ printf("%s mismatch for %s: 0x%llx != 0x%llx (%s)\n", \
+ #s1, user.string, (unsigned long long)s1, (unsigned long long)s2, __location__); \
+ ret = false; \
+ }
+
+static bool test_each_DisplayInfo_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct samr_QueryDisplayInfo *querydisplayinfo,
+ bool *seen_testuser)
+{
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ struct policy_handle user_handle;
+ int i, ret = true;
+ NTSTATUS status;
+ r.in.domain_handle = querydisplayinfo->in.domain_handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ for (i = 0; ; i++) {
+ switch (querydisplayinfo->in.level) {
+ case 1:
+ if (i >= querydisplayinfo->out.info.info1.count) {
+ return ret;
+ }
+ r.in.rid = querydisplayinfo->out.info.info1.entries[i].rid;
+ break;
+ case 2:
+ if (i >= querydisplayinfo->out.info.info2.count) {
+ return ret;
+ }
+ r.in.rid = querydisplayinfo->out.info.info2.entries[i].rid;
+ break;
+ case 3:
+ /* Groups */
+ case 4:
+ case 5:
+ /* Not interested in validating just the account name */
+ return true;
+ }
+
+ r.out.user_handle = &user_handle;
+
+ switch (querydisplayinfo->in.level) {
+ case 1:
+ case 2:
+ status = dcerpc_samr_OpenUser(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%u) failed - %s\n", r.in.rid, nt_errstr(status));
+ return false;
+ }
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 21;
+ status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryUserInfo(%u) failed - %s\n", r.in.rid, nt_errstr(status));
+ return false;
+ }
+
+ switch (querydisplayinfo->in.level) {
+ case 1:
+ if (seen_testuser && strcmp(q.out.info->info21.account_name.string, TEST_ACCOUNT_NAME) == 0) {
+ *seen_testuser = true;
+ }
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].full_name,
+ q.out.info->info21.full_name, q.out.info->info21.account_name);
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].account_name,
+ q.out.info->info21.account_name, q.out.info->info21.account_name);
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].description,
+ q.out.info->info21.description, q.out.info->info21.account_name);
+ INT_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].rid,
+ q.out.info->info21.rid, q.out.info->info21.account_name);
+ INT_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].acct_flags,
+ q.out.info->info21.acct_flags, q.out.info->info21.account_name);
+
+ break;
+ case 2:
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info.info2.entries[i].account_name,
+ q.out.info->info21.account_name, q.out.info->info21.account_name);
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info.info2.entries[i].description,
+ q.out.info->info21.description, q.out.info->info21.account_name);
+ INT_EQUAL_QUERY(querydisplayinfo->out.info.info2.entries[i].rid,
+ q.out.info->info21.rid, q.out.info->info21.account_name);
+ INT_EQUAL_QUERY((querydisplayinfo->out.info.info2.entries[i].acct_flags & ~ACB_NORMAL),
+ q.out.info->info21.acct_flags, q.out.info->info21.account_name);
+
+ if (!(querydisplayinfo->out.info.info2.entries[i].acct_flags & ACB_NORMAL)) {
+ printf("Missing ACB_NORMAL in querydisplayinfo->out.info.info2.entries[i].acct_flags on %s\n",
+ q.out.info->info21.account_name.string);
+ }
+
+ if (!(q.out.info->info21.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST))) {
+ printf("Found non-trust account %s in trust account listing: 0x%x 0x%x\n",
+ q.out.info->info21.account_name.string,
+ querydisplayinfo->out.info.info2.entries[i].acct_flags,
+ q.out.info->info21.acct_flags);
+ return false;
+ }
+
+ break;
+ }
+
+ if (!test_samr_handle_Close(p, mem_ctx, &user_handle)) {
+ return false;
+ }
+ }
+ return ret;
+}
+
+static bool test_QueryDisplayInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryDisplayInfo r;
+ struct samr_QueryDomainInfo dom_info;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ int i;
+ bool seen_testuser = false;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryDisplayInfo level %u\n", levels[i]);
+
+ r.in.start_idx = 0;
+ status = STATUS_MORE_ENTRIES;
+ while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.max_entries = 2;
+ r.in.buf_size = (uint32_t)-1;
+
+ status = dcerpc_samr_QueryDisplayInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) && !NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ switch (r.in.level) {
+ case 1:
+ if (!test_each_DisplayInfo_user(p, mem_ctx, &r, &seen_testuser)) {
+ ret = false;
+ }
+ r.in.start_idx += r.out.info.info1.count;
+ break;
+ case 2:
+ if (!test_each_DisplayInfo_user(p, mem_ctx, &r, NULL)) {
+ ret = false;
+ }
+ r.in.start_idx += r.out.info.info2.count;
+ break;
+ case 3:
+ r.in.start_idx += r.out.info.info3.count;
+ break;
+ case 4:
+ r.in.start_idx += r.out.info.info4.count;
+ break;
+ case 5:
+ r.in.start_idx += r.out.info.info5.count;
+ break;
+ }
+ }
+ dom_info.in.domain_handle = handle;
+ dom_info.in.level = 2;
+ /* Check number of users returned is correct */
+ status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &dom_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ break;
+ }
+ switch (r.in.level) {
+ case 1:
+ case 4:
+ if (dom_info.out.info->general.num_users < r.in.start_idx) {
+ printf("QueryDomainInfo indicates that QueryDisplayInfo returned more users (%d/%d) than the domain %s is said to contain!\n",
+ r.in.start_idx, dom_info.out.info->general.num_groups,
+ dom_info.out.info->general.domain_name.string);
+ ret = false;
+ }
+ if (!seen_testuser) {
+ struct policy_handle user_handle;
+ if (NT_STATUS_IS_OK(test_OpenUser_byname(p, mem_ctx, handle, TEST_ACCOUNT_NAME, &user_handle))) {
+ printf("Didn't find test user " TEST_ACCOUNT_NAME " in enumeration of %s\n",
+ dom_info.out.info->general.domain_name.string);
+ ret = false;
+ test_samr_handle_Close(p, mem_ctx, &user_handle);
+ }
+ }
+ break;
+ case 3:
+ case 5:
+ if (dom_info.out.info->general.num_groups != r.in.start_idx) {
+ printf("QueryDomainInfo indicates that QueryDisplayInfo didn't return all (%d/%d) the groups in %s\n",
+ r.in.start_idx, dom_info.out.info->general.num_groups,
+ dom_info.out.info->general.domain_name.string);
+ ret = false;
+ }
+
+ break;
+ }
+
+ }
+
+ return ret;
+}
+
+static bool test_QueryDisplayInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryDisplayInfo2 r;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryDisplayInfo2 level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.start_idx = 0;
+ r.in.max_entries = 1000;
+ r.in.buf_size = (uint32_t)-1;
+
+ status = dcerpc_samr_QueryDisplayInfo2(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo2 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryDisplayInfo3(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryDisplayInfo3 r;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryDisplayInfo3 level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.start_idx = 0;
+ r.in.max_entries = 1000;
+ r.in.buf_size = (uint32_t)-1;
+
+ status = dcerpc_samr_QueryDisplayInfo3(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo3 level %u failed - %s\n",
+ levels[i], nt_errstr(status));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_QueryDisplayInfo_continue(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryDisplayInfo r;
+ bool ret = true;
+
+ printf("Testing QueryDisplayInfo continuation\n");
+
+ r.in.domain_handle = handle;
+ r.in.level = 1;
+ r.in.start_idx = 0;
+ r.in.max_entries = 1;
+ r.in.buf_size = (uint32_t)-1;
+
+ do {
+ status = dcerpc_samr_QueryDisplayInfo(p, mem_ctx, &r);
+ if (NT_STATUS_IS_OK(status) && r.out.returned_size != 0) {
+ if (r.out.info.info1.entries[0].idx != r.in.start_idx + 1) {
+ printf("expected idx %d but got %d\n",
+ r.in.start_idx + 1,
+ r.out.info.info1.entries[0].idx);
+ break;
+ }
+ }
+ if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ break;
+ }
+ r.in.start_idx++;
+ } while ((NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_IS_OK(status)) &&
+ r.out.returned_size != 0);
+
+ return ret;
+}
+
+static bool test_QueryDomainInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryDomainInfo r;
+ struct samr_SetDomainInfo s;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13};
+ uint16_t set_ok[] = {1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0};
+ int i;
+ bool ret = true;
+ const char *domain_comment = talloc_asprintf(mem_ctx,
+ "Tortured by Samba4 RPC-SAMR: %s",
+ timestring(mem_ctx, time(NULL)));
+
+ s.in.domain_handle = handle;
+ s.in.level = 4;
+ s.in.info = talloc(mem_ctx, union samr_DomainInfo);
+
+ s.in.info->oem.oem_information.string = domain_comment;
+ status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetDomainInfo level %u (set comment) failed - %s\n",
+ r.in.level, nt_errstr(status));
+ return false;
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryDomainInfo level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+
+ status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+
+ switch (levels[i]) {
+ case 2:
+ if (strcmp(r.out.info->general.oem_information.string, domain_comment) != 0) {
+ printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n",
+ levels[i], r.out.info->general.oem_information.string, domain_comment);
+ ret = false;
+ }
+ if (!r.out.info->general.primary.string) {
+ printf("QueryDomainInfo level %u returned no PDC name\n",
+ levels[i]);
+ ret = false;
+ } else if (r.out.info->general.role == SAMR_ROLE_DOMAIN_PDC) {
+ if (dcerpc_server_name(p) && strcasecmp_m(dcerpc_server_name(p), r.out.info->general.primary.string) != 0) {
+ printf("QueryDomainInfo level %u returned different PDC name (%s) compared to server name (%s), despite claiming to be the PDC\n",
+ levels[i], r.out.info->general.primary.string, dcerpc_server_name(p));
+ }
+ }
+ break;
+ case 4:
+ if (strcmp(r.out.info->oem.oem_information.string, domain_comment) != 0) {
+ printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n",
+ levels[i], r.out.info->oem.oem_information.string, domain_comment);
+ ret = false;
+ }
+ break;
+ case 6:
+ if (!r.out.info->info6.primary.string) {
+ printf("QueryDomainInfo level %u returned no PDC name\n",
+ levels[i]);
+ ret = false;
+ }
+ break;
+ case 11:
+ if (strcmp(r.out.info->general2.general.oem_information.string, domain_comment) != 0) {
+ printf("QueryDomainInfo level %u returned different comment (%s, expected %s)\n",
+ levels[i], r.out.info->general2.general.oem_information.string, domain_comment);
+ ret = false;
+ }
+ break;
+ }
+
+ printf("Testing SetDomainInfo level %u\n", levels[i]);
+
+ s.in.domain_handle = handle;
+ s.in.level = levels[i];
+ s.in.info = r.out.info;
+
+ status = dcerpc_samr_SetDomainInfo(p, mem_ctx, &s);
+ if (set_ok[i]) {
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ } else {
+ if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, status)) {
+ printf("SetDomainInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_QueryDomainInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_QueryDomainInfo2 r;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ printf("Testing QueryDomainInfo2 level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+
+ status = dcerpc_samr_QueryDomainInfo2(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDomainInfo2 level %u failed - %s\n",
+ r.in.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ }
+
+ return true;
+}
+
+/* Test whether querydispinfo level 5 and enumdomgroups return the same
+ set of group names. */
+static bool test_GroupList(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ struct samr_EnumDomainGroups q1;
+ struct samr_QueryDisplayInfo q2;
+ NTSTATUS status;
+ uint32_t resume_handle=0;
+ int i;
+ bool ret = true;
+
+ int num_names = 0;
+ const char **names = NULL;
+
+ printf("Testing coherency of querydispinfo vs enumdomgroups\n");
+
+ q1.in.domain_handle = handle;
+ q1.in.resume_handle = &resume_handle;
+ q1.in.max_size = 5;
+ q1.out.resume_handle = &resume_handle;
+
+ status = STATUS_MORE_ENTRIES;
+ while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ status = dcerpc_samr_EnumDomainGroups(p, mem_ctx, &q1);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i=0; i<q1.out.num_entries; i++) {
+ add_string_to_array(mem_ctx,
+ q1.out.sam->entries[i].name.string,
+ &names, &num_names);
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomainGroups failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!q1.out.sam) {
+ printf("EnumDomainGroups failed to return q1.out.sam\n");
+ return false;
+ }
+
+ q2.in.domain_handle = handle;
+ q2.in.level = 5;
+ q2.in.start_idx = 0;
+ q2.in.max_entries = 5;
+ q2.in.buf_size = (uint32_t)-1;
+
+ status = STATUS_MORE_ENTRIES;
+ while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ status = dcerpc_samr_QueryDisplayInfo(p, mem_ctx, &q2);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i=0; i<q2.out.info.info5.count; i++) {
+ int j;
+ const char *name = q2.out.info.info5.entries[i].account_name.string;
+ bool found = false;
+ for (j=0; j<num_names; j++) {
+ if (names[j] == NULL)
+ continue;
+ if (strequal(names[j], name)) {
+ names[j] = NULL;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ printf("QueryDisplayInfo gave name [%s] that EnumDomainGroups did not\n",
+ name);
+ ret = false;
+ }
+ }
+ q2.in.start_idx += q2.out.info.info5.count;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryDisplayInfo level 5 failed - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ for (i=0; i<num_names; i++) {
+ if (names[i] != NULL) {
+ printf("EnumDomainGroups gave name [%s] that QueryDisplayInfo did not\n",
+ names[i]);
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_DeleteDomainGroup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *group_handle)
+{
+ struct samr_DeleteDomainGroup d;
+ NTSTATUS status;
+ bool ret = true;
+
+ printf("Testing DeleteDomainGroup\n");
+
+ d.in.group_handle = group_handle;
+ d.out.group_handle = group_handle;
+
+ status = dcerpc_samr_DeleteDomainGroup(p, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteDomainGroup failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_TestPrivateFunctionsDomain(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle)
+{
+ struct samr_TestPrivateFunctionsDomain r;
+ NTSTATUS status;
+ bool ret = true;
+
+ printf("Testing TestPrivateFunctionsDomain\n");
+
+ r.in.domain_handle = domain_handle;
+
+ status = dcerpc_samr_TestPrivateFunctionsDomain(p, mem_ctx, &r);
+ if (!NT_STATUS_EQUAL(NT_STATUS_NOT_IMPLEMENTED, status)) {
+ printf("TestPrivateFunctionsDomain failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_RidToSid(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct dom_sid *domain_sid,
+ struct policy_handle *domain_handle)
+{
+ struct samr_RidToSid r;
+ NTSTATUS status;
+ bool ret = true;
+ struct dom_sid *calc_sid;
+ int rids[] = { 0, 42, 512, 10200 };
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(rids);i++) {
+
+ printf("Testing RidToSid\n");
+
+ calc_sid = dom_sid_dup(mem_ctx, domain_sid);
+ r.in.domain_handle = domain_handle;
+ r.in.rid = rids[i];
+
+ status = dcerpc_samr_RidToSid(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("RidToSid for %d failed - %s\n", rids[i], nt_errstr(status));
+ ret = false;
+ } else {
+ calc_sid = dom_sid_add_rid(calc_sid, calc_sid, rids[i]);
+
+ if (!dom_sid_equal(calc_sid, r.out.sid)) {
+ printf("RidToSid for %d failed - got %s, expected %s\n", rids[i],
+ dom_sid_string(mem_ctx, r.out.sid),
+ dom_sid_string(mem_ctx, calc_sid));
+ ret = false;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetBootKeyInformation(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle)
+{
+ struct samr_GetBootKeyInformation r;
+ NTSTATUS status;
+ bool ret = true;
+
+ printf("Testing GetBootKeyInformation\n");
+
+ r.in.domain_handle = domain_handle;
+
+ status = dcerpc_samr_GetBootKeyInformation(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* w2k3 seems to fail this sometimes and pass it sometimes */
+ printf("GetBootKeyInformation (ignored) - %s\n", nt_errstr(status));
+ }
+
+ return ret;
+}
+
+static bool test_AddGroupMember(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ struct policy_handle *group_handle)
+{
+ NTSTATUS status;
+ struct samr_AddGroupMember r;
+ struct samr_DeleteGroupMember d;
+ struct samr_QueryGroupMember q;
+ struct samr_SetMemberAttributesOfGroup s;
+ bool ret = true;
+ uint32_t rid;
+
+ status = test_LookupName(p, tctx, domain_handle, TEST_ACCOUNT_NAME, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("test_AddGroupMember looking up name " TEST_ACCOUNT_NAME " failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ r.in.group_handle = group_handle;
+ r.in.rid = rid;
+ r.in.flags = 0; /* ??? */
+
+ printf("Testing AddGroupMember and DeleteGroupMember\n");
+
+ d.in.group_handle = group_handle;
+ d.in.rid = rid;
+
+ status = dcerpc_samr_DeleteGroupMember(p, tctx, &d);
+ if (!NT_STATUS_EQUAL(NT_STATUS_MEMBER_NOT_IN_GROUP, status)) {
+ printf("DeleteGroupMember gave %s - should be NT_STATUS_MEMBER_NOT_IN_GROUP\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_samr_AddGroupMember(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddGroupMember failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_samr_AddGroupMember(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(NT_STATUS_MEMBER_IN_GROUP, status)) {
+ printf("AddGroupMember gave %s - should be NT_STATUS_MEMBER_IN_GROUP\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping SetMemberAttributesOfGroup test against Samba4\n");
+ } else {
+ /* this one is quite strange. I am using random inputs in the
+ hope of triggering an error that might give us a clue */
+
+ s.in.group_handle = group_handle;
+ s.in.unknown1 = random();
+ s.in.unknown2 = random();
+
+ status = dcerpc_samr_SetMemberAttributesOfGroup(p, tctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetMemberAttributesOfGroup failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ }
+
+ q.in.group_handle = group_handle;
+
+ status = dcerpc_samr_QueryGroupMember(p, tctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QueryGroupMember failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_samr_DeleteGroupMember(p, tctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("DeleteGroupMember failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = dcerpc_samr_AddGroupMember(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("AddGroupMember failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return ret;
+}
+
+
+static bool test_CreateDomainGroup(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle,
+ struct policy_handle *group_handle,
+ struct dom_sid *domain_sid)
+{
+ NTSTATUS status;
+ struct samr_CreateDomainGroup r;
+ uint32_t rid;
+ struct lsa_String name;
+ bool ret = true;
+
+ init_lsa_String(&name, TEST_GROUPNAME);
+
+ r.in.domain_handle = domain_handle;
+ r.in.name = &name;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.group_handle = group_handle;
+ r.out.rid = &rid;
+
+ printf("Testing CreateDomainGroup(%s)\n", r.in.name->string);
+
+ status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r);
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(mem_ctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Server correctly refused create of '%s'\n", r.in.name->string);
+ return true;
+ } else {
+ printf("Server should have refused create of '%s', got %s instead\n", r.in.name->string,
+ nt_errstr(status));
+ return false;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_GROUP_EXISTS)) {
+ if (!test_DeleteGroup_byname(p, mem_ctx, domain_handle, r.in.name->string)) {
+ printf("CreateDomainGroup failed: Could not delete domain group %s - %s\n", r.in.name->string,
+ nt_errstr(status));
+ return false;
+ }
+ status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ if (!test_DeleteUser_byname(p, mem_ctx, domain_handle, r.in.name->string)) {
+
+ printf("CreateDomainGroup failed: Could not delete user %s - %s\n", r.in.name->string,
+ nt_errstr(status));
+ return false;
+ }
+ status = dcerpc_samr_CreateDomainGroup(p, mem_ctx, &r);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateDomainGroup failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!test_AddGroupMember(p, mem_ctx, domain_handle, group_handle)) {
+ printf("CreateDomainGroup failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (!test_SetGroupInfo(p, mem_ctx, group_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+/*
+ its not totally clear what this does. It seems to accept any sid you like.
+*/
+static bool test_RemoveMemberFromForeignDomain(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_handle)
+{
+ NTSTATUS status;
+ struct samr_RemoveMemberFromForeignDomain r;
+
+ r.in.domain_handle = domain_handle;
+ r.in.sid = dom_sid_parse_talloc(mem_ctx, "S-1-5-32-12-34-56-78");
+
+ status = dcerpc_samr_RemoveMemberFromForeignDomain(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("RemoveMemberFromForeignDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+
+static bool test_Connect(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle);
+
+static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, struct dom_sid *sid,
+ enum torture_samr_choice which_ops)
+{
+ NTSTATUS status;
+ struct samr_OpenDomain r;
+ struct policy_handle domain_handle;
+ struct policy_handle alias_handle;
+ struct policy_handle user_handle;
+ struct policy_handle group_handle;
+ bool ret = true;
+
+ ZERO_STRUCT(alias_handle);
+ ZERO_STRUCT(user_handle);
+ ZERO_STRUCT(group_handle);
+ ZERO_STRUCT(domain_handle);
+
+ printf("Testing OpenDomain of %s\n", dom_sid_string(tctx, sid));
+
+ r.in.connect_handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.sid = sid;
+ r.out.domain_handle = &domain_handle;
+
+ status = dcerpc_samr_OpenDomain(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* run the domain tests with the main handle closed - this tests
+ the servers reference counting */
+ ret &= test_samr_handle_Close(p, tctx, handle);
+
+ switch (which_ops) {
+ case TORTURE_SAMR_USER_ATTRIBUTES:
+ case TORTURE_SAMR_PASSWORDS:
+ ret &= test_CreateUser2(p, tctx, &domain_handle, sid, which_ops);
+ ret &= test_CreateUser(p, tctx, &domain_handle, &user_handle, sid, which_ops);
+ /* This test needs 'complex' users to validate */
+ ret &= test_QueryDisplayInfo(p, tctx, &domain_handle);
+ if (!ret) {
+ printf("Testing PASSWORDS or ATTRIBUTES on domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+ break;
+ case TORTURE_SAMR_OTHER:
+ ret &= test_CreateUser(p, tctx, &domain_handle, &user_handle, sid, which_ops);
+ if (!ret) {
+ printf("Failed to CreateUser in SAMR-OTHER on domain %s!\n", dom_sid_string(tctx, sid));
+ }
+ ret &= test_QuerySecurity(p, tctx, &domain_handle);
+ ret &= test_RemoveMemberFromForeignDomain(p, tctx, &domain_handle);
+ ret &= test_CreateAlias(p, tctx, &domain_handle, &alias_handle, sid);
+ ret &= test_CreateDomainGroup(p, tctx, &domain_handle, &group_handle, sid);
+ ret &= test_QueryDomainInfo(p, tctx, &domain_handle);
+ ret &= test_QueryDomainInfo2(p, tctx, &domain_handle);
+ ret &= test_EnumDomainUsers(p, tctx, &domain_handle);
+ ret &= test_EnumDomainUsers_async(p, tctx, &domain_handle);
+ ret &= test_EnumDomainGroups(p, tctx, &domain_handle);
+ ret &= test_EnumDomainAliases(p, tctx, &domain_handle);
+ ret &= test_QueryDisplayInfo2(p, tctx, &domain_handle);
+ ret &= test_QueryDisplayInfo3(p, tctx, &domain_handle);
+ ret &= test_QueryDisplayInfo_continue(p, tctx, &domain_handle);
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ printf("skipping GetDisplayEnumerationIndex test against Samba4\n");
+ } else {
+ ret &= test_GetDisplayEnumerationIndex(p, tctx, &domain_handle);
+ ret &= test_GetDisplayEnumerationIndex2(p, tctx, &domain_handle);
+ }
+ ret &= test_GroupList(p, tctx, &domain_handle);
+ ret &= test_TestPrivateFunctionsDomain(p, tctx, &domain_handle);
+ ret &= test_RidToSid(p, tctx, sid, &domain_handle);
+ ret &= test_GetBootKeyInformation(p, tctx, &domain_handle);
+ if (!ret) {
+ printf("Testing SAMR-OTHER on domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+ break;
+ }
+
+ if (!policy_handle_empty(&user_handle) &&
+ !test_DeleteUser(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!policy_handle_empty(&alias_handle) &&
+ !test_DeleteAlias(p, tctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!policy_handle_empty(&group_handle) &&
+ !test_DeleteDomainGroup(p, tctx, &group_handle)) {
+ ret = false;
+ }
+
+ ret &= test_samr_handle_Close(p, tctx, &domain_handle);
+
+ /* reconnect the main handle */
+ ret &= test_Connect(p, tctx, handle);
+
+ if (!ret) {
+ printf("Testing domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+
+ return ret;
+}
+
+static bool test_LookupDomain(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, const char *domain,
+ enum torture_samr_choice which_ops)
+{
+ NTSTATUS status;
+ struct samr_LookupDomain r;
+ struct lsa_String n1;
+ struct lsa_String n2;
+ bool ret = true;
+
+ printf("Testing LookupDomain(%s)\n", domain);
+
+ /* check for correct error codes */
+ r.in.connect_handle = handle;
+ r.in.domain_name = &n2;
+ n2.string = NULL;
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_PARAMETER, status)) {
+ printf("failed: LookupDomain expected NT_STATUS_INVALID_PARAMETER - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ init_lsa_String(&n2, "xxNODOMAINxx");
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &r);
+ if (!NT_STATUS_EQUAL(NT_STATUS_NO_SUCH_DOMAIN, status)) {
+ printf("failed: LookupDomain expected NT_STATUS_NO_SUCH_DOMAIN - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ r.in.connect_handle = handle;
+
+ init_lsa_String(&n1, domain);
+ r.in.domain_name = &n1;
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (!test_GetDomPwInfo(p, tctx, &n1)) {
+ ret = false;
+ }
+
+ if (!test_OpenDomain(p, tctx, handle, r.out.sid, which_ops)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_EnumDomains(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, enum torture_samr_choice which_ops)
+{
+ NTSTATUS status;
+ struct samr_EnumDomains r;
+ uint32_t resume_handle = 0;
+ int i;
+ bool ret = true;
+
+ r.in.connect_handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.buf_size = (uint32_t)-1;
+ r.out.resume_handle = &resume_handle;
+
+ status = dcerpc_samr_EnumDomains(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomains failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!r.out.sam) {
+ return false;
+ }
+
+ for (i=0;i<r.out.sam->count;i++) {
+ if (!test_LookupDomain(p, tctx, handle,
+ r.out.sam->entries[i].name.string, which_ops)) {
+ ret = false;
+ }
+ }
+
+ status = dcerpc_samr_EnumDomains(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomains failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return ret;
+}
+
+
+static bool test_Connect(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct samr_Connect r;
+ struct samr_Connect2 r2;
+ struct samr_Connect3 r3;
+ struct samr_Connect4 r4;
+ struct samr_Connect5 r5;
+ union samr_ConnectInfo info;
+ struct policy_handle h;
+ bool ret = true, got_handle = false;
+
+ printf("testing samr_Connect\n");
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ got_handle = true;
+ *handle = h;
+ }
+
+ printf("testing samr_Connect2\n");
+
+ r2.in.system_name = NULL;
+ r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r2.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect2(p, mem_ctx, &r2);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(p, mem_ctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ printf("testing samr_Connect3\n");
+
+ r3.in.system_name = NULL;
+ r3.in.unknown = 0;
+ r3.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r3.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect3(p, mem_ctx, &r3);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect3 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(p, mem_ctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ printf("testing samr_Connect4\n");
+
+ r4.in.system_name = "";
+ r4.in.unknown = 0;
+ r4.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r4.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect4(p, mem_ctx, &r4);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect4 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(p, mem_ctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ printf("testing samr_Connect5\n");
+
+ info.info1.unknown1 = 0;
+ info.info1.unknown2 = 0;
+
+ r5.in.system_name = "";
+ r5.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r5.in.level = 1;
+ r5.in.info = &info;
+ r5.out.info = &info;
+ r5.out.connect_handle = &h;
+
+ status = dcerpc_samr_Connect5(p, mem_ctx, &r5);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(p, mem_ctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ return ret;
+}
+
+
+bool torture_rpc_samr(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ ret &= test_QuerySecurity(p, torture, &handle);
+
+ ret &= test_EnumDomains(p, torture, &handle, TORTURE_SAMR_OTHER);
+
+ ret &= test_SetDsrmPassword(p, torture, &handle);
+
+ ret &= test_Shutdown(p, torture, &handle);
+
+ ret &= test_samr_handle_Close(p, torture, &handle);
+
+ return ret;
+}
+
+
+bool torture_rpc_samr_users(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ ret &= test_QuerySecurity(p, torture, &handle);
+
+ ret &= test_EnumDomains(p, torture, &handle, TORTURE_SAMR_USER_ATTRIBUTES);
+
+ ret &= test_SetDsrmPassword(p, torture, &handle);
+
+ ret &= test_Shutdown(p, torture, &handle);
+
+ ret &= test_samr_handle_Close(p, torture, &handle);
+
+ return ret;
+}
+
+
+bool torture_rpc_samr_passwords(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle handle;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_Connect(p, torture, &handle);
+
+ ret &= test_EnumDomains(p, torture, &handle, TORTURE_SAMR_PASSWORDS);
+
+ ret &= test_samr_handle_Close(p, torture, &handle);
+
+ return ret;
+}
+
diff --git a/source4/torture/rpc/samr_accessmask.c b/source4/torture/rpc/samr_accessmask.c
new file mode 100644
index 0000000000..f496f6300c
--- /dev/null
+++ b/source4/torture/rpc/samr_accessmask.c
@@ -0,0 +1,658 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for accessmasks on the SAMR pipe
+
+ Copyright (C) Ronnie Sahlberg 2007
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+
+/* test user created to test the ACLs associated to SAMR objects */
+#define TEST_USER_NAME "samr_testuser"
+
+
+static NTSTATUS torture_samr_Close(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *h)
+{
+ NTSTATUS status;
+ struct samr_Close cl;
+
+ cl.in.handle = h;
+ cl.out.handle = h;
+ status = dcerpc_samr_Close(p, tctx, &cl);
+
+ return status;
+}
+
+static NTSTATUS torture_samr_Connect5(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ uint32_t mask, struct policy_handle *h)
+{
+ NTSTATUS status;
+ struct samr_Connect5 r5;
+ union samr_ConnectInfo info;
+
+ info.info1.unknown1 = 0;
+ info.info1.unknown2 = 0;
+ r5.in.system_name = "";
+ r5.in.level = 1;
+ r5.in.info = &info;
+ r5.out.info = &info;
+ r5.out.connect_handle = h;
+ r5.in.access_mask = mask;
+
+ status = dcerpc_samr_Connect5(p, tctx, &r5);
+
+ return status;
+}
+
+/* check which bits in accessmask allows us to connect to the server */
+static bool test_samr_accessmask_Connect5(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct policy_handle h;
+ int i;
+ uint32_t mask;
+
+ printf("testing which bits in accessmask allows us to connect\n");
+ mask = 1;
+ for (i=0;i<33;i++) {
+ printf("testing Connect5 with access mask 0x%08x", mask);
+ status = torture_samr_Connect5(tctx, p, mask, &h);
+ mask <<= 1;
+
+ switch (i) {
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 26:
+ case 27:
+ printf(" expecting to fail");
+ /* of only one of these bits are set we expect to
+ fail by default
+ */
+ if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ break;
+ default:
+ /* these bits set are expected to succeed by default */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &h);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ break;
+ }
+ printf(" OK\n");
+ }
+
+ return true;
+}
+
+/* check which bits in accessmask allows us to EnumDomains()
+ by default we must specify at least one of :
+ SAMR/EnumDomains
+ Maximum
+ GenericAll
+ GenericRead
+ in the access mask to Connect5() in order to be allowed to perform
+ EnumDomains() on the policy handle returned from Connect5()
+*/
+static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct samr_EnumDomains ed;
+ struct policy_handle ch;
+ int i;
+ uint32_t mask;
+ uint32_t resume_handle = 0;
+
+ printf("testing which bits in Connect5 accessmask allows us to EnumDomains\n");
+ mask = 1;
+ for (i=0;i<33;i++) {
+ printf("testing Connect5/EnumDomains with access mask 0x%08x", mask);
+ status = torture_samr_Connect5(tctx, p, mask, &ch);
+ mask <<= 1;
+
+ switch (i) {
+ case 4: /* SAMR/EnumDomains */
+ case 25: /* Maximum */
+ case 28: /* GenericAll */
+ case 31: /* GenericRead */
+ /* these bits set are expected to succeed by default */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ed.in.connect_handle = &ch;
+ ed.in.resume_handle = &resume_handle;
+ ed.in.buf_size = (uint32_t)-1;
+ ed.out.resume_handle = &resume_handle;
+
+ status = dcerpc_samr_EnumDomains(p, tctx, &ed);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumDomains failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &ch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ break;
+ default:
+ printf(" expecting to fail");
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(" OK\n");
+ continue;
+ }
+
+ ed.in.connect_handle = &ch;
+ ed.in.resume_handle = &resume_handle;
+ ed.in.buf_size = (uint32_t)-1;
+ ed.out.resume_handle = &resume_handle;
+
+ status = dcerpc_samr_EnumDomains(p, tctx, &ed);
+ if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
+ printf("EnumDomains failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &ch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ break;
+ }
+ printf(" OK\n");
+ }
+
+ return true;
+}
+
+
+/*
+ * test how ACLs affect how/if a user can connect to the SAMR service
+ *
+ * samr_SetSecurity() returns SUCCESS when changing the ACL for
+ * a policy handle got from Connect5() but the ACL is not changed on
+ * the server
+ */
+static bool test_samr_connect_user_acl(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *test_credentials,
+ const struct dom_sid *test_sid)
+
+{
+ NTSTATUS status;
+ struct policy_handle ch;
+ struct policy_handle uch;
+ struct samr_QuerySecurity qs;
+ struct samr_SetSecurity ss;
+ struct security_ace ace;
+ struct security_descriptor *sd;
+ struct sec_desc_buf sdb;
+ bool ret = true;
+ int sd_size;
+ struct dcerpc_pipe *test_p;
+ const char *binding = torture_setting_string(tctx, "binding", NULL);
+
+ printf("testing ACLs to allow/prevent users to connect to SAMR");
+
+ /* connect to SAMR */
+ status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+
+ /* get the current ACL for the SAMR policy handle */
+ qs.in.handle = &ch;
+ qs.in.sec_info = SECINFO_DACL;
+ status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ /* how big is the security descriptor? */
+ sd_size = qs.out.sdbuf->sd_size;
+
+
+ /* add an ACE to the security descriptor to deny the user the
+ * 'connect to server' right
+ */
+ sd = qs.out.sdbuf->sd;
+ ace.type = SEC_ACE_TYPE_ACCESS_DENIED;
+ ace.flags = 0;
+ ace.access_mask = SAMR_ACCESS_CONNECT_TO_SERVER;
+ ace.trustee = *test_sid;
+ status = security_descriptor_dacl_add(sd, &ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to add ACE to security descriptor\n");
+ ret = false;
+ }
+ ss.in.handle = &ch;
+ ss.in.sec_info = SECINFO_DACL;
+ ss.in.sdbuf = &sdb;
+ sdb.sd = sd;
+ status = dcerpc_samr_SetSecurity(p, tctx, &ss);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetSecurity failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+
+ /* Try to connect as the test user */
+ status = dcerpc_pipe_connect(tctx,
+ &test_p, binding, &ndr_table_samr,
+ test_credentials, NULL, tctx->lp_ctx);
+ /* connect to SAMR as the user */
+ status = torture_samr_Connect5(tctx, test_p, SEC_FLAG_MAXIMUM_ALLOWED, &uch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ /* disconnec the user */
+ talloc_free(test_p);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+
+ /* read the sequrity descriptor back. it should not have changed
+ * eventhough samr_SetSecurity returned SUCCESS
+ */
+ status = dcerpc_samr_QuerySecurity(p, tctx, &qs);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+ if (sd_size != qs.out.sdbuf->sd_size) {
+ printf("security descriptor changed\n");
+ ret = false;
+ }
+
+
+ status = torture_samr_Close(tctx, p, &ch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (ret == true) {
+ printf(" OK\n");
+ }
+ return ret;
+}
+
+/*
+ * test if the ACLs are enforced for users.
+ * a normal testuser only gets the rights provided in hte ACL for
+ * Everyone which does not include the SAMR_ACCESS_SHUTDOWN_SERVER
+ * right. If the ACLs are checked when a user connects
+ * a testuser that requests the accessmask with only this bit set
+ * the connect should fail.
+ */
+static bool test_samr_connect_user_acl_enforced(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *test_credentials,
+ const struct dom_sid *test_sid)
+
+{
+ NTSTATUS status;
+ struct policy_handle uch;
+ bool ret = true;
+ struct dcerpc_pipe *test_p;
+ const char *binding = torture_setting_string(tctx, "binding", NULL);
+
+ printf("testing if ACLs are enforced for non domain admin users when connecting to SAMR");
+
+
+ status = dcerpc_pipe_connect(tctx,
+ &test_p, binding, &ndr_table_samr,
+ test_credentials, NULL, tctx->lp_ctx);
+
+ /* connect to SAMR as the user */
+ status = torture_samr_Connect5(tctx, test_p, SAMR_ACCESS_SHUTDOWN_SERVER, &uch);
+ if (NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ printf(" OK\n");
+
+ /* disconnec the user */
+ talloc_free(test_p);
+
+ return ret;
+}
+
+/* check which bits in accessmask allows us to LookupDomain()
+ by default we must specify at least one of :
+ in the access mask to Connect5() in order to be allowed to perform
+ case 5: samr/opendomain
+ case 25: Maximum
+ case 28: GenericAll
+ case 29: GenericExecute
+ LookupDomain() on the policy handle returned from Connect5()
+*/
+static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct samr_LookupDomain ld;
+ struct policy_handle ch;
+ struct lsa_String dn;
+ int i;
+ uint32_t mask;
+
+ printf("testing which bits in Connect5 accessmask allows us to LookupDomain\n");
+ mask = 1;
+ for (i=0;i<33;i++) {
+ printf("testing Connect5/LookupDomain with access mask 0x%08x", mask);
+ status = torture_samr_Connect5(tctx, p, mask, &ch);
+ mask <<= 1;
+
+ switch (i) {
+ case 5:
+ case 25: /* Maximum */
+ case 28: /* GenericAll */
+ case 29: /* GenericExecute */
+ /* these bits set are expected to succeed by default */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ld.in.connect_handle = &ch;
+ ld.in.domain_name = &dn;
+ dn.string = lp_workgroup(tctx->lp_ctx);
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &ld);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &ch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ break;
+ default:
+ printf(" expecting to fail");
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(" OK\n");
+ continue;
+ }
+
+ ld.in.connect_handle = &ch;
+ ld.in.domain_name = &dn;
+ dn.string = lp_workgroup(tctx->lp_ctx);
+
+ status = dcerpc_samr_LookupDomain(p, tctx, &ld);
+ if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &ch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ break;
+ }
+ printf(" OK\n");
+ }
+
+ return true;
+}
+
+/* check which bits in accessmask allows us to OpenDomain()
+ by default we must specify at least one of :
+ samr/opendomain
+ Maximum
+ GenericAll
+ GenericExecute
+ in the access mask to Connect5() in order to be allowed to perform
+ OpenDomain() on the policy handle returned from Connect5()
+*/
+static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct samr_LookupDomain ld;
+ struct samr_OpenDomain od;
+ struct policy_handle ch;
+ struct policy_handle dh;
+ struct lsa_String dn;
+ int i;
+ uint32_t mask;
+
+
+ /* first we must grab the sid of the domain */
+ status = torture_samr_Connect5(tctx, p, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ld.in.connect_handle = &ch;
+ ld.in.domain_name = &dn;
+ dn.string = lp_workgroup(tctx->lp_ctx);
+ status = dcerpc_samr_LookupDomain(p, tctx, &ld);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+
+
+ printf("testing which bits in Connect5 accessmask allows us to OpenDomain\n");
+ mask = 1;
+ for (i=0;i<33;i++) {
+ printf("testing Connect5/OpenDomain with access mask 0x%08x", mask);
+ status = torture_samr_Connect5(tctx, p, mask, &ch);
+ mask <<= 1;
+
+ switch (i) {
+ case 5:
+ case 25: /* Maximum */
+ case 28: /* GenericAll */
+ case 29: /* GenericExecute */
+ /* these bits set are expected to succeed by default */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Connect5 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ od.in.connect_handle = &ch;
+ od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ od.in.sid = ld.out.sid;
+ od.out.domain_handle = &dh;
+
+ status = dcerpc_samr_OpenDomain(p, tctx, &od);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &dh);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, p, &ch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ break;
+ default:
+ printf(" expecting to fail");
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(" OK\n");
+ continue;
+ }
+
+ status = torture_samr_Close(tctx, p, &ch);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ break;
+ }
+ printf(" OK\n");
+ }
+
+ return true;
+}
+
+static bool test_samr_connect(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ void *testuser;
+ const char *testuser_passwd;
+ struct cli_credentials *test_credentials;
+ bool ret = true;
+ const struct dom_sid *test_sid;
+
+ /* create a test user */
+ testuser = torture_create_testuser(tctx, TEST_USER_NAME, lp_workgroup(tctx->lp_ctx),
+ ACB_NORMAL, &testuser_passwd);
+ if (!testuser) {
+ printf("Failed to create test user\n");
+ return false;
+ }
+ test_credentials = cli_credentials_init(tctx);
+ cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED);
+ cli_credentials_set_domain(test_credentials, lp_workgroup(tctx->lp_ctx),
+ CRED_SPECIFIED);
+ cli_credentials_set_username(test_credentials, TEST_USER_NAME, CRED_SPECIFIED);
+ cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED);
+ test_sid = torture_join_user_sid(testuser);
+
+
+ /* test which bits in the accessmask to Connect5
+ will allow us to connect to the server
+ */
+ if (!test_samr_accessmask_Connect5(tctx, p)) {
+ ret = false;
+ }
+
+
+ /* test which bits in the accessmask to Connect5 will allow
+ * us to call EnumDomains()
+ */
+ if (!test_samr_accessmask_EnumDomains(tctx, p)) {
+ ret = false;
+ }
+
+ /* test which bits in the accessmask to Connect5 will allow
+ * us to call LookupDomain()
+ */
+ if (!test_samr_accessmask_LookupDomain(tctx, p)) {
+ ret = false;
+ }
+
+
+ /* test which bits in the accessmask to Connect5 will allow
+ * us to call OpenDomain()
+ */
+ if (!test_samr_accessmask_OpenDomain(tctx, p)) {
+ ret = false;
+ }
+
+
+ /* test if ACLs can be changed for the policy handle
+ * returned by Connect5
+ */
+ if (!test_samr_connect_user_acl(tctx, p, test_credentials, test_sid)) {
+ ret = false;
+ }
+
+ /* test if the ACLs that are reported from the Connect5
+ * policy handle is enforced.
+ * i.e. an ordinary user only has the same rights as Everybody
+ * ReadControl
+ * Samr/OpenDomain
+ * Samr/EnumDomains
+ * Samr/ConnectToServer
+ * is granted and should therefore not be able to connect when
+ * requesting SAMR_ACCESS_SHUTDOWN_SERVER
+ */
+ if (!test_samr_connect_user_acl_enforced(tctx, p, test_credentials, test_sid)) {
+ ret = false;
+ }
+
+
+
+ /* remove the test user */
+ torture_leave_domain(tctx, testuser);
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_samr_accessmask(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SAMR_ACCESSMASK");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "samr",
+ &ndr_table_samr);
+
+ torture_rpc_tcase_add_test(tcase, "CONNECT", test_samr_connect);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/samsync.c b/source4/torture/rpc/samsync.c
new file mode 100644
index 0000000000..1e76de1dd2
--- /dev/null
+++ b/source4/torture/rpc/samsync.c
@@ -0,0 +1,1639 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for netlogon rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
+ Copyright (C) Tim Potter 2003
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "auth/auth.h"
+#include "lib/util/dlinklist.h"
+#include "lib/crypto/crypto.h"
+#include "system/time.h"
+#include "torture/rpc/rpc.h"
+#include "auth/gensec/schannel_proto.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "param/param.h"
+
+#define TEST_MACHINE_NAME "samsynctest"
+#define TEST_WKSTA_MACHINE_NAME "samsynctest2"
+#define TEST_USER_NAME "samsynctestuser"
+
+/*
+ try a netlogon SamLogon
+*/
+static NTSTATUS test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState *creds,
+ const char *domain, const char *account_name,
+ const char *workstation,
+ struct samr_Password *lm_hash,
+ struct samr_Password *nt_hash,
+ struct netr_SamInfo3 **info3)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogon r;
+ struct netr_Authenticator auth, auth2;
+ struct netr_NetworkInfo ninfo;
+
+ ninfo.identity_info.domain_name.string = domain;
+ ninfo.identity_info.parameter_control = 0;
+ ninfo.identity_info.logon_id_low = 0;
+ ninfo.identity_info.logon_id_high = 0;
+ ninfo.identity_info.account_name.string = account_name;
+ ninfo.identity_info.workstation.string = workstation;
+ generate_random_buffer(ninfo.challenge,
+ sizeof(ninfo.challenge));
+ if (nt_hash) {
+ ninfo.nt.length = 24;
+ ninfo.nt.data = talloc_array(mem_ctx, uint8_t, 24);
+ SMBOWFencrypt(nt_hash->hash, ninfo.challenge, ninfo.nt.data);
+ } else {
+ ninfo.nt.length = 0;
+ ninfo.nt.data = NULL;
+ }
+
+ if (lm_hash) {
+ ninfo.lm.length = 24;
+ ninfo.lm.data = talloc_array(mem_ctx, uint8_t, 24);
+ SMBOWFencrypt(lm_hash->hash, ninfo.challenge, ninfo.lm.data);
+ } else {
+ ninfo.lm.length = 0;
+ ninfo.lm.data = NULL;
+ }
+
+ r.in.server_name = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = workstation;
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = 2;
+ r.in.logon.network = &ninfo;
+
+ ZERO_STRUCT(auth2);
+ creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = 3;
+
+ status = dcerpc_netr_LogonSamLogon(p, mem_ctx, &r);
+
+ if (!creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ printf("Credential chaining failed\n");
+ }
+
+ if (info3) {
+ *info3 = r.out.validation.sam3;
+ }
+
+ return status;
+}
+
+struct samsync_state {
+/* we remember the sequence numbers so we can easily do a DatabaseDelta */
+ uint64_t seq_num[3];
+ const char *domain_name[2];
+ struct samsync_secret *secrets;
+ struct samsync_trusted_domain *trusted_domains;
+ struct creds_CredentialState *creds;
+ struct creds_CredentialState *creds_netlogon_wksta;
+ struct policy_handle *connect_handle;
+ struct policy_handle *domain_handle[2];
+ struct dom_sid *sid[2];
+ struct dcerpc_pipe *p;
+ struct dcerpc_pipe *p_netlogon_wksta;
+ struct dcerpc_pipe *p_samr;
+ struct dcerpc_pipe *p_lsa;
+ struct policy_handle *lsa_handle;
+};
+
+struct samsync_secret {
+ struct samsync_secret *prev, *next;
+ DATA_BLOB secret;
+ const char *name;
+ NTTIME mtime;
+};
+
+struct samsync_trusted_domain {
+ struct samsync_trusted_domain *prev, *next;
+ struct dom_sid *sid;
+ const char *name;
+};
+
+static struct policy_handle *samsync_open_domain(TALLOC_CTX *mem_ctx,
+ struct samsync_state *samsync_state,
+ const char *domain,
+ struct dom_sid **sid)
+{
+ struct lsa_String name;
+ struct samr_OpenDomain o;
+ struct samr_LookupDomain l;
+ struct policy_handle *domain_handle = talloc(mem_ctx, struct policy_handle);
+ NTSTATUS nt_status;
+
+ name.string = domain;
+ l.in.connect_handle = samsync_state->connect_handle;
+ l.in.domain_name = &name;
+
+ nt_status = dcerpc_samr_LookupDomain(samsync_state->p_samr, mem_ctx, &l);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(nt_status));
+ return NULL;
+ }
+
+ o.in.connect_handle = samsync_state->connect_handle;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.in.sid = l.out.sid;
+ o.out.domain_handle = domain_handle;
+
+ if (sid) {
+ *sid = l.out.sid;
+ }
+
+ nt_status = dcerpc_samr_OpenDomain(samsync_state->p_samr, mem_ctx, &o);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(nt_status));
+ return NULL;
+ }
+
+ return domain_handle;
+}
+
+static struct sec_desc_buf *samsync_query_samr_sec_desc(TALLOC_CTX *mem_ctx,
+ struct samsync_state *samsync_state,
+ struct policy_handle *handle)
+{
+ struct samr_QuerySecurity r;
+ NTSTATUS status;
+
+ r.in.handle = handle;
+ r.in.sec_info = 0x7;
+
+ status = dcerpc_samr_QuerySecurity(samsync_state->p_samr, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SAMR QuerySecurity failed - %s\n", nt_errstr(status));
+ return NULL;
+ }
+
+ return r.out.sdbuf;
+}
+
+static struct sec_desc_buf *samsync_query_lsa_sec_desc(TALLOC_CTX *mem_ctx,
+ struct samsync_state *samsync_state,
+ struct policy_handle *handle)
+{
+ struct lsa_QuerySecurity r;
+ NTSTATUS status;
+
+ r.in.handle = handle;
+ r.in.sec_info = 0x7;
+
+ status = dcerpc_lsa_QuerySecurity(samsync_state->p_lsa, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LSA QuerySecurity failed - %s\n", nt_errstr(status));
+ return NULL;
+ }
+
+ return r.out.sdbuf;
+}
+
+#define TEST_UINT64_EQUAL(i1, i2) do {\
+ if (i1 != i2) {\
+ printf("%s: uint64 mismatch: " #i1 ": 0x%016llx (%lld) != " #i2 ": 0x%016llx (%lld)\n", \
+ __location__, \
+ (long long)i1, (long long)i1, \
+ (long long)i2, (long long)i2);\
+ ret = false;\
+ } \
+} while (0)
+#define TEST_INT_EQUAL(i1, i2) do {\
+ if (i1 != i2) {\
+ printf("%s: integer mismatch: " #i1 ": 0x%08x (%d) != " #i2 ": 0x%08x (%d)\n", \
+ __location__, i1, i1, i2, i2); \
+ ret = false;\
+ } \
+} while (0)
+#define TEST_TIME_EQUAL(t1, t2) do {\
+ if (t1 != t2) {\
+ printf("%s: NTTIME mismatch: " #t1 ":%s != " #t2 ": %s\n", \
+ __location__, nt_time_string(mem_ctx, t1), nt_time_string(mem_ctx, t2));\
+ ret = false;\
+ } \
+} while (0)
+
+#define TEST_STRING_EQUAL(s1, s2) do {\
+ if (!((!s1.string || s1.string[0]=='\0') && (!s2.string || s2.string[0]=='\0')) \
+ && strcmp_safe(s1.string, s2.string) != 0) {\
+ printf("%s: string mismatch: " #s1 ":%s != " #s2 ": %s\n", \
+ __location__, s1.string, s2.string);\
+ ret = false;\
+ } \
+} while (0)
+
+#define TEST_SID_EQUAL(s1, s2) do {\
+ if (!dom_sid_equal(s1, s2)) {\
+ printf("%s: dom_sid mismatch: " #s1 ":%s != " #s2 ": %s\n", \
+ __location__, dom_sid_string(mem_ctx, s1), dom_sid_string(mem_ctx, s2));\
+ ret = false;\
+ } \
+} while (0)
+
+/* The ~SEC_DESC_SACL_PRESENT is because we don't, as administrator,
+ * get back the SACL part of the SD when we ask over SAMR */
+
+#define TEST_SEC_DESC_EQUAL(sd1, pipe, handle) do {\
+ struct sec_desc_buf *sdbuf = samsync_query_ ##pipe## _sec_desc(mem_ctx, samsync_state, \
+ handle); \
+ if (!sdbuf || !sdbuf->sd) { \
+ printf("Could not obtain security descriptor to match " #sd1 "\n");\
+ ret = false; \
+ } else {\
+ if (!security_descriptor_mask_equal(sd1.sd, sdbuf->sd, \
+ ~SEC_DESC_SACL_PRESENT)) {\
+ printf("Security Descriptor Mismatch for %s:\n", #sd1);\
+ ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor, "SamSync", sd1.sd);\
+ ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor, "SamR", sdbuf->sd);\
+ ret = false;\
+ }\
+ }\
+} while (0)
+
+static bool samsync_handle_domain(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain;
+ struct dom_sid *dom_sid;
+ struct samr_QueryDomainInfo q[14]; /* q[0] will be unused simple for clarity */
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13};
+ NTSTATUS nt_status;
+ int i;
+ bool ret = true;
+
+ samsync_state->seq_num[database_id] =
+ domain->sequence_num;
+ switch (database_id) {
+ case SAM_DATABASE_DOMAIN:
+ break;
+ case SAM_DATABASE_BUILTIN:
+ if (strcasecmp_m("BUILTIN", domain->domain_name.string) != 0) {
+ printf("BUILTIN domain has different name: %s\n", domain->domain_name.string);
+ }
+ break;
+ case SAM_DATABASE_PRIVS:
+ printf("DOMAIN entry on privs DB!\n");
+ return false;
+ break;
+ }
+
+ if (!samsync_state->domain_name[database_id]) {
+ samsync_state->domain_name[database_id] =
+ talloc_reference(samsync_state, domain->domain_name.string);
+ } else {
+ if (strcasecmp_m(samsync_state->domain_name[database_id], domain->domain_name.string) != 0) {
+ printf("Domain has name varies!: %s != %s\n", samsync_state->domain_name[database_id],
+ domain->domain_name.string);
+ return false;
+ }
+ }
+
+ if (!samsync_state->domain_handle[database_id]) {
+ samsync_state->domain_handle[database_id]
+ = talloc_reference(samsync_state,
+ samsync_open_domain(mem_ctx, samsync_state, samsync_state->domain_name[database_id],
+ &dom_sid));
+ }
+ if (samsync_state->domain_handle[database_id]) {
+ samsync_state->sid[database_id] = talloc_reference(samsync_state, dom_sid);
+ }
+
+ printf("\tsequence_nums[%d/%s]=%llu\n",
+ database_id, domain->domain_name.string,
+ (long long)samsync_state->seq_num[database_id]);
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ q[levels[i]].in.domain_handle = samsync_state->domain_handle[database_id];
+ q[levels[i]].in.level = levels[i];
+
+ nt_status = dcerpc_samr_QueryDomainInfo(samsync_state->p_samr, mem_ctx, &q[levels[i]]);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryDomainInfo level %u failed - %s\n",
+ q[levels[i]].in.level, nt_errstr(nt_status));
+ return false;
+ }
+ }
+
+ TEST_STRING_EQUAL(q[5].out.info->info5.domain_name, domain->domain_name);
+
+ TEST_STRING_EQUAL(q[2].out.info->general.oem_information, domain->oem_information);
+ TEST_STRING_EQUAL(q[4].out.info->oem.oem_information, domain->oem_information);
+ TEST_TIME_EQUAL(q[2].out.info->general.force_logoff_time, domain->force_logoff_time);
+ TEST_TIME_EQUAL(q[3].out.info->info3.force_logoff_time, domain->force_logoff_time);
+
+ TEST_TIME_EQUAL(q[1].out.info->info1.min_password_length, domain->min_password_length);
+ TEST_TIME_EQUAL(q[1].out.info->info1.password_history_length, domain->password_history_length);
+ TEST_TIME_EQUAL(q[1].out.info->info1.max_password_age, domain->max_password_age);
+ TEST_TIME_EQUAL(q[1].out.info->info1.min_password_age, domain->min_password_age);
+
+ TEST_UINT64_EQUAL(q[8].out.info->info8.sequence_num,
+ domain->sequence_num);
+ TEST_TIME_EQUAL(q[8].out.info->info8.domain_create_time,
+ domain->domain_create_time);
+ TEST_TIME_EQUAL(q[13].out.info->info13.domain_create_time,
+ domain->domain_create_time);
+
+ TEST_SEC_DESC_EQUAL(domain->sdbuf, samr, samsync_state->domain_handle[database_id]);
+
+ return ret;
+}
+
+static bool samsync_handle_policy(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ struct netr_DELTA_POLICY *policy = delta->delta_union.policy;
+
+ samsync_state->seq_num[database_id] =
+ policy->sequence_num;
+
+ if (!samsync_state->domain_name[SAM_DATABASE_DOMAIN]) {
+ samsync_state->domain_name[SAM_DATABASE_DOMAIN] =
+ talloc_reference(samsync_state, policy->primary_domain_name.string);
+ } else {
+ if (strcasecmp_m(samsync_state->domain_name[SAM_DATABASE_DOMAIN], policy->primary_domain_name.string) != 0) {
+ printf("PRIMARY domain has name varies between DOMAIN and POLICY!: %s != %s\n", samsync_state->domain_name[SAM_DATABASE_DOMAIN],
+ policy->primary_domain_name.string);
+ return false;
+ }
+ }
+
+ if (!dom_sid_equal(samsync_state->sid[SAM_DATABASE_DOMAIN], policy->sid)) {
+ printf("Domain SID from POLICY (%s) does not match domain sid from SAMR (%s)\n",
+ dom_sid_string(mem_ctx, policy->sid), dom_sid_string(mem_ctx, samsync_state->sid[SAM_DATABASE_DOMAIN]));
+ return false;
+ }
+
+ printf("\tsequence_nums[%d/PRIVS]=%llu\n",
+ database_id,
+ (long long)samsync_state->seq_num[database_id]);
+ return true;
+}
+
+static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_USER *user = delta->delta_union.user;
+ struct netr_SamInfo3 *info3;
+ struct samr_Password lm_hash;
+ struct samr_Password nt_hash;
+ struct samr_Password *lm_hash_p = NULL;
+ struct samr_Password *nt_hash_p = NULL;
+ const char *domain = samsync_state->domain_name[database_id];
+ const char *username = user->account_name.string;
+ NTSTATUS nt_status;
+ bool ret = true;
+
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ struct policy_handle user_handle;
+
+ struct samr_GetGroupsForUser getgroups;
+ if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) {
+ printf("SamSync needs domain information before the users\n");
+ return false;
+ }
+
+ r.in.domain_handle = samsync_state->domain_handle[database_id];
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.user_handle = &user_handle;
+
+ nt_status = dcerpc_samr_OpenUser(samsync_state->p_samr, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status));
+ return false;
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 21;
+
+ TEST_SEC_DESC_EQUAL(user->sdbuf, samr, &user_handle);
+
+ nt_status = dcerpc_samr_QueryUserInfo(samsync_state->p_samr, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(nt_status));
+ ret = false;
+ }
+
+ getgroups.in.user_handle = &user_handle;
+
+ nt_status = dcerpc_samr_GetGroupsForUser(samsync_state->p_samr, mem_ctx, &getgroups);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("GetGroupsForUser failed - %s\n",
+ nt_errstr(nt_status));
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &user_handle)) {
+ printf("samr_handle_Close failed - %s\n",
+ nt_errstr(nt_status));
+ ret = false;
+ }
+ if (!ret) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(nt_status));
+ return false;
+ }
+
+ TEST_STRING_EQUAL(q.out.info->info21.account_name, user->account_name);
+ TEST_STRING_EQUAL(q.out.info->info21.full_name, user->full_name);
+ TEST_INT_EQUAL(q.out.info->info21.rid, user->rid);
+ TEST_INT_EQUAL(q.out.info->info21.primary_gid, user->primary_gid);
+ TEST_STRING_EQUAL(q.out.info->info21.home_directory, user->home_directory);
+ TEST_STRING_EQUAL(q.out.info->info21.home_drive, user->home_drive);
+ TEST_STRING_EQUAL(q.out.info->info21.logon_script, user->logon_script);
+ TEST_STRING_EQUAL(q.out.info->info21.description, user->description);
+ TEST_STRING_EQUAL(q.out.info->info21.workstations, user->workstations);
+
+ TEST_TIME_EQUAL(q.out.info->info21.last_logon, user->last_logon);
+ TEST_TIME_EQUAL(q.out.info->info21.last_logoff, user->last_logoff);
+
+
+ TEST_INT_EQUAL(q.out.info->info21.logon_hours.units_per_week,
+ user->logon_hours.units_per_week);
+ if (ret) {
+ if (memcmp(q.out.info->info21.logon_hours.bits, user->logon_hours.bits,
+ q.out.info->info21.logon_hours.units_per_week/8) != 0) {
+ printf("Logon hours mismatch\n");
+ ret = false;
+ }
+ }
+
+ TEST_INT_EQUAL(q.out.info->info21.bad_password_count,
+ user->bad_password_count);
+ TEST_INT_EQUAL(q.out.info->info21.logon_count,
+ user->logon_count);
+
+ TEST_TIME_EQUAL(q.out.info->info21.last_password_change,
+ user->last_password_change);
+ TEST_TIME_EQUAL(q.out.info->info21.acct_expiry,
+ user->acct_expiry);
+
+ TEST_INT_EQUAL((q.out.info->info21.acct_flags & ~ACB_PW_EXPIRED), user->acct_flags);
+ if (user->acct_flags & ACB_PWNOEXP) {
+ if (q.out.info->info21.acct_flags & ACB_PW_EXPIRED) {
+ printf("ACB flags mismatch: both expired and no expiry!\n");
+ ret = false;
+ }
+ if (q.out.info->info21.force_password_change != (NTTIME)0x7FFFFFFFFFFFFFFFULL) {
+ printf("ACB flags mismatch: no password expiry, but force password change 0x%016llx (%lld) != 0x%016llx (%lld)\n",
+ (unsigned long long)q.out.info->info21.force_password_change,
+ (unsigned long long)q.out.info->info21.force_password_change,
+ (unsigned long long)0x7FFFFFFFFFFFFFFFULL, (unsigned long long)0x7FFFFFFFFFFFFFFFULL
+ );
+ ret = false;
+ }
+ }
+
+ TEST_INT_EQUAL(q.out.info->info21.nt_password_set, user->nt_password_present);
+ TEST_INT_EQUAL(q.out.info->info21.lm_password_set, user->lm_password_present);
+ TEST_INT_EQUAL(q.out.info->info21.password_expired, user->password_expired);
+
+ TEST_STRING_EQUAL(q.out.info->info21.comment, user->comment);
+ TEST_STRING_EQUAL(q.out.info->info21.parameters, user->parameters);
+
+ TEST_INT_EQUAL(q.out.info->info21.country_code, user->country_code);
+ TEST_INT_EQUAL(q.out.info->info21.code_page, user->code_page);
+
+ TEST_STRING_EQUAL(q.out.info->info21.profile_path, user->profile_path);
+
+ if (user->lm_password_present) {
+ sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0);
+ lm_hash_p = &lm_hash;
+ }
+ if (user->nt_password_present) {
+ sam_rid_crypt(rid, user->ntpassword.hash, nt_hash.hash, 0);
+ nt_hash_p = &nt_hash;
+ }
+
+ if (user->user_private_info.SensitiveData) {
+ DATA_BLOB data;
+ struct netr_USER_KEYS keys;
+ enum ndr_err_code ndr_err;
+ data.data = user->user_private_info.SensitiveData;
+ data.length = user->user_private_info.DataLength;
+ creds_arcfour_crypt(samsync_state->creds, data.data, data.length);
+ ndr_err = ndr_pull_struct_blob(&data, mem_ctx, lp_iconv_convenience(tctx->lp_ctx), &keys, (ndr_pull_flags_fn_t)ndr_pull_netr_USER_KEYS);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ if (keys.keys.keys2.lmpassword.length == 16) {
+ sam_rid_crypt(rid, keys.keys.keys2.lmpassword.pwd.hash, lm_hash.hash, 0);
+ lm_hash_p = &lm_hash;
+ }
+ if (keys.keys.keys2.ntpassword.length == 16) {
+ sam_rid_crypt(rid, keys.keys.keys2.ntpassword.pwd.hash, nt_hash.hash, 0);
+ nt_hash_p = &nt_hash;
+ }
+ } else {
+ printf("Failed to parse Sensitive Data for %s:\n", username);
+#if 0
+ dump_data(0, data.data, data.length);
+#endif
+ return false;
+ }
+ }
+
+ if (nt_hash_p) {
+ DATA_BLOB nt_hash_blob = data_blob_const(nt_hash_p, 16);
+ DEBUG(100,("ACCOUNT [%s\\%-25s] NTHASH %s\n", samsync_state->domain_name[0], username, data_blob_hex_string(mem_ctx, &nt_hash_blob)));
+ }
+ if (lm_hash_p) {
+ DATA_BLOB lm_hash_blob = data_blob_const(lm_hash_p, 16);
+ DEBUG(100,("ACCOUNT [%s\\%-25s] LMHASH %s\n", samsync_state->domain_name[0], username, data_blob_hex_string(mem_ctx, &lm_hash_blob)));
+ }
+
+ nt_status = test_SamLogon(samsync_state->p_netlogon_wksta, mem_ctx, samsync_state->creds_netlogon_wksta,
+ domain,
+ username,
+ TEST_WKSTA_MACHINE_NAME,
+ lm_hash_p,
+ nt_hash_p,
+ &info3);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_DISABLED)) {
+ if (user->acct_flags & ACB_DISABLED) {
+ return true;
+ }
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT)) {
+ if (user->acct_flags & ACB_WSTRUST) {
+ return true;
+ }
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT)) {
+ if (user->acct_flags & ACB_SVRTRUST) {
+ return true;
+ }
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
+ if (user->acct_flags & ACB_DOMTRUST) {
+ return true;
+ }
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
+ if (user->acct_flags & ACB_DOMTRUST) {
+ return true;
+ }
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCOUNT_LOCKED_OUT)) {
+ if (user->acct_flags & ACB_AUTOLOCK) {
+ return true;
+ }
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED)) {
+ if (q.out.info->info21.acct_flags & ACB_PW_EXPIRED) {
+ return true;
+ }
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
+ if (!lm_hash_p && !nt_hash_p) {
+ return true;
+ }
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_MUST_CHANGE)) {
+ /* We would need to know the server's current time to test this properly */
+ return true;
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ TEST_INT_EQUAL(user->rid, info3->base.rid);
+ TEST_INT_EQUAL(user->primary_gid, info3->base.primary_gid);
+ /* this is 0x0 from NT4 sp6 */
+ if (info3->base.acct_flags) {
+ TEST_INT_EQUAL(user->acct_flags, info3->base.acct_flags);
+ }
+ /* this is NULL from NT4 sp6 */
+ if (info3->base.account_name.string) {
+ TEST_STRING_EQUAL(user->account_name, info3->base.account_name);
+ }
+ /* this is NULL from Win2k3 */
+ if (info3->base.full_name.string) {
+ TEST_STRING_EQUAL(user->full_name, info3->base.full_name);
+ }
+ TEST_STRING_EQUAL(user->logon_script, info3->base.logon_script);
+ TEST_STRING_EQUAL(user->profile_path, info3->base.profile_path);
+ TEST_STRING_EQUAL(user->home_directory, info3->base.home_directory);
+ TEST_STRING_EQUAL(user->home_drive, info3->base.home_drive);
+ TEST_STRING_EQUAL(user->logon_script, info3->base.logon_script);
+
+
+ TEST_TIME_EQUAL(user->last_logon, info3->base.last_logon);
+ TEST_TIME_EQUAL(user->acct_expiry, info3->base.acct_expiry);
+ TEST_TIME_EQUAL(user->last_password_change, info3->base.last_password_change);
+ TEST_TIME_EQUAL(q.out.info->info21.force_password_change, info3->base.force_password_change);
+
+ /* Does the concept of a logoff time ever really
+ * exist? (not in any sensible way, according to the
+ * doco I read -- abartlet) */
+
+ /* This copes with the two different versions of 0 I see */
+ /* with NT4 sp6 we have the || case */
+ if (!((user->last_logoff == 0)
+ || (info3->base.last_logoff == 0x7fffffffffffffffLL))) {
+ TEST_TIME_EQUAL(user->last_logoff, info3->base.last_logoff);
+ }
+
+ TEST_INT_EQUAL(getgroups.out.rids->count, info3->base.groups.count);
+ if (getgroups.out.rids->count == info3->base.groups.count) {
+ int i, j;
+ int count = getgroups.out.rids->count;
+ bool *matched = talloc_zero_array(mem_ctx, bool, getgroups.out.rids->count);
+
+ for (i = 0; i < count; i++) {
+ for (j = 0; j < count; j++) {
+ if ((getgroups.out.rids->rids[i].rid ==
+ info3->base.groups.rids[j].rid)
+ && (getgroups.out.rids->rids[i].attributes ==
+ info3->base.groups.rids[j].attributes)) {
+ matched[i] = true;
+ }
+ }
+ }
+
+ for (i = 0; i < getgroups.out.rids->count; i++) {
+ if (matched[i] == false) {
+ ret = false;
+ printf("Could not find group RID %u found in getgroups in NETLOGON reply\n",
+ getgroups.out.rids->rids[i].rid);
+ }
+ }
+ }
+ return ret;
+ } else {
+ printf("Could not validate password for user %s\\%s: %s\n",
+ domain, username, nt_errstr(nt_status));
+ return false;
+ }
+ return false;
+}
+
+static bool samsync_handle_alias(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_ALIAS *alias = delta->delta_union.alias;
+ NTSTATUS nt_status;
+ bool ret = true;
+
+ struct samr_OpenAlias r;
+ struct samr_QueryAliasInfo q;
+ struct policy_handle alias_handle;
+
+ if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) {
+ printf("SamSync needs domain information before the users\n");
+ return false;
+ }
+
+ r.in.domain_handle = samsync_state->domain_handle[database_id];
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.alias_handle = &alias_handle;
+
+ nt_status = dcerpc_samr_OpenAlias(samsync_state->p_samr, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status));
+ return false;
+ }
+
+ q.in.alias_handle = &alias_handle;
+ q.in.level = 1;
+
+ TEST_SEC_DESC_EQUAL(alias->sdbuf, samr, &alias_handle);
+
+ nt_status = dcerpc_samr_QueryAliasInfo(samsync_state->p_samr, mem_ctx, &q);
+ if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &alias_handle)) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryAliasInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(nt_status));
+ return false;
+ }
+
+ TEST_STRING_EQUAL(q.out.info->all.name, alias->alias_name);
+ TEST_STRING_EQUAL(q.out.info->all.description, alias->description);
+ return ret;
+}
+
+static bool samsync_handle_group(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ uint32_t rid = delta->delta_id_union.rid;
+ struct netr_DELTA_GROUP *group = delta->delta_union.group;
+ NTSTATUS nt_status;
+ bool ret = true;
+
+ struct samr_OpenGroup r;
+ struct samr_QueryGroupInfo q;
+ struct policy_handle group_handle;
+
+ if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) {
+ printf("SamSync needs domain information before the users\n");
+ return false;
+ }
+
+ r.in.domain_handle = samsync_state->domain_handle[database_id];
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.group_handle = &group_handle;
+
+ nt_status = dcerpc_samr_OpenGroup(samsync_state->p_samr, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("OpenUser(%u) failed - %s\n", rid, nt_errstr(nt_status));
+ return false;
+ }
+
+ q.in.group_handle = &group_handle;
+ q.in.level = 1;
+
+ TEST_SEC_DESC_EQUAL(group->sdbuf, samr, &group_handle);
+
+ nt_status = dcerpc_samr_QueryGroupInfo(samsync_state->p_samr, mem_ctx, &q);
+ if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, &group_handle)) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("QueryGroupInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(nt_status));
+ return false;
+ }
+
+ TEST_STRING_EQUAL(q.out.info->all.name, group->group_name);
+ TEST_INT_EQUAL(q.out.info->all.attributes, group->attributes);
+ TEST_STRING_EQUAL(q.out.info->all.description, group->description);
+ return ret;
+}
+
+static bool samsync_handle_secret(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
+ const char *name = delta->delta_id_union.name;
+ struct samsync_secret *new = talloc(samsync_state, struct samsync_secret);
+ struct samsync_secret *old = talloc(mem_ctx, struct samsync_secret);
+ struct lsa_QuerySecret q;
+ struct lsa_OpenSecret o;
+ struct policy_handle sec_handle;
+ struct lsa_DATA_BUF_PTR bufp1;
+ struct lsa_DATA_BUF_PTR bufp2;
+ NTTIME new_mtime;
+ NTTIME old_mtime;
+ bool ret = true;
+ DATA_BLOB lsa_blob1, lsa_blob_out, session_key;
+ NTSTATUS status;
+
+ creds_arcfour_crypt(samsync_state->creds, secret->current_cipher.cipher_data,
+ secret->current_cipher.maxlen);
+
+ creds_arcfour_crypt(samsync_state->creds, secret->old_cipher.cipher_data,
+ secret->old_cipher.maxlen);
+
+ new->name = talloc_reference(new, name);
+ new->secret = data_blob_talloc(new, secret->current_cipher.cipher_data, secret->current_cipher.maxlen);
+ new->mtime = secret->current_cipher_set_time;
+
+ new = talloc_reference(samsync_state, new);
+ DLIST_ADD(samsync_state->secrets, new);
+
+ old->name = talloc_reference(old, name);
+ old->secret = data_blob_const(secret->old_cipher.cipher_data, secret->old_cipher.maxlen);
+ old->mtime = secret->old_cipher_set_time;
+
+ o.in.handle = samsync_state->lsa_handle;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.in.name.string = name;
+ o.out.sec_handle = &sec_handle;
+
+ status = dcerpc_lsa_OpenSecret(samsync_state->p_lsa, mem_ctx, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenSecret failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+/*
+ We would like to do this, but it is NOT_SUPPORTED on win2k3
+ TEST_SEC_DESC_EQUAL(secret->sdbuf, lsa, &sec_handle);
+*/
+ status = dcerpc_fetch_session_key(samsync_state->p_lsa, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_fetch_session_key failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+
+ ZERO_STRUCT(new_mtime);
+ ZERO_STRUCT(old_mtime);
+
+ /* fetch the secret back again */
+ q.in.sec_handle = &sec_handle;
+ q.in.new_val = &bufp1;
+ q.in.new_mtime = &new_mtime;
+ q.in.old_val = &bufp2;
+ q.in.old_mtime = &old_mtime;
+
+ bufp1.buf = NULL;
+ bufp2.buf = NULL;
+
+ status = dcerpc_lsa_QuerySecret(samsync_state->p_lsa, mem_ctx, &q);
+ if (NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) {
+ /* some things are just off limits */
+ return true;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("QuerySecret failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (q.out.old_val->buf == NULL) {
+ /* probably just not available due to ACLs */
+ } else {
+ lsa_blob1.data = q.out.old_val->buf->data;
+ lsa_blob1.length = q.out.old_val->buf->length;
+
+ status = sess_decrypt_blob(mem_ctx, &lsa_blob1, &session_key, &lsa_blob_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to decrypt secrets OLD blob: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!q.out.old_mtime) {
+ printf("OLD mtime not available on LSA for secret %s\n", old->name);
+ ret = false;
+ }
+ if (old->mtime != *q.out.old_mtime) {
+ printf("OLD mtime on secret %s does not match between SAMSYNC (%s) and LSA (%s)\n",
+ old->name, nt_time_string(mem_ctx, old->mtime),
+ nt_time_string(mem_ctx, *q.out.old_mtime));
+ ret = false;
+ }
+
+ if (old->secret.length != lsa_blob_out.length) {
+ printf("Returned secret %s doesn't match: %d != %d\n",
+ old->name, (int)old->secret.length, (int)lsa_blob_out.length);
+ ret = false;
+ } else if (memcmp(lsa_blob_out.data,
+ old->secret.data, old->secret.length) != 0) {
+ printf("Returned secret %s doesn't match: \n",
+ old->name);
+ DEBUG(1, ("SamSync Secret:\n"));
+ dump_data(1, old->secret.data, old->secret.length);
+ DEBUG(1, ("LSA Secret:\n"));
+ dump_data(1, lsa_blob_out.data, lsa_blob_out.length);
+ ret = false;
+ }
+
+ }
+
+ if (q.out.new_val->buf == NULL) {
+ /* probably just not available due to ACLs */
+ } else {
+ lsa_blob1.data = q.out.new_val->buf->data;
+ lsa_blob1.length = q.out.new_val->buf->length;
+
+ status = sess_decrypt_blob(mem_ctx, &lsa_blob1, &session_key, &lsa_blob_out);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to decrypt secrets OLD blob\n");
+ return false;
+ }
+
+ if (!q.out.new_mtime) {
+ printf("NEW mtime not available on LSA for secret %s\n", new->name);
+ ret = false;
+ }
+ if (new->mtime != *q.out.new_mtime) {
+ printf("NEW mtime on secret %s does not match between SAMSYNC (%s) and LSA (%s)\n",
+ new->name, nt_time_string(mem_ctx, new->mtime),
+ nt_time_string(mem_ctx, *q.out.new_mtime));
+ ret = false;
+ }
+
+ if (new->secret.length != lsa_blob_out.length) {
+ printf("Returned secret %s doesn't match: %d != %d\n",
+ new->name, (int)new->secret.length, (int)lsa_blob_out.length);
+ ret = false;
+ } else if (memcmp(lsa_blob_out.data,
+ new->secret.data, new->secret.length) != 0) {
+ printf("Returned secret %s doesn't match: \n",
+ new->name);
+ DEBUG(1, ("SamSync Secret:\n"));
+ dump_data(1, new->secret.data, new->secret.length);
+ DEBUG(1, ("LSA Secret:\n"));
+ dump_data(1, lsa_blob_out.data, lsa_blob_out.length);
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool samsync_handle_trusted_domain(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct netr_DELTA_TRUSTED_DOMAIN *trusted_domain = delta->delta_union.trusted_domain;
+ struct dom_sid *dom_sid = delta->delta_id_union.sid;
+
+ struct samsync_trusted_domain *new = talloc(samsync_state, struct samsync_trusted_domain);
+ struct lsa_OpenTrustedDomain t;
+ struct policy_handle trustdom_handle;
+ struct lsa_QueryTrustedDomainInfo q;
+ union lsa_TrustedDomainInfo *info[9];
+ int levels [] = {1, 3, 8};
+ int i;
+
+ new->name = talloc_reference(new, trusted_domain->domain_name.string);
+ new->sid = talloc_reference(new, dom_sid);
+
+ t.in.handle = samsync_state->lsa_handle;
+ t.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ t.in.sid = dom_sid;
+ t.out.trustdom_handle = &trustdom_handle;
+
+ status = dcerpc_lsa_OpenTrustedDomain(samsync_state->p_lsa, mem_ctx, &t);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenTrustedDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ for (i=0; i< ARRAY_SIZE(levels); i++) {
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = levels[i];
+ status = dcerpc_lsa_QueryTrustedDomainInfo(samsync_state->p_lsa, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (q.in.level == 8 && NT_STATUS_EQUAL(status,NT_STATUS_INVALID_PARAMETER)) {
+ info[levels[i]] = NULL;
+ continue;
+ }
+ printf("QueryInfoTrustedDomain level %d failed - %s\n",
+ levels[i], nt_errstr(status));
+ return false;
+ }
+ info[levels[i]] = q.out.info;
+ }
+
+ if (info[8]) {
+ TEST_SID_EQUAL(info[8]->full_info.info_ex.sid, dom_sid);
+ TEST_STRING_EQUAL(info[8]->full_info.info_ex.netbios_name, trusted_domain->domain_name);
+ }
+ TEST_STRING_EQUAL(info[1]->name.netbios_name, trusted_domain->domain_name);
+ TEST_INT_EQUAL(info[3]->posix_offset.posix_offset, trusted_domain->posix_offset);
+/*
+ We would like to do this, but it is NOT_SUPPORTED on win2k3
+ TEST_SEC_DESC_EQUAL(trusted_domain->sdbuf, lsa, &trustdom_handle);
+*/
+ new = talloc_reference(samsync_state, new);
+ DLIST_ADD(samsync_state->trusted_domains, new);
+
+ return ret;
+}
+
+static bool samsync_handle_account(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct netr_DELTA_ACCOUNT *account = delta->delta_union.account;
+ struct dom_sid *dom_sid = delta->delta_id_union.sid;
+
+ struct lsa_OpenAccount a;
+ struct policy_handle acct_handle;
+ struct lsa_EnumPrivsAccount e;
+ struct lsa_LookupPrivName r;
+
+ int i, j;
+
+ bool *found_priv_in_lsa;
+
+ a.in.handle = samsync_state->lsa_handle;
+ a.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ a.in.sid = dom_sid;
+ a.out.acct_handle = &acct_handle;
+
+ status = dcerpc_lsa_OpenAccount(samsync_state->p_lsa, mem_ctx, &a);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenTrustedDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ TEST_SEC_DESC_EQUAL(account->sdbuf, lsa, &acct_handle);
+
+ found_priv_in_lsa = talloc_zero_array(mem_ctx, bool, account->privilege_entries);
+
+ e.in.handle = &acct_handle;
+
+ status = dcerpc_lsa_EnumPrivsAccount(samsync_state->p_lsa, mem_ctx, &e);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("EnumPrivsAccount failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((account->privilege_entries && !e.out.privs)) {
+ printf("Account %s has privileges in SamSync, but not LSA\n",
+ dom_sid_string(mem_ctx, dom_sid));
+ return false;
+ }
+
+ if (!account->privilege_entries && e.out.privs && e.out.privs->count) {
+ printf("Account %s has privileges in LSA, but not SamSync\n",
+ dom_sid_string(mem_ctx, dom_sid));
+ return false;
+ }
+
+ TEST_INT_EQUAL(account->privilege_entries, e.out.privs->count);
+
+ for (i=0;i< e.out.privs->count; i++) {
+ r.in.handle = samsync_state->lsa_handle;
+ r.in.luid = &e.out.privs->set[i].luid;
+
+ status = dcerpc_lsa_LookupPrivName(samsync_state->p_lsa, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("\nLookupPrivName failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!r.out.name) {
+ printf("\nLookupPrivName failed to return a name\n");
+ return false;
+ }
+ for (j=0;j<account->privilege_entries; j++) {
+ if (strcmp(r.out.name->string, account->privilege_name[j].string) == 0) {
+ found_priv_in_lsa[j] = true;
+ break;
+ }
+ }
+ }
+ for (j=0;j<account->privilege_entries; j++) {
+ if (!found_priv_in_lsa[j]) {
+ printf("Privilage %s on account %s not found in LSA\n", account->privilege_name[j].string,
+ dom_sid_string(mem_ctx, dom_sid));
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+/*
+ try a netlogon DatabaseSync
+*/
+static bool test_DatabaseSync(struct torture_context *tctx,
+ struct samsync_state *samsync_state,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ TALLOC_CTX *loop_ctx, *delta_ctx, *trustdom_ctx;
+ struct netr_DatabaseSync r;
+ const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS};
+ int i, d;
+ bool ret = true;
+ struct samsync_trusted_domain *t;
+ struct samsync_secret *s;
+
+ const char *domain, *username;
+
+ r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(samsync_state->p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.preferredmaximumlength = (uint32_t)-1;
+ ZERO_STRUCT(r.in.return_authenticator);
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+ r.in.sync_context = 0;
+ r.in.database_id = database_ids[i];
+
+ printf("Testing DatabaseSync of id %d\n", r.in.database_id);
+
+ do {
+ loop_ctx = talloc_named(mem_ctx, 0, "DatabaseSync loop context");
+ creds_client_authenticator(samsync_state->creds, &r.in.credential);
+
+ status = dcerpc_netr_DatabaseSync(samsync_state->p, loop_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ printf("DatabaseSync - %s\n", nt_errstr(status));
+ ret = false;
+ break;
+ }
+
+ if (!creds_client_check(samsync_state->creds, &r.out.return_authenticator.cred)) {
+ printf("Credential chaining failed\n");
+ }
+
+ r.in.sync_context = r.out.sync_context;
+
+ for (d=0; d < r.out.delta_enum_array->num_deltas; d++) {
+ delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context");
+ switch (r.out.delta_enum_array->delta_enum[d].delta_type) {
+ case NETR_DELTA_DOMAIN:
+ if (!samsync_handle_domain(delta_ctx, samsync_state,
+ r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_DOMAIN\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_GROUP:
+ if (!samsync_handle_group(delta_ctx, samsync_state,
+ r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_USER\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_USER:
+ if (!samsync_handle_user(tctx, delta_ctx, samsync_state,
+ r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_USER\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_ALIAS:
+ if (!samsync_handle_alias(delta_ctx, samsync_state,
+ r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_ALIAS\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_POLICY:
+ if (!samsync_handle_policy(delta_ctx, samsync_state,
+ r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_POLICY\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_TRUSTED_DOMAIN:
+ if (!samsync_handle_trusted_domain(delta_ctx, samsync_state,
+ r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_TRUSTED_DOMAIN\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_ACCOUNT:
+ if (!samsync_handle_account(delta_ctx, samsync_state,
+ r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_ACCOUNT\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_SECRET:
+ if (!samsync_handle_secret(delta_ctx, samsync_state,
+ r.in.database_id, &r.out.delta_enum_array->delta_enum[d])) {
+ printf("Failed to handle DELTA_SECRET\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_GROUP_MEMBER:
+ case NETR_DELTA_ALIAS_MEMBER:
+ /* These are harder to cross-check, and we expect them */
+ break;
+ case NETR_DELTA_DELETE_GROUP:
+ case NETR_DELTA_RENAME_GROUP:
+ case NETR_DELTA_DELETE_USER:
+ case NETR_DELTA_RENAME_USER:
+ case NETR_DELTA_DELETE_ALIAS:
+ case NETR_DELTA_RENAME_ALIAS:
+ case NETR_DELTA_DELETE_TRUST:
+ case NETR_DELTA_DELETE_ACCOUNT:
+ case NETR_DELTA_DELETE_SECRET:
+ case NETR_DELTA_DELETE_GROUP2:
+ case NETR_DELTA_DELETE_USER2:
+ case NETR_DELTA_MODIFY_COUNT:
+ default:
+ printf("Uxpected delta type %d\n", r.out.delta_enum_array->delta_enum[d].delta_type);
+ ret = false;
+ break;
+ }
+ talloc_free(delta_ctx);
+ }
+ talloc_free(loop_ctx);
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+ }
+
+ domain = samsync_state->domain_name[SAM_DATABASE_DOMAIN];
+ if (!domain) {
+ printf("Never got a DOMAIN object in samsync!\n");
+ return false;
+ }
+
+ trustdom_ctx = talloc_named(mem_ctx, 0, "test_DatabaseSync Trusted domains context");
+
+ username = talloc_asprintf(trustdom_ctx, "%s$", domain);
+ for (t=samsync_state->trusted_domains; t; t=t->next) {
+ char *secret_name = talloc_asprintf(trustdom_ctx, "G$$%s", t->name);
+ for (s=samsync_state->secrets; s; s=s->next) {
+ if (strcasecmp_m(s->name, secret_name) == 0) {
+ NTSTATUS nt_status;
+ struct samr_Password nt_hash;
+ mdfour(nt_hash.hash, s->secret.data, s->secret.length);
+
+ printf("Checking password for %s\\%s\n", t->name, username);
+ nt_status = test_SamLogon(samsync_state->p_netlogon_wksta, trustdom_ctx, samsync_state->creds_netlogon_wksta,
+ t->name,
+ username,
+ TEST_WKSTA_MACHINE_NAME,
+ NULL,
+ &nt_hash,
+ NULL);
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_LOGON_SERVERS)) {
+ printf("Verifiction of trust password to %s failed: %s (the trusted domain is not available)\n",
+ t->name, nt_errstr(nt_status));
+
+ break;
+ }
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
+ printf("Verifiction of trust password to %s: should have failed (nologon interdomain trust account), instead: %s\n",
+ t->name, nt_errstr(nt_status));
+ ret = false;
+ }
+
+ /* break it */
+ nt_hash.hash[0]++;
+ nt_status = test_SamLogon(samsync_state->p_netlogon_wksta, trustdom_ctx, samsync_state->creds_netlogon_wksta,
+ t->name,
+ username,
+ TEST_WKSTA_MACHINE_NAME,
+ NULL,
+ &nt_hash,
+ NULL);
+
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) {
+ printf("Verifiction of trust password to %s: should have failed (wrong password), instead: %s\n",
+ t->name, nt_errstr(nt_status));
+ ret = false;
+ }
+
+ break;
+ }
+ }
+ }
+ talloc_free(trustdom_ctx);
+ return ret;
+}
+
+
+/*
+ try a netlogon DatabaseDeltas
+*/
+static bool test_DatabaseDeltas(struct samsync_state *samsync_state, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ TALLOC_CTX *loop_ctx;
+ struct netr_DatabaseDeltas r;
+ const uint32_t database_ids[] = {0, 1, 2};
+ int i;
+ bool ret = true;
+
+ r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(samsync_state->p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.preferredmaximumlength = (uint32_t)-1;
+ ZERO_STRUCT(r.in.return_authenticator);
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+ r.in.database_id = database_ids[i];
+ r.in.sequence_num = samsync_state->seq_num[i];
+
+ if (r.in.sequence_num == 0) continue;
+
+ /* this shows that the bdc doesn't need to do a single call for
+ * each seqnumber, and the pdc doesn't need to know about old values
+ * -- metze
+ */
+ r.in.sequence_num -= 10;
+
+
+ printf("Testing DatabaseDeltas of id %d at %llu\n",
+ r.in.database_id, (long long)r.in.sequence_num);
+
+ do {
+ loop_ctx = talloc_named(mem_ctx, 0, "test_DatabaseDeltas loop context");
+ creds_client_authenticator(samsync_state->creds, &r.in.credential);
+
+ status = dcerpc_netr_DatabaseDeltas(samsync_state->p, loop_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED)) {
+ printf("DatabaseDeltas - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (!creds_client_check(samsync_state->creds, &r.out.return_authenticator.cred)) {
+ printf("Credential chaining failed\n");
+ }
+
+ r.in.sequence_num++;
+ talloc_free(loop_ctx);
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+ }
+
+ return ret;
+}
+
+
+/*
+ try a netlogon DatabaseSync2
+*/
+static bool test_DatabaseSync2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct creds_CredentialState *creds)
+{
+ NTSTATUS status;
+ TALLOC_CTX *loop_ctx;
+ struct netr_DatabaseSync2 r;
+ const uint32_t database_ids[] = {0, 1, 2};
+ int i;
+ bool ret = true;
+
+ r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.preferredmaximumlength = (uint32_t)-1;
+ ZERO_STRUCT(r.in.return_authenticator);
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+ r.in.sync_context = 0;
+ r.in.database_id = database_ids[i];
+ r.in.restart_state = 0;
+
+ printf("Testing DatabaseSync2 of id %d\n", r.in.database_id);
+
+ do {
+ loop_ctx = talloc_named(mem_ctx, 0, "test_DatabaseSync2 loop context");
+ creds_client_authenticator(creds, &r.in.credential);
+
+ status = dcerpc_netr_DatabaseSync2(p, loop_ctx, &r);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ printf("DatabaseSync2 - %s\n", nt_errstr(status));
+ ret = false;
+ }
+
+ if (!creds_client_check(creds, &r.out.return_authenticator.cred)) {
+ printf("Credential chaining failed\n");
+ }
+
+ r.in.sync_context = r.out.sync_context;
+ talloc_free(loop_ctx);
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+ }
+
+ return ret;
+}
+
+
+
+bool torture_rpc_samsync(struct torture_context *torture)
+{
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ struct test_join *join_ctx;
+ struct test_join *join_ctx2;
+ struct test_join *user_ctx;
+ const char *machine_password;
+ const char *wksta_machine_password;
+ struct dcerpc_binding *b;
+ struct dcerpc_binding *b_netlogon_wksta;
+ struct samr_Connect c;
+ struct samr_SetDomainInfo s;
+ struct policy_handle *domain_policy;
+
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+ struct cli_credentials *credentials;
+ struct cli_credentials *credentials_wksta;
+
+ struct samsync_state *samsync_state;
+
+ char *test_machine_account;
+
+ char *test_wksta_machine_account;
+
+ mem_ctx = talloc_init("torture_rpc_netlogon");
+
+ test_machine_account = talloc_asprintf(mem_ctx, "%s$", TEST_MACHINE_NAME);
+ join_ctx = torture_create_testuser(torture, test_machine_account,
+ lp_workgroup(torture->lp_ctx), ACB_SVRTRUST,
+ &machine_password);
+ if (!join_ctx) {
+ talloc_free(mem_ctx);
+ printf("Failed to join as BDC\n");
+ return false;
+ }
+
+ test_wksta_machine_account = talloc_asprintf(mem_ctx, "%s$", TEST_WKSTA_MACHINE_NAME);
+ join_ctx2 = torture_create_testuser(torture, test_wksta_machine_account, lp_workgroup(torture->lp_ctx), ACB_WSTRUST, &wksta_machine_password);
+ if (!join_ctx2) {
+ talloc_free(mem_ctx);
+ printf("Failed to join as member\n");
+ return false;
+ }
+
+ user_ctx = torture_create_testuser(torture, TEST_USER_NAME,
+ lp_workgroup(torture->lp_ctx),
+ ACB_NORMAL, NULL);
+ if (!user_ctx) {
+ talloc_free(mem_ctx);
+ printf("Failed to create test account\n");
+ return false;
+ }
+
+ samsync_state = talloc_zero(mem_ctx, struct samsync_state);
+
+ samsync_state->p_samr = torture_join_samr_pipe(join_ctx);
+ samsync_state->connect_handle = talloc_zero(samsync_state, struct policy_handle);
+ samsync_state->lsa_handle = talloc_zero(samsync_state, struct policy_handle);
+ c.in.system_name = NULL;
+ c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ c.out.connect_handle = samsync_state->connect_handle;
+
+ status = dcerpc_samr_Connect(samsync_state->p_samr, mem_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("samr_Connect failed\n");
+ ret = false;
+ goto failed;
+ }
+
+ domain_policy = samsync_open_domain(mem_ctx, samsync_state, lp_workgroup(torture->lp_ctx), NULL);
+ if (!domain_policy) {
+ printf("samrsync_open_domain failed\n");
+ ret = false;
+ goto failed;
+ }
+
+ s.in.domain_handle = domain_policy;
+ s.in.level = 4;
+ s.in.info = talloc(mem_ctx, union samr_DomainInfo);
+
+ s.in.info->oem.oem_information.string
+ = talloc_asprintf(mem_ctx,
+ "Tortured by Samba4: %s",
+ timestring(mem_ctx, time(NULL)));
+ status = dcerpc_samr_SetDomainInfo(samsync_state->p_samr, mem_ctx, &s);
+
+ if (!test_samr_handle_Close(samsync_state->p_samr, mem_ctx, domain_policy)) {
+ ret = false;
+ goto failed;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetDomainInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+
+ status = torture_rpc_connection(torture,
+ &samsync_state->p_lsa,
+ &ndr_table_lsarpc);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = "\\";
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = samsync_state->lsa_handle;
+
+ status = dcerpc_lsa_OpenPolicy2(samsync_state->p_lsa, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenPolicy2 failed - %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = torture_rpc_binding(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN;
+
+ credentials = cli_credentials_init(mem_ctx);
+
+ cli_credentials_set_workstation(credentials, TEST_MACHINE_NAME, CRED_SPECIFIED);
+ cli_credentials_set_domain(credentials, lp_workgroup(torture->lp_ctx), CRED_SPECIFIED);
+ cli_credentials_set_username(credentials, test_machine_account, CRED_SPECIFIED);
+ cli_credentials_set_password(credentials, machine_password, CRED_SPECIFIED);
+ cli_credentials_set_secure_channel_type(credentials,
+ SEC_CHAN_BDC);
+
+ status = dcerpc_pipe_connect_b(samsync_state,
+ &samsync_state->p, b,
+ &ndr_table_netlogon,
+ credentials, torture->ev, torture->lp_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect to server as a BDC: %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = dcerpc_schannel_creds(samsync_state->p->conn->security_state.generic_state,
+ samsync_state, &samsync_state->creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ }
+
+
+
+ status = torture_rpc_binding(torture, &b_netlogon_wksta);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ b_netlogon_wksta->flags &= ~DCERPC_AUTH_OPTIONS;
+ b_netlogon_wksta->flags |= DCERPC_SCHANNEL | DCERPC_SIGN;
+
+ credentials_wksta = cli_credentials_init(mem_ctx);
+
+ cli_credentials_set_workstation(credentials_wksta, TEST_WKSTA_MACHINE_NAME, CRED_SPECIFIED);
+ cli_credentials_set_domain(credentials_wksta, lp_workgroup(torture->lp_ctx), CRED_SPECIFIED);
+ cli_credentials_set_username(credentials_wksta, test_wksta_machine_account, CRED_SPECIFIED);
+ cli_credentials_set_password(credentials_wksta, wksta_machine_password, CRED_SPECIFIED);
+ cli_credentials_set_secure_channel_type(credentials_wksta,
+ SEC_CHAN_WKSTA);
+
+ status = dcerpc_pipe_connect_b(samsync_state,
+ &samsync_state->p_netlogon_wksta,
+ b_netlogon_wksta,
+ &ndr_table_netlogon,
+ credentials_wksta, torture->ev, torture->lp_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect to server as a Workstation: %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ status = dcerpc_schannel_creds(samsync_state->p_netlogon_wksta->conn->security_state.generic_state,
+ samsync_state, &samsync_state->creds_netlogon_wksta);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to obtail schanel creds!\n");
+ ret = false;
+ }
+
+ if (!test_DatabaseSync(torture, samsync_state, mem_ctx)) {
+ printf("DatabaseSync failed\n");
+ ret = false;
+ }
+
+ if (!test_DatabaseDeltas(samsync_state, mem_ctx)) {
+ printf("DatabaseDeltas failed\n");
+ ret = false;
+ }
+
+ if (!test_DatabaseSync2(samsync_state->p, mem_ctx, samsync_state->creds)) {
+ printf("DatabaseSync2 failed\n");
+ ret = false;
+ }
+failed:
+
+ torture_leave_domain(torture, join_ctx);
+ torture_leave_domain(torture, join_ctx2);
+ torture_leave_domain(torture, user_ctx);
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/scanner.c b/source4/torture/rpc/scanner.c
new file mode 100644
index 0000000000..b761f406ec
--- /dev/null
+++ b/source4/torture/rpc/scanner.c
@@ -0,0 +1,151 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ scanner for rpc calls
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_mgmt_c.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/rpc.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc_proto.h"
+
+/*
+ work out how many calls there are for an interface
+ */
+static bool test_num_calls(struct torture_context *tctx,
+ const struct ndr_interface_table *iface,
+ TALLOC_CTX *mem_ctx,
+ struct ndr_syntax_id *id)
+{
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ int i;
+ DATA_BLOB stub_in, stub_out;
+ int idl_calls;
+ struct ndr_interface_table tbl;
+
+ /* FIXME: This should be fixed when torture_rpc_connection
+ * takes a ndr_syntax_id */
+ tbl.name = iface->name;
+ tbl.syntax_id = *id;
+
+ status = torture_rpc_connection(tctx, &p, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ char *uuid_str = GUID_string(mem_ctx, &id->uuid);
+ printf("Failed to connect to '%s' on '%s' - %s\n",
+ uuid_str, iface->name, nt_errstr(status));
+ talloc_free(uuid_str);
+ return true;
+ }
+
+ /* make null calls */
+ stub_in = data_blob(NULL, 1000);
+ memset(stub_in.data, 0xFF, stub_in.length);
+
+ for (i=0;i<200;i++) {
+ status = dcerpc_request(p, NULL, i, false, mem_ctx, &stub_in, &stub_out);
+ if (!NT_STATUS_IS_OK(status) &&
+ p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status) && p->last_fault_code == 5) {
+ printf("\tpipe disconnected at %d\n", i);
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(status) && p->last_fault_code == 0x80010111) {
+ printf("\terr 0x80010111 at %d\n", i);
+ goto done;
+ }
+ }
+
+ printf("\t%d calls available\n", i);
+ idl_calls = ndr_interface_num_calls(&id->uuid, id->if_version);
+ if (idl_calls == -1) {
+ printf("\tinterface not known in local IDL\n");
+ } else if (i != idl_calls) {
+ printf("\tWARNING: local IDL defines %u calls\n", idl_calls);
+ } else {
+ printf("\tOK: matches num_calls in local IDL\n");
+ }
+
+done:
+ talloc_free(p);
+ return true;
+}
+
+
+
+bool torture_rpc_scanner(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ TALLOC_CTX *loop_ctx;
+ bool ret = true;
+ const struct ndr_interface_list *l;
+ struct dcerpc_binding *b;
+
+ status = torture_rpc_binding(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ for (l=ndr_table_list();l;l=l->next) {
+ loop_ctx = talloc_named(torture, 0, "torture_rpc_scanner loop context");
+ /* some interfaces are not mappable */
+ if (l->table->num_calls == 0 ||
+ strcmp(l->table->name, "mgmt") == 0) {
+ talloc_free(loop_ctx);
+ continue;
+ }
+
+ printf("\nTesting pipe '%s'\n", l->table->name);
+
+ if (b->transport == NCACN_IP_TCP) {
+ status = dcerpc_epm_map_binding(torture, b, l->table, NULL, torture->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to map port for uuid %s\n",
+ GUID_string(loop_ctx, &l->table->syntax_id.uuid));
+ talloc_free(loop_ctx);
+ continue;
+ }
+ } else {
+ b->endpoint = talloc_strdup(b, l->table->name);
+ }
+
+ lp_set_cmdline(torture->lp_ctx, "torture:binding", dcerpc_binding_string(torture, b));
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_mgmt);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(loop_ctx);
+ ret = false;
+ continue;
+ }
+
+ if (!test_inq_if_ids(torture, p, torture, test_num_calls, l->table)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
diff --git a/source4/torture/rpc/schannel.c b/source4/torture/rpc/schannel.c
new file mode 100644
index 0000000000..fae0093e4d
--- /dev/null
+++ b/source4/torture/rpc/schannel.c
@@ -0,0 +1,835 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for schannel operations
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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 "includes.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "auth/credentials/credentials.h"
+#include "torture/rpc/rpc.h"
+#include "lib/cmdline/popt_common.h"
+#include "auth/gensec/schannel_proto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/security/security.h"
+#include "system/filesys.h"
+#include "param/param.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/composite/composite.h"
+#include "lib/events/events.h"
+
+#define TEST_MACHINE_NAME "schannel"
+
+/*
+ try a netlogon SamLogon
+*/
+bool test_netlogon_ex_ops(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct creds_CredentialState *creds)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogonEx r;
+ struct netr_NetworkInfo ninfo;
+ DATA_BLOB names_blob, chal, lm_resp, nt_resp;
+ int i;
+ int flags = CLI_CRED_NTLM_AUTH;
+ if (lp_client_lanman_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lp_client_ntlmv2_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ cli_credentials_get_ntlm_username_domain(cmdline_credentials, tctx,
+ &ninfo.identity_info.account_name.string,
+ &ninfo.identity_info.domain_name.string);
+
+ generate_random_buffer(ninfo.challenge,
+ sizeof(ninfo.challenge));
+ chal = data_blob_const(ninfo.challenge,
+ sizeof(ninfo.challenge));
+
+ names_blob = NTLMv2_generate_names_blob(tctx, lp_iconv_convenience(tctx->lp_ctx), cli_credentials_get_workstation(credentials),
+ cli_credentials_get_domain(credentials));
+
+ status = cli_credentials_get_ntlm_response(cmdline_credentials, tctx,
+ &flags,
+ chal,
+ names_blob,
+ &lm_resp, &nt_resp,
+ NULL, NULL);
+ torture_assert_ntstatus_ok(tctx, status,
+ "cli_credentials_get_ntlm_response failed");
+
+ ninfo.lm.data = lm_resp.data;
+ ninfo.lm.length = lm_resp.length;
+
+ ninfo.nt.data = nt_resp.data;
+ ninfo.nt.length = nt_resp.length;
+
+ ninfo.identity_info.parameter_control = 0;
+ ninfo.identity_info.logon_id_low = 0;
+ ninfo.identity_info.logon_id_high = 0;
+ ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials);
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(credentials);
+ r.in.logon_level = 2;
+ r.in.logon.network = &ninfo;
+ r.in.flags = 0;
+
+ torture_comment(tctx,
+ "Testing LogonSamLogonEx with name %s\n",
+ ninfo.identity_info.account_name.string);
+
+ for (i=2;i<3;i++) {
+ r.in.validation_level = i;
+
+ status = dcerpc_netr_LogonSamLogonEx(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed");
+ }
+
+ return true;
+}
+
+/*
+ do some samr ops using the schannel connection
+ */
+static bool test_samr_ops(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct samr_GetDomPwInfo r;
+ struct samr_Connect connect;
+ struct samr_OpenDomain opendom;
+ int i;
+ struct lsa_String name;
+ struct policy_handle handle;
+ struct policy_handle domain_handle;
+
+ name.string = lp_workgroup(tctx->lp_ctx);
+ r.in.domain_name = &name;
+
+ connect.in.system_name = 0;
+ connect.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ connect.out.connect_handle = &handle;
+
+ printf("Testing Connect and OpenDomain on BUILTIN\n");
+
+ status = dcerpc_samr_Connect(p, tctx, &connect);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("Connect failed (expected, schannel mapped to anonymous): %s\n",
+ nt_errstr(status));
+ } else {
+ printf("Connect failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ } else {
+ opendom.in.connect_handle = &handle;
+ opendom.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ opendom.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32");
+ opendom.out.domain_handle = &domain_handle;
+
+ status = dcerpc_samr_OpenDomain(p, tctx, &opendom);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ return false;
+ }
+ }
+
+ printf("Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ /* do several ops to test credential chaining */
+ for (i=0;i<5;i++) {
+ status = dcerpc_samr_GetDomPwInfo(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("GetDomPwInfo op %d failed - %s\n", i, nt_errstr(status));
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+/*
+ do some lsa ops using the schannel connection
+ */
+static bool test_lsa_ops(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct lsa_GetUserName r;
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_StringPointer authority_name_p;
+
+ printf("\nTesting GetUserName\n");
+
+ r.in.system_name = "\\";
+ r.in.account_name = NULL;
+ r.in.authority_name = &authority_name_p;
+ authority_name_p.string = NULL;
+
+ /* do several ops to test credential chaining and various operations */
+ status = dcerpc_lsa_GetUserName(p, tctx, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ printf("not considering %s to be an error\n", nt_errstr(status));
+ } else if (!NT_STATUS_IS_OK(status)) {
+ printf("GetUserName failed - %s\n", nt_errstr(status));
+ return false;
+ } else {
+ if (!r.out.account_name) {
+ return false;
+ }
+
+ if (strcmp(r.out.account_name->string, "ANONYMOUS LOGON") != 0) {
+ printf("GetUserName returned wrong user: %s, expected %s\n",
+ r.out.account_name->string, "ANONYMOUS LOGON");
+ return false;
+ }
+ if (!r.out.authority_name || !r.out.authority_name->string) {
+ return false;
+ }
+
+ if (strcmp(r.out.authority_name->string->string, "NT AUTHORITY") != 0) {
+ printf("GetUserName returned wrong user: %s, expected %s\n",
+ r.out.authority_name->string->string, "NT AUTHORITY");
+ return false;
+ }
+ }
+ if (!test_many_LookupSids(p, tctx, NULL)) {
+ printf("LsaLookupSids3 failed!\n");
+ return false;
+ }
+
+ return ret;
+}
+
+
+/*
+ test a schannel connection with the given flags
+ */
+static bool test_schannel(struct torture_context *tctx,
+ uint16_t acct_flags, uint32_t dcerpc_flags,
+ int i)
+{
+ struct test_join *join_ctx;
+ NTSTATUS status;
+ const char *binding = torture_setting_string(tctx, "binding", NULL);
+ struct dcerpc_binding *b;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_pipe *p_netlogon = NULL;
+ struct dcerpc_pipe *p_netlogon2 = NULL;
+ struct dcerpc_pipe *p_netlogon3 = NULL;
+ struct dcerpc_pipe *p_samr2 = NULL;
+ struct dcerpc_pipe *p_lsa = NULL;
+ struct creds_CredentialState *creds;
+ struct cli_credentials *credentials;
+
+ join_ctx = torture_join_domain(tctx,
+ talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, i),
+ acct_flags, &credentials);
+ torture_assert(tctx, join_ctx != NULL, "Failed to join domain");
+
+ status = dcerpc_parse_binding(tctx, binding, &b);
+ torture_assert_ntstatus_ok(tctx, status, "Bad binding string");
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= dcerpc_flags;
+
+ status = dcerpc_pipe_connect_b(tctx, &p, b, &ndr_table_samr,
+ credentials, tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to connect with schannel");
+
+ torture_assert(tctx, test_samr_ops(tctx, p),
+ "Failed to process schannel secured SAMR ops");
+
+ /* Also test that when we connect to the netlogon pipe, that
+ * the credentials we setup on the first pipe are valid for
+ * the second */
+
+ /* Swap the binding details from SAMR to NETLOGON */
+ status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status, "epm map");
+
+ status = dcerpc_secondary_connection(p, &p_netlogon,
+ b);
+ torture_assert_ntstatus_ok(tctx, status, "seconday connection");
+
+ status = dcerpc_bind_auth(p_netlogon, &ndr_table_netlogon,
+ credentials, tctx->lp_ctx,
+ DCERPC_AUTH_TYPE_SCHANNEL,
+ dcerpc_auth_level(p->conn),
+ NULL);
+
+ torture_assert_ntstatus_ok(tctx, status, "bind auth");
+
+ status = dcerpc_schannel_creds(p_netlogon->conn->security_state.generic_state, tctx, &creds);
+ torture_assert_ntstatus_ok(tctx, status, "schannel creds");
+
+ /* do a couple of logins */
+ torture_assert(tctx, test_netlogon_ops(p_netlogon, tctx, credentials, creds),
+ "Failed to process schannel secured NETLOGON ops");
+
+ torture_assert(tctx, test_netlogon_ex_ops(p_netlogon, tctx, credentials, creds),
+ "Failed to process schannel secured NETLOGON EX ops");
+
+ /* Swap the binding details from SAMR to LSARPC */
+ status = dcerpc_epm_map_binding(tctx, b, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status, "epm map");
+
+ status = dcerpc_secondary_connection(p, &p_lsa,
+ b);
+
+ torture_assert_ntstatus_ok(tctx, status, "seconday connection");
+
+ status = dcerpc_bind_auth(p_lsa, &ndr_table_lsarpc,
+ credentials, tctx->lp_ctx,
+ DCERPC_AUTH_TYPE_SCHANNEL,
+ dcerpc_auth_level(p->conn),
+ NULL);
+
+ torture_assert_ntstatus_ok(tctx, status, "bind auth");
+
+ torture_assert(tctx, test_lsa_ops(tctx, p_lsa),
+ "Failed to process schannel secured LSA ops");
+
+ /* Drop the socket, we want to start from scratch */
+ talloc_free(p);
+ p = NULL;
+
+ /* Now see what we are still allowed to do */
+
+ status = dcerpc_parse_binding(tctx, binding, &b);
+ torture_assert_ntstatus_ok(tctx, status, "Bad binding string");
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= dcerpc_flags;
+
+ status = dcerpc_pipe_connect_b(tctx, &p_samr2, b, &ndr_table_samr,
+ credentials, tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to connect with schannel");
+
+ /* do a some SAMR operations. We have *not* done a new serverauthenticate */
+ torture_assert (tctx, test_samr_ops(tctx, p_samr2),
+ "Failed to process schannel secured SAMR ops (on fresh connection)");
+
+ /* Swap the binding details from SAMR to NETLOGON */
+ status = dcerpc_epm_map_binding(tctx, b, &ndr_table_netlogon, tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status, "epm");
+
+ status = dcerpc_secondary_connection(p_samr2, &p_netlogon2,
+ b);
+ torture_assert_ntstatus_ok(tctx, status, "seconday connection");
+
+ /* and now setup an SCHANNEL bind on netlogon */
+ status = dcerpc_bind_auth(p_netlogon2, &ndr_table_netlogon,
+ credentials, tctx->lp_ctx,
+ DCERPC_AUTH_TYPE_SCHANNEL,
+ dcerpc_auth_level(p_samr2->conn),
+ NULL);
+
+ torture_assert_ntstatus_ok(tctx, status, "auth failed");
+
+ /* Try the schannel-only SamLogonEx operation */
+ torture_assert(tctx, test_netlogon_ex_ops(p_netlogon2, tctx, credentials, creds),
+ "Failed to process schannel secured NETLOGON EX ops (on fresh connection)");
+
+
+ /* And the more traditional style, proving that the
+ * credentials chaining state is fully present */
+ torture_assert(tctx, test_netlogon_ops(p_netlogon2, tctx, credentials, creds),
+ "Failed to process schannel secured NETLOGON ops (on fresh connection)");
+
+ /* Drop the socket, we want to start from scratch (again) */
+ talloc_free(p_samr2);
+
+ /* We don't want schannel for this test */
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+
+ status = dcerpc_pipe_connect_b(tctx, &p_netlogon3, b, &ndr_table_netlogon,
+ credentials, tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to connect without schannel");
+
+ torture_assert(tctx, !test_netlogon_ex_ops(p_netlogon3, tctx, credentials, creds),
+ "Processed NOT schannel secured NETLOGON EX ops without SCHANNEL (unsafe)");
+
+ /* Required because the previous call will mark the current context as having failed */
+ tctx->last_result = TORTURE_OK;
+ tctx->last_reason = NULL;
+
+ torture_assert(tctx, test_netlogon_ops(p_netlogon3, tctx, credentials, creds),
+ "Failed to processed NOT schannel secured NETLOGON ops without new ServerAuth");
+
+ torture_leave_domain(tctx, join_ctx);
+ return true;
+}
+
+
+
+/*
+ a schannel test suite
+ */
+bool torture_rpc_schannel(struct torture_context *torture)
+{
+ bool ret = true;
+ struct {
+ uint16_t acct_flags;
+ uint32_t dcerpc_flags;
+ } tests[] = {
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128 },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 }
+ };
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(tests);i++) {
+ if (!test_schannel(torture,
+ tests[i].acct_flags, tests[i].dcerpc_flags,
+ i)) {
+ torture_comment(torture, "Failed with acct_flags=0x%x dcerpc_flags=0x%x \n",
+ tests[i].acct_flags, tests[i].dcerpc_flags);
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ test two schannel connections
+ */
+bool torture_rpc_schannel2(struct torture_context *torture)
+{
+ struct test_join *join_ctx;
+ NTSTATUS status;
+ const char *binding = torture_setting_string(torture, "binding", NULL);
+ struct dcerpc_binding *b;
+ struct dcerpc_pipe *p1 = NULL, *p2 = NULL;
+ struct cli_credentials *credentials1, *credentials2;
+ uint32_t dcerpc_flags = DCERPC_SCHANNEL | DCERPC_SIGN;
+
+ join_ctx = torture_join_domain(torture, talloc_asprintf(torture, "%s2", TEST_MACHINE_NAME),
+ ACB_WSTRUST, &credentials1);
+ torture_assert(torture, join_ctx != NULL,
+ "Failed to join domain with acct_flags=ACB_WSTRUST");
+
+ credentials2 = (struct cli_credentials *)talloc_memdup(torture, credentials1, sizeof(*credentials1));
+ credentials1->netlogon_creds = NULL;
+ credentials2->netlogon_creds = NULL;
+
+ status = dcerpc_parse_binding(torture, binding, &b);
+ torture_assert_ntstatus_ok(torture, status, "Bad binding string");
+
+ b->flags &= ~DCERPC_AUTH_OPTIONS;
+ b->flags |= dcerpc_flags;
+
+ printf("Opening first connection\n");
+ status = dcerpc_pipe_connect_b(torture, &p1, b, &ndr_table_netlogon,
+ credentials1, torture->ev, torture->lp_ctx);
+ torture_assert_ntstatus_ok(torture, status, "Failed to connect with schannel");
+
+ torture_comment(torture, "Opening second connection\n");
+ status = dcerpc_pipe_connect_b(torture, &p2, b, &ndr_table_netlogon,
+ credentials2, torture->ev, torture->lp_ctx);
+ torture_assert_ntstatus_ok(torture, status, "Failed to connect with schannel");
+
+ credentials1->netlogon_creds = NULL;
+ credentials2->netlogon_creds = NULL;
+
+ torture_comment(torture, "Testing logon on pipe1\n");
+ if (!test_netlogon_ex_ops(p1, torture, credentials1, NULL))
+ return false;
+
+ torture_comment(torture, "Testing logon on pipe2\n");
+ if (!test_netlogon_ex_ops(p2, torture, credentials2, NULL))
+ return false;
+
+ torture_comment(torture, "Again on pipe1\n");
+ if (!test_netlogon_ex_ops(p1, torture, credentials1, NULL))
+ return false;
+
+ torture_comment(torture, "Again on pipe2\n");
+ if (!test_netlogon_ex_ops(p2, torture, credentials2, NULL))
+ return false;
+
+ torture_leave_domain(torture, join_ctx);
+ return true;
+}
+
+struct torture_schannel_bench;
+
+struct torture_schannel_bench_conn {
+ struct torture_schannel_bench *s;
+ int index;
+ struct cli_credentials *wks_creds;
+ struct dcerpc_pipe *pipe;
+ struct netr_LogonSamLogonEx r;
+ struct netr_NetworkInfo ninfo;
+ TALLOC_CTX *tmp;
+ uint64_t total;
+ uint32_t count;
+};
+
+struct torture_schannel_bench {
+ struct torture_context *tctx;
+ bool progress;
+ int timelimit;
+ int nprocs;
+ int nconns;
+ struct torture_schannel_bench_conn *conns;
+ struct test_join *join_ctx1;
+ struct cli_credentials *wks_creds1;
+ struct test_join *join_ctx2;
+ struct cli_credentials *wks_creds2;
+ struct cli_credentials *user1_creds;
+ struct cli_credentials *user2_creds;
+ struct dcerpc_binding *b;
+ NTSTATUS error;
+ uint64_t total;
+ uint32_t count;
+ bool stopped;
+};
+
+static void torture_schannel_bench_connected(struct composite_context *c)
+{
+ struct torture_schannel_bench_conn *conn =
+ (struct torture_schannel_bench_conn *)c->async.private_data;
+ struct torture_schannel_bench *s = talloc_get_type(conn->s,
+ struct torture_schannel_bench);
+
+ s->error = dcerpc_pipe_connect_b_recv(c, s->conns, &conn->pipe);
+ torture_comment(s->tctx, "conn[%u]: %s\n", conn->index, nt_errstr(s->error));
+ if (NT_STATUS_IS_OK(s->error)) {
+ s->nconns++;
+ }
+}
+
+static void torture_schannel_bench_recv(struct rpc_request *req);
+
+static bool torture_schannel_bench_start(struct torture_schannel_bench_conn *conn)
+{
+ struct torture_schannel_bench *s = conn->s;
+ NTSTATUS status;
+ DATA_BLOB names_blob, chal, lm_resp, nt_resp;
+ int flags = CLI_CRED_NTLM_AUTH;
+ struct rpc_request *req;
+ struct cli_credentials *user_creds;
+
+ if (conn->total % 2) {
+ user_creds = s->user1_creds;
+ } else {
+ user_creds = s->user2_creds;
+ }
+
+ if (lp_client_lanman_auth(s->tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lp_client_ntlmv2_auth(s->tctx->lp_ctx)) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ talloc_free(conn->tmp);
+ conn->tmp = talloc_new(s);
+ ZERO_STRUCT(conn->ninfo);
+ ZERO_STRUCT(conn->r);
+
+ cli_credentials_get_ntlm_username_domain(user_creds, conn->tmp,
+ &conn->ninfo.identity_info.account_name.string,
+ &conn->ninfo.identity_info.domain_name.string);
+
+ generate_random_buffer(conn->ninfo.challenge,
+ sizeof(conn->ninfo.challenge));
+ chal = data_blob_const(conn->ninfo.challenge,
+ sizeof(conn->ninfo.challenge));
+
+ names_blob = NTLMv2_generate_names_blob(conn->tmp, lp_iconv_convenience(s->tctx->lp_ctx),
+ cli_credentials_get_workstation(conn->wks_creds),
+ cli_credentials_get_domain(conn->wks_creds));
+
+ status = cli_credentials_get_ntlm_response(user_creds, conn->tmp,
+ &flags,
+ chal,
+ names_blob,
+ &lm_resp, &nt_resp,
+ NULL, NULL);
+ torture_assert_ntstatus_ok(s->tctx, status,
+ "cli_credentials_get_ntlm_response failed");
+
+ conn->ninfo.lm.data = lm_resp.data;
+ conn->ninfo.lm.length = lm_resp.length;
+
+ conn->ninfo.nt.data = nt_resp.data;
+ conn->ninfo.nt.length = nt_resp.length;
+
+ conn->ninfo.identity_info.parameter_control = 0;
+ conn->ninfo.identity_info.logon_id_low = 0;
+ conn->ninfo.identity_info.logon_id_high = 0;
+ conn->ninfo.identity_info.workstation.string = cli_credentials_get_workstation(conn->wks_creds);
+
+ conn->r.in.server_name = talloc_asprintf(conn->tmp, "\\\\%s", dcerpc_server_name(conn->pipe));
+ conn->r.in.computer_name = cli_credentials_get_workstation(conn->wks_creds);
+ conn->r.in.logon_level = 2;
+ conn->r.in.logon.network = &conn->ninfo;
+ conn->r.in.flags = 0;
+ conn->r.in.validation_level = 2;
+
+ req = dcerpc_netr_LogonSamLogonEx_send(conn->pipe, conn->tmp, &conn->r);
+ torture_assert(s->tctx, req, "Failed to setup LogonSamLogonEx request");
+
+ req->async.callback = torture_schannel_bench_recv;
+ req->async.private_data = conn;
+
+ return true;
+}
+
+static void torture_schannel_bench_recv(struct rpc_request *req)
+{
+ bool ret;
+ struct torture_schannel_bench_conn *conn =
+ (struct torture_schannel_bench_conn *)req->async.private_data;
+ struct torture_schannel_bench *s = talloc_get_type(conn->s,
+ struct torture_schannel_bench);
+
+ s->error = dcerpc_ndr_request_recv(req);
+ if (!NT_STATUS_IS_OK(s->error)) {
+ return;
+ }
+
+ conn->total++;
+ conn->count++;
+
+ if (s->stopped) {
+ return;
+ }
+
+ ret = torture_schannel_bench_start(conn);
+ if (!ret) {
+ s->error = NT_STATUS_INTERNAL_ERROR;
+ }
+}
+
+/*
+ test multiple schannel connection in parallel
+ */
+bool torture_rpc_schannel_bench1(struct torture_context *torture)
+{
+ bool ret = true;
+ NTSTATUS status;
+ const char *binding = torture_setting_string(torture, "binding", NULL);
+ struct torture_schannel_bench *s;
+ struct timeval start;
+ struct timeval end;
+ int i;
+ const char *tmp;
+
+ s = talloc_zero(torture, struct torture_schannel_bench);
+ s->tctx = torture;
+ s->progress = torture_setting_bool(torture, "progress", true);
+ s->timelimit = torture_setting_int(torture, "timelimit", 10);
+ s->nprocs = torture_setting_int(torture, "nprocs", 4);
+ s->conns = talloc_zero_array(s, struct torture_schannel_bench_conn, s->nprocs);
+
+ s->user1_creds = (struct cli_credentials *)talloc_memdup(s,
+ cmdline_credentials,
+ sizeof(*s->user1_creds));
+ tmp = torture_setting_string(s->tctx, "extra_user1", NULL);
+ if (tmp) {
+ cli_credentials_parse_string(s->user1_creds, tmp, CRED_SPECIFIED);
+ }
+ s->user2_creds = (struct cli_credentials *)talloc_memdup(s,
+ cmdline_credentials,
+ sizeof(*s->user1_creds));
+ tmp = torture_setting_string(s->tctx, "extra_user2", NULL);
+ if (tmp) {
+ cli_credentials_parse_string(s->user1_creds, tmp, CRED_SPECIFIED);
+ }
+
+ s->join_ctx1 = torture_join_domain(s->tctx, talloc_asprintf(s, "%sb", TEST_MACHINE_NAME),
+ ACB_WSTRUST, &s->wks_creds1);
+ torture_assert(torture, s->join_ctx1 != NULL,
+ "Failed to join domain with acct_flags=ACB_WSTRUST");
+ s->join_ctx2 = torture_join_domain(s->tctx, talloc_asprintf(s, "%sc", TEST_MACHINE_NAME),
+ ACB_WSTRUST, &s->wks_creds2);
+ torture_assert(torture, s->join_ctx2 != NULL,
+ "Failed to join domain with acct_flags=ACB_WSTRUST");
+
+ cli_credentials_set_kerberos_state(s->wks_creds1, CRED_DONT_USE_KERBEROS);
+ cli_credentials_set_kerberos_state(s->wks_creds2, CRED_DONT_USE_KERBEROS);
+
+ for (i=0; i < s->nprocs; i++) {
+ s->conns[i].s = s;
+ s->conns[i].index = i;
+ s->conns[i].wks_creds = (struct cli_credentials *)talloc_memdup(
+ s->conns, s->wks_creds1,sizeof(*s->wks_creds1));
+ if ((i % 2) && (torture_setting_bool(torture, "multijoin", false))) {
+ memcpy(s->conns[i].wks_creds, s->wks_creds2,
+ talloc_get_size(s->conns[i].wks_creds));
+ }
+ s->conns[i].wks_creds->netlogon_creds = NULL;
+ }
+
+ status = dcerpc_parse_binding(s, binding, &s->b);
+ torture_assert_ntstatus_ok(torture, status, "Bad binding string");
+ s->b->flags &= ~DCERPC_AUTH_OPTIONS;
+ s->b->flags |= DCERPC_SCHANNEL | DCERPC_SIGN;
+
+ torture_comment(torture, "Opening %d connections in parallel\n", s->nprocs);
+ for (i=0; i < s->nprocs; i++) {
+#if 1
+ s->error = dcerpc_pipe_connect_b(s->conns, &s->conns[i].pipe, s->b,
+ &ndr_table_netlogon,
+ s->conns[i].wks_creds,
+ torture->ev, torture->lp_ctx);
+ torture_assert_ntstatus_ok(torture, s->error, "Failed to connect with schannel");
+#else
+ /*
+ * This path doesn't work against windows,
+ * because of windows drops the connections
+ * which haven't reached a session setup yet
+ *
+ * The same as the reset on zero vc stuff.
+ */
+ struct composite_context *c;
+ c = dcerpc_pipe_connect_b_send(s->conns, s->b,
+ &ndr_table_netlogon,
+ s->conns[i].wks_creds,
+ torture->ev,
+ torture->lp_ctx);
+ torture_assert(torture, c != NULL, "Failed to setup connect");
+ c->async.fn = torture_schannel_bench_connected;
+ c->async.private_data = &s->conns[i];
+ }
+
+ while (NT_STATUS_IS_OK(s->error) && s->nprocs != s->nconns) {
+ int ev_ret = event_loop_once(torture->ev);
+ torture_assert(torture, ev_ret == 0, "event_loop_once failed");
+#endif
+ }
+ torture_assert_ntstatus_ok(torture, s->error, "Failed establish a connect");
+
+ /*
+ * Change the workstation password after establishing the netlogon
+ * schannel connections to prove that existing connections are not
+ * affected by a wks pwchange.
+ */
+
+ {
+ struct netr_ServerPasswordSet pwset;
+ char *password = generate_random_str(s->join_ctx1, 8);
+ struct creds_CredentialState *creds_state;
+ struct dcerpc_pipe *net_pipe;
+
+ status = dcerpc_pipe_connect_b(s, &net_pipe, s->b,
+ &ndr_table_netlogon,
+ s->wks_creds1,
+ torture->ev, torture->lp_ctx);
+
+ torture_assert_ntstatus_ok(torture, status,
+ "dcerpc_pipe_connect_b failed");
+
+ pwset.in.server_name = talloc_asprintf(
+ net_pipe, "\\\\%s", dcerpc_server_name(net_pipe));
+ pwset.in.computer_name =
+ cli_credentials_get_workstation(s->wks_creds1);
+ pwset.in.account_name = talloc_asprintf(
+ net_pipe, "%s$", pwset.in.computer_name);
+ pwset.in.secure_channel_type = SEC_CHAN_WKSTA;
+ E_md4hash(password, pwset.in.new_password.hash);
+
+ creds_state = cli_credentials_get_netlogon_creds(
+ s->wks_creds1);
+ creds_des_encrypt(creds_state, &pwset.in.new_password);
+ creds_client_authenticator(creds_state, &pwset.in.credential);
+
+ status = dcerpc_netr_ServerPasswordSet(net_pipe, torture, &pwset);
+ torture_assert_ntstatus_ok(torture, status,
+ "ServerPasswordSet failed");
+
+ if (!creds_client_check(creds_state,
+ &pwset.out.return_authenticator.cred)) {
+ printf("Credential chaining failed\n");
+ }
+
+ cli_credentials_set_password(s->wks_creds1, password,
+ CRED_SPECIFIED);
+
+ talloc_free(net_pipe);
+
+ /* Just as a test, connect with the new creds */
+
+ talloc_free(s->wks_creds1->netlogon_creds);
+ s->wks_creds1->netlogon_creds = NULL;
+
+ status = dcerpc_pipe_connect_b(s, &net_pipe, s->b,
+ &ndr_table_netlogon,
+ s->wks_creds1,
+ torture->ev, torture->lp_ctx);
+
+ torture_assert_ntstatus_ok(torture, status,
+ "dcerpc_pipe_connect_b failed");
+
+ talloc_free(net_pipe);
+ }
+
+ torture_comment(torture, "Start looping LogonSamLogonEx on %d connections for %d secs\n",
+ s->nprocs, s->timelimit);
+ for (i=0; i < s->nprocs; i++) {
+ ret = torture_schannel_bench_start(&s->conns[i]);
+ torture_assert(torture, ret, "Failed to setup LogonSamLogonEx");
+ }
+
+ start = timeval_current();
+ end = timeval_add(&start, s->timelimit, 0);
+
+ while (NT_STATUS_IS_OK(s->error) && !timeval_expired(&end)) {
+ int ev_ret = event_loop_once(torture->ev);
+ torture_assert(torture, ev_ret == 0, "event_loop_once failed");
+ }
+ torture_assert_ntstatus_ok(torture, s->error, "Failed some request");
+ s->stopped = true;
+ talloc_free(s->conns);
+
+ for (i=0; i < s->nprocs; i++) {
+ s->total += s->conns[i].total;
+ }
+
+ torture_comment(torture,
+ "Total ops[%llu] (%u ops/s)\n",
+ (unsigned long long)s->total,
+ (unsigned)s->total/s->timelimit);
+
+ torture_leave_domain(torture, s->join_ctx1);
+ torture_leave_domain(torture, s->join_ctx2);
+ return true;
+}
diff --git a/source4/torture/rpc/session_key.c b/source4/torture/rpc/session_key.c
new file mode 100644
index 0000000000..c069c62724
--- /dev/null
+++ b/source4/torture/rpc/session_key.c
@@ -0,0 +1,233 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for lsa rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/rpc.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
+
+static void init_lsa_String(struct lsa_String *name, const char *s)
+{
+ name->string = s;
+}
+
+static bool test_CreateSecret_basic(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct lsa_CreateSecret r;
+ struct lsa_SetSecret r3;
+ struct lsa_QuerySecret r4;
+ struct policy_handle sec_handle;
+ struct lsa_DeleteObject d;
+ struct lsa_DATA_BUF buf1;
+ struct lsa_DATA_BUF_PTR bufp1;
+ DATA_BLOB enc_key;
+ DATA_BLOB session_key;
+ NTTIME old_mtime, new_mtime;
+ DATA_BLOB blob1, blob2;
+ const char *secret1 = "abcdef12345699qwerty";
+ char *secret2;
+ char *secname;
+
+ secname = talloc_asprintf(tctx, "torturesecret-%u", (uint_t)random());
+
+ torture_comment(tctx, "Testing CreateSecret of %s\n", secname);
+
+ init_lsa_String(&r.in.name, secname);
+
+ r.in.handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.sec_handle = &sec_handle;
+
+ status = dcerpc_lsa_CreateSecret(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "CreateSecret failed");
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_fetch_session_key failed");
+
+ enc_key = sess_encrypt_string(secret1, &session_key);
+
+ r3.in.sec_handle = &sec_handle;
+ r3.in.new_val = &buf1;
+ r3.in.old_val = NULL;
+ r3.in.new_val->data = enc_key.data;
+ r3.in.new_val->length = enc_key.length;
+ r3.in.new_val->size = enc_key.length;
+
+ torture_comment(tctx, "Testing SetSecret\n");
+
+ status = dcerpc_lsa_SetSecret(p, tctx, &r3);
+ torture_assert_ntstatus_ok(tctx, status, "SetSecret failed");
+
+ r3.in.sec_handle = &sec_handle;
+ r3.in.new_val = &buf1;
+ r3.in.old_val = NULL;
+ r3.in.new_val->data = enc_key.data;
+ r3.in.new_val->length = enc_key.length;
+ r3.in.new_val->size = enc_key.length;
+
+ /* break the encrypted data */
+ enc_key.data[0]++;
+
+ torture_comment(tctx, "Testing SetSecret with broken key\n");
+
+ status = dcerpc_lsa_SetSecret(p, tctx, &r3);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_UNKNOWN_REVISION,
+ "SetSecret should have failed UNKNOWN_REVISION");
+
+ data_blob_free(&enc_key);
+
+ ZERO_STRUCT(new_mtime);
+ ZERO_STRUCT(old_mtime);
+
+ /* fetch the secret back again */
+ r4.in.sec_handle = &sec_handle;
+ r4.in.new_val = &bufp1;
+ r4.in.new_mtime = &new_mtime;
+ r4.in.old_val = NULL;
+ r4.in.old_mtime = NULL;
+
+ bufp1.buf = NULL;
+
+ torture_comment(tctx, "Testing QuerySecret\n");
+ status = dcerpc_lsa_QuerySecret(p, tctx, &r4);
+ torture_assert_ntstatus_ok(tctx, status, "QuerySecret failed");
+ if (r4.out.new_val == NULL || r4.out.new_val->buf == NULL)
+ torture_fail(tctx, "No secret buffer returned");
+ blob1.data = r4.out.new_val->buf->data;
+ blob1.length = r4.out.new_val->buf->size;
+
+ blob2 = data_blob_talloc(tctx, NULL, blob1.length);
+
+ secret2 = sess_decrypt_string(tctx, &blob1, &session_key);
+
+ torture_assert_str_equal(tctx, secret1, secret2, "Returned secret invalid");
+
+ d.in.handle = &sec_handle;
+ d.out.handle = &sec_handle;
+ status = dcerpc_lsa_DeleteObject(p, tctx, &d);
+ torture_assert_ntstatus_ok(tctx, status, "delete should have returned OKINVALID_HANDLE");
+ return true;
+}
+
+struct secret_settings {
+ uint32_t bindoptions;
+ bool keyexchange;
+ bool ntlm2;
+ bool lm_key;
+};
+
+static bool test_secrets(struct torture_context *torture, const void *_data)
+{
+ struct dcerpc_pipe *p;
+ struct policy_handle *handle;
+ struct dcerpc_binding *binding;
+ const struct secret_settings *settings =
+ (const struct secret_settings *)_data;
+
+ lp_set_cmdline(torture->lp_ctx, "ntlmssp client:keyexchange", settings->keyexchange?"True":"False");
+ lp_set_cmdline(torture->lp_ctx, "ntlmssp_client:ntlm2", settings->ntlm2?"True":"False");
+ lp_set_cmdline(torture->lp_ctx, "ntlmssp_client:lm_key", settings->lm_key?"True":"False");
+
+ torture_assert_ntstatus_ok(torture, torture_rpc_binding(torture, &binding),
+ "Getting bindoptions");
+
+ binding->flags |= settings->bindoptions;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_pipe_connect_b(torture, &p, binding,
+ &ndr_table_lsarpc,
+ cmdline_credentials,
+ torture->ev,
+ torture->lp_ctx),
+ "connect");
+
+ if (!test_lsa_OpenPolicy2(p, torture, &handle)) {
+ return false;
+ }
+
+ torture_assert(torture, handle, "OpenPolicy2 failed. This test cannot run against this server");
+
+ if (!test_CreateSecret_basic(p, torture, handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static struct torture_tcase *add_test(struct torture_suite *suite, uint32_t bindoptions,
+ bool keyexchange, bool ntlm2, bool lm_key)
+{
+ char *name = NULL;
+ struct secret_settings *settings;
+
+ settings = talloc_zero(suite, struct secret_settings);
+ settings->bindoptions = bindoptions;
+
+ if (bindoptions == DCERPC_PUSH_BIGENDIAN)
+ name = talloc_strdup(suite, "bigendian");
+ else if (bindoptions == DCERPC_SEAL)
+ name = talloc_strdup(suite, "seal");
+ else if (bindoptions == 0)
+ name = talloc_strdup(suite, "none");
+ else
+ name = talloc_strdup(suite, "unknown");
+
+ name = talloc_asprintf_append_buffer(name, " keyexchange:%s", keyexchange?"yes":"no");
+ settings->keyexchange = keyexchange;
+
+ name = talloc_asprintf_append_buffer(name, " ntlm2:%s", ntlm2?"yes":"no");
+ settings->ntlm2 = ntlm2;
+
+ name = talloc_asprintf_append_buffer(name, " lm_key:%s", lm_key?"yes":"no");
+ settings->lm_key = lm_key;
+
+ return torture_suite_add_simple_tcase_const(suite, name, test_secrets,
+ settings);
+}
+
+static const bool bool_vals[] = { true, false };
+
+/* TEST session key correctness by pushing and pulling secrets */
+struct torture_suite *torture_rpc_lsa_secrets(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SECRETS");
+ int keyexchange, ntlm2, lm_key;
+
+ for (keyexchange = 0; keyexchange < ARRAY_SIZE(bool_vals); keyexchange++) {
+ for (ntlm2 = 0; ntlm2 < ARRAY_SIZE(bool_vals); ntlm2++) {
+ for (lm_key = 0; lm_key < ARRAY_SIZE(bool_vals); lm_key++) {
+ add_test(suite, DCERPC_PUSH_BIGENDIAN, bool_vals[keyexchange], bool_vals[ntlm2],
+ bool_vals[lm_key]);
+ add_test(suite, DCERPC_SEAL, bool_vals[keyexchange], bool_vals[ntlm2], bool_vals[lm_key]);
+ add_test(suite, 0, bool_vals[keyexchange], bool_vals[ntlm2], bool_vals[lm_key]);
+ }
+ }
+ }
+
+ return suite;
+}
diff --git a/source4/torture/rpc/spoolss.c b/source4/torture/rpc/spoolss.c
new file mode 100644
index 0000000000..4a6ff480c4
--- /dev/null
+++ b/source4/torture/rpc/spoolss.c
@@ -0,0 +1,1769 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for spoolss rpc operations
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Stefan Metzmacher 2005
+ Copyright (C) Jelmer Vernooij 2007
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+
+struct test_spoolss_context {
+ /* print server handle */
+ struct policy_handle server_handle;
+
+ /* for EnumPorts */
+ uint32_t port_count[3];
+ union spoolss_PortInfo *ports[3];
+
+ /* for EnumPrinterDrivers */
+ uint32_t driver_count[7];
+ union spoolss_DriverInfo *drivers[7];
+
+ /* for EnumMonitors */
+ uint32_t monitor_count[3];
+ union spoolss_MonitorInfo *monitors[3];
+
+ /* for EnumPrintProcessors */
+ uint32_t print_processor_count[2];
+ union spoolss_PrintProcessorInfo *print_processors[2];
+
+ /* for EnumPrinters */
+ uint32_t printer_count[6];
+ union spoolss_PrinterInfo *printers[6];
+};
+
+#define COMPARE_STRING(tctx, c,r,e) \
+ torture_assert_str_equal(tctx, c.e, r.e, "invalid value")
+
+/* not every compiler supports __typeof__() */
+#if (__GNUC__ >= 3)
+#define _CHECK_FIELD_SIZE(c,r,e,type) do {\
+ if (sizeof(__typeof__(c.e)) != sizeof(type)) { \
+ torture_fail(tctx, #c "." #e "field is not " #type "\n"); \
+ }\
+ if (sizeof(__typeof__(r.e)) != sizeof(type)) { \
+ torture_fail(tctx, #r "." #e "field is not " #type "\n"); \
+ }\
+} while(0)
+#else
+#define _CHECK_FIELD_SIZE(c,r,e,type) do {} while(0)
+#endif
+
+#define COMPARE_UINT32(tctx, c, r, e) do {\
+ _CHECK_FIELD_SIZE(c, r, e, uint32_t); \
+ torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \
+} while(0)
+
+#define COMPARE_STRING_ARRAY(tctx, c,r,e)
+
+static bool test_OpenPrinter_server(struct torture_context *tctx, struct dcerpc_pipe *p, struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter op;
+
+ op.in.printername = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p));
+ op.in.datatype = NULL;
+ op.in.devmode_ctr.devmode= NULL;
+ op.in.access_mask = 0;
+ op.out.handle = &ctx->server_handle;
+
+ torture_comment(tctx, "Testing OpenPrinter(%s)\n", op.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter(p, ctx, &op);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_OpenPrinter failed");
+ torture_assert_werr_ok(tctx, op.out.result, "dcerpc_spoolss_OpenPrinter failed");
+
+ return true;
+}
+
+static bool test_EnumPorts(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPorts r;
+ uint16_t levels[] = { 1, 2 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+
+ r.in.servername = "";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumPorts level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPorts(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPorts failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPorts unexpected return code");
+
+ blob = data_blob_talloc(ctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumPorts(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPorts failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
+
+ ctx->port_count[level] = r.out.count;
+ ctx->ports[level] = r.out.info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->port_count[level], ctx->port_count[old_level],
+ "EnumPorts invalid value");
+ }
+ /* if the array sizes are not the same we would maybe segfault in the following code */
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->port_count[level];j++) {
+ union spoolss_PortInfo *cur = &ctx->ports[level][j];
+ union spoolss_PortInfo *ref = &ctx->ports[2][j];
+ switch (level) {
+ case 1:
+ COMPARE_STRING(tctx, cur->info1, ref->info2, port_name);
+ break;
+ case 2:
+ /* level 2 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterDriverDirectory r;
+ struct {
+ uint16_t level;
+ const char *server;
+ } levels[] = {{
+ .level = 1,
+ .server = NULL
+ },{
+ .level = 1,
+ .server = ""
+ },{
+ .level = 78,
+ .server = ""
+ },{
+ .level = 1,
+ .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p))
+ },{
+ .level = 1024,
+ .server = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p))
+ }
+ };
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i].level;
+ DATA_BLOB blob;
+
+ r.in.server = levels[i].server;
+ r.in.environment = SPOOLSS_ARCHITECTURE_NT_X86;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing GetPrinterDriverDirectory level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_GetPrinterDriverDirectory(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_GetPrinterDriverDirectory failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "GetPrinterDriverDirectory unexpected return code");
+
+ blob = data_blob_talloc(ctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_GetPrinterDriverDirectory(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrinterDriverDirectory failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDriverDirectory failed");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterDrivers(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterDrivers r;
+ uint16_t levels[] = { 1, 2, 3, 4, 5, 6 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+
+ r.in.server = "";
+ r.in.environment = SPOOLSS_ARCHITECTURE_NT_X86;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumPrinterDrivers level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinterDrivers(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_EnumPrinterDrivers failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPrinterDrivers failed");
+
+ blob = data_blob_talloc(ctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumPrinterDrivers(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinterDrivers failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDrivers failed");
+
+ ctx->driver_count[level] = r.out.count;
+ ctx->drivers[level] = r.out.info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->driver_count[level], ctx->driver_count[old_level],
+ "EnumPrinterDrivers invalid value");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->driver_count[level];j++) {
+ union spoolss_DriverInfo *cur = &ctx->drivers[level][j];
+ union spoolss_DriverInfo *ref = &ctx->drivers[6][j];
+ switch (level) {
+ case 1:
+ COMPARE_STRING(tctx, cur->info1, ref->info6, driver_name);
+ break;
+ case 2:
+ COMPARE_UINT32(tctx, cur->info2, ref->info6, version);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, driver_name);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, architecture);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, driver_path);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, data_file);
+ COMPARE_STRING(tctx, cur->info2, ref->info6, config_file);
+ break;
+ case 3:
+ COMPARE_UINT32(tctx, cur->info3, ref->info6, version);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, driver_name);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, architecture);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, driver_path);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, data_file);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, config_file);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info3, ref->info6, dependent_files);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, monitor_name);
+ COMPARE_STRING(tctx, cur->info3, ref->info6, default_datatype);
+ break;
+ case 4:
+ COMPARE_UINT32(tctx, cur->info4, ref->info6, version);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, driver_name);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, architecture);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, driver_path);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, data_file);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, config_file);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info6, dependent_files);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, monitor_name);
+ COMPARE_STRING(tctx, cur->info4, ref->info6, default_datatype);
+ COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info6, previous_names);
+ break;
+ case 5:
+ COMPARE_UINT32(tctx, cur->info5, ref->info6, version);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, driver_name);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, architecture);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, driver_path);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, data_file);
+ COMPARE_STRING(tctx, cur->info5, ref->info6, config_file);
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info6, driver_attributes);*/
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info6, config_version);*/
+ /*TODO: ! COMPARE_UINT32(tctx, cur->info5, ref->info6, driver_version); */
+ break;
+ case 6:
+ /* level 6 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumMonitors(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumMonitors r;
+ uint16_t levels[] = { 1, 2 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+
+ r.in.servername = "";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumMonitors level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumMonitors(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumMonitors failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumMonitors failed");
+
+ blob = data_blob_talloc(ctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumMonitors(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumMonitors failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumMonitors failed");
+
+ ctx->monitor_count[level] = r.out.count;
+ ctx->monitors[level] = r.out.info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->monitor_count[level], ctx->monitor_count[old_level],
+ "EnumMonitors invalid value");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->monitor_count[level];j++) {
+ union spoolss_MonitorInfo *cur = &ctx->monitors[level][j];
+ union spoolss_MonitorInfo *ref = &ctx->monitors[2][j];
+ switch (level) {
+ case 1:
+ COMPARE_STRING(tctx, cur->info1, ref->info2, monitor_name);
+ break;
+ case 2:
+ /* level 2 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessors(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrintProcessors r;
+ uint16_t levels[] = { 1 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+
+ r.in.servername = "";
+ r.in.environment = "Windows NT x86";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumPrintProcessors level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrintProcessors(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPrintProcessors unexpected return code");
+
+ blob = data_blob_talloc(ctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumPrintProcessors(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrintProcessors failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrintProcessors failed");
+
+ ctx->print_processor_count[level] = r.out.count;
+ ctx->print_processors[level] = r.out.info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->print_processor_count[level], ctx->print_processor_count[old_level],
+ "EnumPrintProcessors failed");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->print_processor_count[level];j++) {
+#if 0
+ union spoolss_PrintProcessorInfo *cur = &ctx->print_processors[level][j];
+ union spoolss_PrintProcessorInfo *ref = &ctx->print_processors[1][j];
+#endif
+ switch (level) {
+ case 1:
+ /* level 1 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinters(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_context *ctx)
+{
+ struct spoolss_EnumPrinters r;
+ NTSTATUS status;
+ uint16_t levels[] = { 0, 1, 2, 4, 5 };
+ int i, j;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ DATA_BLOB blob;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = "";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinters(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinters failed");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ /* TODO: do some more checks here */
+ continue;
+ }
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPrinters unexpected return code");
+
+ blob = data_blob_talloc(ctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumPrinters(p, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinters failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ ctx->printer_count[level] = r.out.count;
+ ctx->printers[level] = r.out.info;
+ }
+
+ for (i=1;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ int old_level = levels[i-1];
+ torture_assert_int_equal(tctx, ctx->printer_count[level], ctx->printer_count[old_level],
+ "EnumPrinters invalid value");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ for (j=0;j<ctx->printer_count[level];j++) {
+ union spoolss_PrinterInfo *cur = &ctx->printers[level][j];
+ union spoolss_PrinterInfo *ref = &ctx->printers[2][j];
+ switch (level) {
+ case 0:
+ COMPARE_STRING(tctx, cur->info0, ref->info2, printername);
+ COMPARE_STRING(tctx, cur->info0, ref->info2, servername);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, cjobs);
+ /*COMPARE_UINT32(tctx, cur->info0, ref->info2, total_jobs);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, total_bytes);
+ COMPARE_SPOOLSS_TIME(cur->info0, ref->info2, spoolss_Time time);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, global_counter);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, total_pages);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, version);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown10);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown11);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown12);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, session_counter);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown14);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, printer_errors);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown16);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown17);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown18);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown19);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, change_id);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown21);*/
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, status);
+ /*COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown23);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, c_setprinter);
+ COMPARE_UINT16(cur->info0, ref->info2, unknown25);
+ COMPARE_UINT16(cur->info0, ref->info2, unknown26);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown27);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown28);
+ COMPARE_UINT32(tctx, cur->info0, ref->info2, unknown29);*/
+ break;
+ case 1:
+ /*COMPARE_UINT32(tctx, cur->info1, ref->info2, flags);*/
+ /*COMPARE_STRING(tctx, cur->info1, ref->info2, name);*/
+ /*COMPARE_STRING(tctx, cur->info1, ref->info2, description);*/
+ COMPARE_STRING(tctx, cur->info1, ref->info2, comment);
+ break;
+ case 2:
+ /* level 2 is our reference, and it makes no sense to compare it to itself */
+ break;
+ case 4:
+ COMPARE_STRING(tctx, cur->info4, ref->info2, printername);
+ COMPARE_STRING(tctx, cur->info4, ref->info2, servername);
+ COMPARE_UINT32(tctx, cur->info4, ref->info2, attributes);
+ break;
+ case 5:
+ COMPARE_STRING(tctx, cur->info5, ref->info2, printername);
+ COMPARE_STRING(tctx, cur->info5, ref->info2, portname);
+ COMPARE_UINT32(tctx, cur->info5, ref->info2, attributes);
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info2, device_not_selected_timeout);
+ COMPARE_UINT32(tctx, cur->info5, ref->info2, transmission_retry_timeout);*/
+ break;
+ }
+ }
+ }
+
+ /* TODO:
+ * - verify that the port of a printer was in the list returned by EnumPorts
+ */
+
+ return true;
+}
+
+static bool test_GetPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinter r;
+ uint16_t levels[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.handle = handle;
+ r.in.level = levels[i];
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing GetPrinter level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_GetPrinter(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+ status = dcerpc_spoolss_GetPrinter(p, tctx, &r);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinter failed");
+ }
+
+ return true;
+}
+
+
+static bool test_ClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_ClosePrinter r;
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+
+ torture_comment(tctx, "Testing ClosePrinter\n");
+
+ status = dcerpc_spoolss_ClosePrinter(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+
+ return true;
+}
+
+static bool test_GetForm(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *form_name)
+{
+ NTSTATUS status;
+ struct spoolss_GetForm r;
+
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing GetForm\n");
+
+ status = dcerpc_spoolss_GetForm(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetForm failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+ status = dcerpc_spoolss_GetForm(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetForm failed");
+
+ torture_assert(tctx, r.out.info, "No form info returned");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetForm failed");
+
+ return true;
+}
+
+static bool test_EnumForms(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle, bool print_server)
+{
+ NTSTATUS status;
+ struct spoolss_EnumForms r;
+ bool ret = true;
+
+ r.in.handle = handle;
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumForms\n");
+
+ status = dcerpc_spoolss_EnumForms(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+
+ if (print_server && W_ERROR_EQUAL(r.out.result, WERR_BADFID))
+ torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ union spoolss_FormInfo *info;
+ int j;
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumForms(p, tctx, &r);
+
+ torture_assert(tctx, r.out.info, "No forms returned");
+
+ info = r.out.info;
+
+ for (j = 0; j < r.out.count; j++) {
+ if (!print_server)
+ ret &= test_GetForm(tctx, p, handle, info[j].info1.form_name);
+ }
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed");
+
+ return true;
+}
+
+static bool test_DeleteForm(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *form_name)
+{
+ NTSTATUS status;
+ struct spoolss_DeleteForm r;
+
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+
+ status = dcerpc_spoolss_DeleteForm(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "DeleteForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "DeleteForm failed");
+
+ return true;
+}
+
+static bool test_AddForm(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle, bool print_server)
+{
+ struct spoolss_AddForm r;
+ struct spoolss_AddFormInfo1 addform;
+ const char *form_name = "testform3";
+ NTSTATUS status;
+ bool ret = true;
+
+ r.in.handle = handle;
+ r.in.level = 1;
+ r.in.info.info1 = &addform;
+ addform.flags = SPOOLSS_FORM_USER;
+ addform.form_name = form_name;
+ addform.size.width = 50;
+ addform.size.height = 25;
+ addform.area.left = 5;
+ addform.area.top = 10;
+ addform.area.right = 45;
+ addform.area.bottom = 15;
+
+ status = dcerpc_spoolss_AddForm(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "AddForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "AddForm failed");
+
+ if (!print_server) ret &= test_GetForm(tctx, p, handle, form_name);
+
+ {
+ struct spoolss_SetForm sf;
+ struct spoolss_AddFormInfo1 setform;
+
+ sf.in.handle = handle;
+ sf.in.form_name = form_name;
+ sf.in.level = 1;
+ sf.in.info.info1= &setform;
+ setform.flags = addform.flags;
+ setform.form_name = addform.form_name;
+ setform.size = addform.size;
+ setform.area = addform.area;
+
+ setform.size.width = 1234;
+
+ status = dcerpc_spoolss_SetForm(p, tctx, &sf);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "SetForm failed");
+ }
+
+ if (!print_server) ret &= test_GetForm(tctx, p, handle, form_name);
+
+ if (!test_DeleteForm(tctx, p, handle, form_name)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPorts_old(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPorts r;
+
+ r.in.servername = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.level = 2;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumPorts\n");
+
+ status = dcerpc_spoolss_EnumPorts(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumPorts(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed");
+
+ torture_assert(tctx, r.out.info, "No ports returned");
+ }
+
+ return true;
+}
+
+static bool test_AddPort(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct spoolss_AddPort r;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.unknown = 0;
+ r.in.monitor_name = "foo";
+
+ torture_comment(tctx, "Testing AddPort\n");
+
+ status = dcerpc_spoolss_AddPort(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "AddPort failed");
+
+ /* win2k3 returns WERR_NOT_SUPPORTED */
+
+#if 0
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("AddPort failed - %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+#endif
+
+ return true;
+}
+
+static bool test_GetJob(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle, uint32_t job_id)
+{
+ NTSTATUS status;
+ struct spoolss_GetJob r;
+
+ r.in.handle = handle;
+ r.in.job_id = job_id;
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing GetJob\n");
+
+ status = dcerpc_spoolss_GetJob(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_GetJob(p, tctx, &r);
+
+ torture_assert(tctx, r.out.info, "No job info returned");
+ }
+
+ return true;
+}
+
+static bool test_SetJob(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle, uint32_t job_id,
+ enum spoolss_JobControl command)
+{
+ NTSTATUS status;
+ struct spoolss_SetJob r;
+
+ r.in.handle = handle;
+ r.in.job_id = job_id;
+ r.in.ctr = NULL;
+ r.in.command = command;
+
+ torture_comment(tctx, "Testing SetJob\n");
+
+ status = dcerpc_spoolss_SetJob(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "SetJob failed");
+ torture_assert_werr_ok(tctx, r.out.result, "SetJob failed");
+
+ return true;
+}
+
+static bool test_EnumJobs(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_EnumJobs r;
+
+ r.in.handle = handle;
+ r.in.firstjob = 0;
+ r.in.numjobs = 0xffffffff;
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumJobs\n");
+
+ status = dcerpc_spoolss_EnumJobs(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ union spoolss_JobInfo *info;
+ int j;
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumJobs(p, tctx, &r);
+
+ torture_assert(tctx, r.out.info, "No jobs returned");
+
+ info = r.out.info;
+
+ for (j = 0; j < r.out.count; j++) {
+ test_GetJob(tctx, p, handle, info[j].info1.job_id);
+ test_SetJob(tctx, p, handle, info[j].info1.job_id, SPOOLSS_JOB_CONTROL_PAUSE);
+ test_SetJob(tctx, p, handle, info[j].info1.job_id, SPOOLSS_JOB_CONTROL_RESUME);
+ }
+
+ } else {
+ torture_assert_werr_ok(tctx, r.out.result, "EnumJobs failed");
+ }
+
+ return true;
+}
+
+static bool test_DoPrintTest(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ bool ret = true;
+ NTSTATUS status;
+ struct spoolss_StartDocPrinter s;
+ struct spoolss_DocumentInfo1 info1;
+ struct spoolss_StartPagePrinter sp;
+ struct spoolss_WritePrinter w;
+ struct spoolss_EndPagePrinter ep;
+ struct spoolss_EndDocPrinter e;
+ int i;
+ uint32_t job_id;
+
+ torture_comment(tctx, "Testing StartDocPrinter\n");
+
+ s.in.handle = handle;
+ s.in.level = 1;
+ s.in.info.info1 = &info1;
+ info1.document_name = "TorturePrintJob";
+ info1.output_file = NULL;
+ info1.datatype = "RAW";
+
+ status = dcerpc_spoolss_StartDocPrinter(p, tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_StartDocPrinter failed");
+ torture_assert_werr_ok(tctx, s.out.result, "StartDocPrinter failed");
+
+ job_id = s.out.job_id;
+
+ for (i=1; i < 4; i++) {
+ torture_comment(tctx, "Testing StartPagePrinter: Page[%d]\n", i);
+
+ sp.in.handle = handle;
+
+ status = dcerpc_spoolss_StartPagePrinter(p, tctx, &sp);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_StartPagePrinter failed");
+ torture_assert_werr_ok(tctx, sp.out.result, "StartPagePrinter failed");
+
+ torture_comment(tctx, "Testing WritePrinter: Page[%d]\n", i);
+
+ w.in.handle = handle;
+ w.in.data = data_blob_string_const(talloc_asprintf(tctx,"TortureTestPage: %d\nData\n",i));
+
+ status = dcerpc_spoolss_WritePrinter(p, tctx, &w);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_WritePrinter failed");
+ torture_assert_werr_ok(tctx, w.out.result, "WritePrinter failed");
+
+ torture_comment(tctx, "Testing EndPagePrinter: Page[%d]\n", i);
+
+ ep.in.handle = handle;
+
+ status = dcerpc_spoolss_EndPagePrinter(p, tctx, &ep);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndPagePrinter failed");
+ torture_assert_werr_ok(tctx, ep.out.result, "EndPagePrinter failed");
+ }
+
+ torture_comment(tctx, "Testing EndDocPrinter\n");
+
+ e.in.handle = handle;
+
+ status = dcerpc_spoolss_EndDocPrinter(p, tctx, &e);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndDocPrinter failed");
+ torture_assert_werr_ok(tctx, e.out.result, "EndDocPrinter failed");
+
+ ret &= test_EnumJobs(tctx, p, handle);
+
+ ret &= test_SetJob(tctx, p, handle, job_id, SPOOLSS_JOB_CONTROL_DELETE);
+
+ return ret;
+}
+
+static bool test_PausePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+
+ r.in.handle = handle;
+ r.in.level = 0;
+ r.in.info.info1 = NULL;
+ r.in.devmode_ctr.devmode= NULL;
+ r.in.secdesc_ctr.sd = NULL;
+ r.in.command = SPOOLSS_PRINTER_CONTROL_PAUSE;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_PAUSE\n");
+
+ status = dcerpc_spoolss_SetPrinter(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed");
+
+ return true;
+}
+
+static bool test_ResumePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+
+ r.in.handle = handle;
+ r.in.level = 0;
+ r.in.info.info1 = NULL;
+ r.in.devmode_ctr.devmode= NULL;
+ r.in.secdesc_ctr.sd = NULL;
+ r.in.command = SPOOLSS_PRINTER_CONTROL_RESUME;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_RESUME\n");
+
+ status = dcerpc_spoolss_SetPrinter(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed");
+
+ return true;
+}
+
+static bool test_GetPrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *value_name)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterData r;
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing GetPrinterData\n");
+
+ status = dcerpc_spoolss_GetPrinterData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_GetPrinterData(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinterData failed");
+ }
+
+ return true;
+}
+
+static bool test_GetPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *value_name)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterDataEx r;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.in.value_name = value_name;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing GetPrinterDataEx\n");
+
+ status = dcerpc_spoolss_GetPrinterDataEx(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status,NT_STATUS_NET_WRITE_FAULT) &&
+ p->last_fault_code == DCERPC_FAULT_OP_RNG_ERROR) {
+ torture_skip(tctx, "GetPrinterDataEx not supported by server\n");
+ }
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed");
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_GetPrinterDataEx(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDataEx failed");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterData(struct torture_context *tctx, struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterData r;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.in.enum_index = 0;
+
+ do {
+ uint32_t value_size = 0;
+ uint32_t data_size = 0;
+ uint32_t printerdata_type = 0;
+ DATA_BLOB data = data_blob(NULL,0);
+
+ r.in.value_offered = value_size;
+ r.out.value_needed = &value_size;
+ r.in.data_offered = data_size;
+ r.out.data_needed = &data_size;
+
+ r.out.printerdata_type = &printerdata_type;
+ r.out.buffer = &data;
+
+ torture_comment(tctx, "Testing EnumPrinterData\n");
+
+ status = dcerpc_spoolss_EnumPrinterData(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterData failed");
+
+ r.in.value_offered = value_size;
+ r.in.data_offered = data_size;
+
+ status = dcerpc_spoolss_EnumPrinterData(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterData failed");
+
+ test_GetPrinterData(tctx, p, handle, r.out.value_name);
+
+ test_GetPrinterDataEx(tctx,
+ p, handle, "PrinterDriverData",
+ r.out.value_name);
+
+ r.in.enum_index++;
+
+ } while (W_ERROR_IS_OK(r.out.result));
+
+ return true;
+}
+
+static bool test_EnumPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterDataEx r;
+
+ r.in.handle = handle;
+ r.in.key_name = "PrinterDriverData";
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumPrinterDataEx\n");
+
+ status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed");
+
+ r.in.offered = r.out.needed;
+
+ status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed");
+
+ return true;
+}
+
+
+static bool test_DeletePrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *value_name)
+{
+ NTSTATUS status;
+ struct spoolss_DeletePrinterData r;
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+
+ torture_comment(tctx, "Testing DeletePrinterData\n");
+
+ status = dcerpc_spoolss_DeletePrinterData(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "DeletePrinterData failed");
+
+ return true;
+}
+
+static bool test_SetPrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinterData r;
+ const char *value_name = "spottyfoot";
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+ r.in.type = SPOOLSS_PRINTER_DATA_TYPE_STRING;
+ r.in.data.string = "dog";
+
+ torture_comment(tctx, "Testing SetPrinterData\n");
+
+ status = dcerpc_spoolss_SetPrinterData(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinterData failed");
+
+ if (!test_GetPrinterData(tctx, p, handle, value_name)) {
+ return false;
+ }
+
+ if (!test_DeletePrinterData(tctx, p, handle, value_name)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_SecondaryClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *b;
+ struct dcerpc_pipe *p2;
+ struct spoolss_ClosePrinter cp;
+
+ /* only makes sense on SMB */
+ if (p->conn->transport.transport != NCACN_NP) {
+ return true;
+ }
+
+ torture_comment(tctx, "testing close on secondary pipe\n");
+
+ status = dcerpc_parse_binding(tctx, p->conn->binding_string, &b);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to parse dcerpc binding");
+
+ status = dcerpc_secondary_connection(p, &p2, b);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+
+ status = dcerpc_bind_auth_none(p2, &ndr_table_spoolss);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create bind on secondary connection");
+
+ cp.in.handle = handle;
+ cp.out.handle = handle;
+
+ status = dcerpc_spoolss_ClosePrinter(p2, tctx, &cp);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_NET_WRITE_FAULT,
+ "ERROR: Allowed close on secondary connection");
+
+ torture_assert_int_equal(tctx, p2->last_fault_code, DCERPC_FAULT_CONTEXT_MISMATCH,
+ "Unexpected fault code");
+
+ talloc_free(p2);
+
+ return true;
+}
+
+static bool test_OpenPrinter_badname(struct torture_context *tctx,
+ struct dcerpc_pipe *p, const char *name)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter op;
+ struct spoolss_OpenPrinterEx opEx;
+ struct policy_handle handle;
+ bool ret = true;
+
+ op.in.printername = name;
+ op.in.datatype = NULL;
+ op.in.devmode_ctr.devmode= NULL;
+ op.in.access_mask = 0;
+ op.out.handle = &handle;
+
+ torture_comment(tctx, "\nTesting OpenPrinter(%s) with bad name\n", op.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter(p, tctx, &op);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+ if (!W_ERROR_EQUAL(WERR_INVALID_PRINTER_NAME,op.out.result)) {
+ torture_comment(tctx, "OpenPrinter(%s) unexpected result[%s] should be WERR_INVALID_PRINTER_NAME\n",
+ name, win_errstr(op.out.result));
+ }
+
+ if (W_ERROR_IS_OK(op.out.result)) {
+ ret &=test_ClosePrinter(tctx, p, &handle);
+ }
+
+ opEx.in.printername = name;
+ opEx.in.datatype = NULL;
+ opEx.in.devmode_ctr.devmode = NULL;
+ opEx.in.access_mask = 0;
+ opEx.in.level = 1;
+ opEx.in.userlevel.level1 = NULL;
+ opEx.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s) with bad name\n", opEx.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &opEx);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
+ if (!W_ERROR_EQUAL(WERR_INVALID_PARAM,opEx.out.result)) {
+ torture_comment(tctx, "OpenPrinterEx(%s) unexpected result[%s] should be WERR_INVALID_PARAM\n",
+ name, win_errstr(opEx.out.result));
+ }
+
+ if (W_ERROR_IS_OK(opEx.out.result)) {
+ ret &=test_ClosePrinter(tctx, p, &handle);
+ }
+
+ return ret;
+}
+
+static bool test_OpenPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter r;
+ struct policy_handle handle;
+ bool ret = true;
+
+ r.in.printername = talloc_asprintf(tctx, "\\\\%s\\%s", dcerpc_server_name(p), name);
+ r.in.datatype = NULL;
+ r.in.devmode_ctr.devmode= NULL;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinter(%s)\n", r.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "OpenPrinter failed");
+
+ if (!test_GetPrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_ClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool call_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name, struct policy_handle *handle)
+{
+ struct spoolss_OpenPrinterEx r;
+ struct spoolss_UserLevel1 userlevel1;
+ NTSTATUS status;
+
+ if (name && name[0]) {
+ r.in.printername = talloc_asprintf(tctx, "\\\\%s\\%s",
+ dcerpc_server_name(p), name);
+ } else {
+ r.in.printername = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ }
+
+ r.in.datatype = NULL;
+ r.in.devmode_ctr.devmode= NULL;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.level = 1;
+ r.in.userlevel.level1 = &userlevel1;
+ r.out.handle = handle;
+
+ userlevel1.size = 1234;
+ userlevel1.client = "hello";
+ userlevel1.user = "spottyfoot!";
+ userlevel1.build = 1;
+ userlevel1.major = 2;
+ userlevel1.minor = 3;
+ userlevel1.processor = 4;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "OpenPrinterEx failed");
+
+ return true;
+}
+
+static bool test_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name)
+{
+ struct policy_handle handle;
+ bool ret = true;
+
+ if (!call_OpenPrinterEx(tctx, p, name, &handle)) {
+ return false;
+ }
+
+ if (!test_GetPrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumForms(tctx, p, &handle, false)) {
+ ret = false;
+ }
+
+ if (!test_AddForm(tctx, p, &handle, false)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterData(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterDataEx(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_PausePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_DoPrintTest(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_ResumePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_SetPrinterData(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_ClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrinters_old(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct spoolss_EnumPrinters r;
+ NTSTATUS status;
+ uint16_t levels[] = {1, 2, 4, 5};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ union spoolss_PrinterInfo *info;
+ int j;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = "";
+ r.in.level = levels[i];
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinters(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+ status = dcerpc_spoolss_EnumPrinters(p, tctx, &r);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ if (!r.out.info) {
+ torture_comment(tctx, "No printers returned\n");
+ return true;
+ }
+
+ info = r.out.info;
+
+ for (j=0;j<r.out.count;j++) {
+ if (r.in.level == 1) {
+ /* the names appear to be comma-separated name lists? */
+ char *name = talloc_strdup(tctx, info[j].info1.name);
+ char *comma = strchr(name, ',');
+ if (comma) *comma = 0;
+ if (!test_OpenPrinter(tctx, p, name)) {
+ ret = false;
+ }
+ if (!test_OpenPrinterEx(tctx, p, name)) {
+ ret = false;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+#if 0
+static bool test_GetPrinterDriver2(struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *driver_name)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterDriver2 r;
+
+ r.in.handle = handle;
+ r.in.architecture = "W32X86";
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.in.client_major_version = 0;
+ r.in.client_minor_version = 0;
+
+ printf("Testing GetPrinterDriver2\n");
+
+ status = dcerpc_spoolss_GetPrinterDriver2(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetPrinterDriver2 failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ r.in.offered = r.out.needed;
+ status = dcerpc_spoolss_GetPrinterDriver2(p, tctx, &r);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("GetPrinterDriver2 failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("GetPrinterDriver2 failed - %s\n",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+static bool test_EnumPrinterDrivers_old(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct spoolss_EnumPrinterDrivers r;
+ NTSTATUS status;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ r.in.server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.environment = "Windows NT x86";
+ r.in.level = levels[i];
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+
+ torture_comment(tctx, "Testing EnumPrinterDrivers level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinterDrivers(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDrivers failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc(tctx, NULL, r.out.needed);
+ data_blob_clear(&blob);
+ r.in.buffer = &blob;
+ r.in.offered = r.out.needed;
+ status = dcerpc_spoolss_EnumPrinterDrivers(p, tctx, &r);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDrivers failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDrivers failed");
+
+ if (!r.out.info) {
+ torture_comment(tctx, "No printer drivers returned\n");
+ break;
+ }
+ }
+
+ return true;
+}
+
+/** Test that makes sure that calling ReplyOpenPrinter()
+ * on Samba 4 will cause an irpc broadcast call.
+ */
+static bool test_ReplyOpenPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *pipe)
+{
+ struct spoolss_ReplyOpenPrinter r;
+ struct spoolss_ReplyClosePrinter s;
+ struct policy_handle h;
+
+ r.in.server_name = "earth";
+ r.in.printer_local = 2;
+ r.in.type = REG_DWORD;
+ r.in.unknown1 = 0;
+ r.in.unknown2 = 0;
+ r.out.handle = &h;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_ReplyOpenPrinter(pipe, tctx, &r),
+ "spoolss_ReplyOpenPrinter call failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "error return code");
+
+ s.in.handle = &h;
+ s.out.handle = &h;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_ReplyClosePrinter(pipe, tctx, &s),
+ "spoolss_ReplyClosePrinter call failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "error return code");
+
+ return true;
+}
+
+bool torture_rpc_spoolss(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct test_spoolss_context *ctx;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_spoolss);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ctx = talloc_zero(torture, struct test_spoolss_context);
+
+ ret &= test_OpenPrinter_server(torture, p, ctx);
+
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "W3SvcInstalled");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "BeepEnabled");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "EventLog");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "NetPopup");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "NetPopupToComputer");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "MajorVersion");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "MinorVersion");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "DefaultSpoolDirectory");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "Architecture");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "DsPresent");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "OSVersion");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "OSVersionEx");
+ ret &= test_GetPrinterData(torture, p, &ctx->server_handle, "DNSMachineName");
+ ret &= test_EnumForms(torture, p, &ctx->server_handle, true);
+ ret &= test_AddForm(torture, p, &ctx->server_handle, true);
+ ret &= test_EnumPorts(torture, p, ctx);
+ ret &= test_GetPrinterDriverDirectory(torture, p, ctx);
+ ret &= test_EnumPrinterDrivers(torture, p, ctx);
+ ret &= test_EnumMonitors(torture, p, ctx);
+ ret &= test_EnumPrintProcessors(torture, p, ctx);
+ ret &= test_EnumPrinters(torture, p, ctx);
+ ret &= test_OpenPrinter_badname(torture, p, "__INVALID_PRINTER__");
+ ret &= test_OpenPrinter_badname(torture, p, "\\\\__INVALID_HOST__");
+ ret &= test_OpenPrinter_badname(torture, p, "");
+ ret &= test_OpenPrinter_badname(torture, p, "\\\\\\");
+ ret &= test_OpenPrinter_badname(torture, p, "\\\\\\__INVALID_PRINTER__");
+ ret &= test_OpenPrinter_badname(torture, p, talloc_asprintf(torture, "\\\\%s\\", dcerpc_server_name(p)));
+ ret &= test_OpenPrinter_badname(torture, p,
+ talloc_asprintf(torture, "\\\\%s\\__INVALID_PRINTER__", dcerpc_server_name(p)));
+
+
+ ret &= test_AddPort(torture, p);
+ ret &= test_EnumPorts_old(torture, p);
+ ret &= test_EnumPrinters_old(torture, p);
+ ret &= test_EnumPrinterDrivers_old(torture, p);
+ ret &= test_ReplyOpenPrinter(torture, p);
+
+ return ret;
+}
diff --git a/source4/torture/rpc/spoolss_notify.c b/source4/torture/rpc/spoolss_notify.c
new file mode 100644
index 0000000000..ab6309d55f
--- /dev/null
+++ b/source4/torture/rpc/spoolss_notify.c
@@ -0,0 +1,300 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for spoolss rpc notify operations
+
+ Copyright (C) Jelmer Vernooij 2007
+
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "rpc_server/dcerpc_server.h"
+#include "lib/events/events.h"
+#include "smbd/process_model.h"
+#include "smb_server/smb_server.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "lib/socket/netif.h"
+#include "util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+
+static NTSTATUS spoolss__op_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface)
+{
+ return NT_STATUS_OK;
+}
+
+static void spoolss__op_unbind(struct dcesrv_connection_context *context, const struct dcesrv_interface *iface)
+{
+}
+
+static NTSTATUS spoolss__op_ndr_pull(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_pull *pull, void **r)
+{
+ enum ndr_err_code ndr_err;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ dce_call->fault_code = 0;
+
+ if (opnum >= ndr_table_spoolss.num_calls) {
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ *r = talloc_size(mem_ctx, ndr_table_spoolss.calls[opnum].struct_size);
+ NT_STATUS_HAVE_NO_MEMORY(*r);
+
+ /* unravel the NDR for the packet */
+ ndr_err = ndr_table_spoolss.calls[opnum].ndr_pull(pull, NDR_IN, *r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dcerpc_log_packet(&ndr_table_spoolss, opnum, NDR_IN,
+ &dce_call->pkt.u.request.stub_and_verifier);
+ dce_call->fault_code = DCERPC_FAULT_NDR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Note that received_packets are allocated in talloc_autofree_context(),
+ * because no other context appears to stay around long enough. */
+static struct received_packet {
+ uint16_t opnum;
+ void *r;
+ struct received_packet *prev, *next;
+} *received_packets = NULL;
+
+
+static NTSTATUS spoolss__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+ struct received_packet *rp;
+
+ rp = talloc_zero(talloc_autofree_context(), struct received_packet);
+ rp->opnum = opnum;
+ rp->r = talloc_reference(rp, r);
+
+ DLIST_ADD_END(received_packets, rp, struct received_packet *);
+
+ switch (opnum) {
+ case 58: {
+ struct spoolss_ReplyOpenPrinter *r2 = (struct spoolss_ReplyOpenPrinter *)r;
+ r2->out.result = WERR_OK;
+ break;
+ }
+
+ default:
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ break;
+ }
+
+ if (dce_call->fault_code != 0) {
+ dcerpc_log_packet(&ndr_table_spoolss, opnum, NDR_IN,
+ &dce_call->pkt.u.request.stub_and_verifier);
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS spoolss__op_reply(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r)
+{
+ return NT_STATUS_OK;
+}
+
+
+static NTSTATUS spoolss__op_ndr_push(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, struct ndr_push *push, const void *r)
+{
+ enum ndr_err_code ndr_err;
+ uint16_t opnum = dce_call->pkt.u.request.opnum;
+
+ ndr_err = ndr_table_spoolss.calls[opnum].ndr_push(push, NDR_OUT, r);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ dce_call->fault_code = DCERPC_FAULT_NDR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+const static struct dcesrv_interface notify_test_spoolss_interface = {
+ .name = "spoolss",
+ .syntax_id = {{0x12345678,0x1234,0xabcd,{0xef,0x00},{0x01,0x23,0x45,0x67,0x89,0xab}},1.0},
+ .bind = spoolss__op_bind,
+ .unbind = spoolss__op_unbind,
+ .ndr_pull = spoolss__op_ndr_pull,
+ .dispatch = spoolss__op_dispatch,
+ .reply = spoolss__op_reply,
+ .ndr_push = spoolss__op_ndr_push
+};
+
+static bool spoolss__op_interface_by_uuid(struct dcesrv_interface *iface, const struct GUID *uuid, uint32_t if_version)
+{
+ if (notify_test_spoolss_interface.syntax_id.if_version == if_version &&
+ GUID_equal(&notify_test_spoolss_interface.syntax_id.uuid, uuid)) {
+ memcpy(iface,&notify_test_spoolss_interface, sizeof(*iface));
+ return true;
+ }
+
+ return false;
+}
+
+static bool spoolss__op_interface_by_name(struct dcesrv_interface *iface, const char *name)
+{
+ if (strcmp(notify_test_spoolss_interface.name, name)==0) {
+ memcpy(iface, &notify_test_spoolss_interface, sizeof(*iface));
+ return true;
+ }
+
+ return false;
+}
+
+static NTSTATUS spoolss__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server)
+{
+ int i;
+
+ for (i=0;i<ndr_table_spoolss.endpoints->count;i++) {
+ NTSTATUS ret;
+ const char *name = ndr_table_spoolss.endpoints->names[i];
+
+ ret = dcesrv_interface_register(dce_ctx, name, &notify_test_spoolss_interface, NULL);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1,("spoolss_op_init_server: failed to register endpoint '%s'\n",name));
+ return ret;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static bool test_RFFPCNEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct spoolss_OpenPrinter q;
+ struct spoolss_RemoteFindFirstPrinterChangeNotifyEx r;
+ struct dcesrv_endpoint_server ep_server;
+ NTSTATUS status;
+ struct dcesrv_context *dce_ctx;
+ const char *endpoints[] = { "spoolss", NULL };
+ struct spoolss_NotifyOptionsContainer t1;
+ struct spoolss_ClosePrinter cp;
+
+ struct policy_handle handle;
+ const char *address;
+ struct interface *ifaces;
+
+ received_packets = NULL;
+
+ ntvfs_init(tctx->lp_ctx);
+
+ ZERO_STRUCT(q);
+
+ q.in.printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ q.in.datatype = NULL;
+ q.in.devmode_ctr.devmode= NULL;
+ q.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ q.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinter(%s)\n", q.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter(p, tctx, &q);
+
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+
+ torture_assert_werr_ok(tctx, q.out.result, "OpenPrinter failed");
+
+ /* Start DCE/RPC server */
+
+ /* fill in our name */
+ ep_server.name = "spoolss";
+
+ /* fill in all the operations */
+ ep_server.init_server = spoolss__op_init_server;
+
+ ep_server.interface_by_uuid = spoolss__op_interface_by_uuid;
+ ep_server.interface_by_name = spoolss__op_interface_by_name;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_register_ep_server(&ep_server),
+ "unable to register spoolss server");
+
+ lp_set_cmdline(tctx->lp_ctx, "dcerpc endpoint servers", "spoolss");
+
+ load_interfaces(tctx, lp_interfaces(tctx->lp_ctx), &ifaces);
+ address = iface_n_ip(ifaces, 0);
+ torture_comment(tctx, "Listening for callbacks on %s\n", address);
+ status = smbsrv_add_socket(p->conn->event_ctx, tctx->lp_ctx, &single_ops, address);
+ torture_assert_ntstatus_ok(tctx, status, "starting smb server");
+
+ status = dcesrv_init_context(tctx, tctx->lp_ctx, endpoints, &dce_ctx);
+ torture_assert_ntstatus_ok(tctx, status,
+ "unable to initialize DCE/RPC server");
+
+ r.in.flags = 0;
+ r.in.str = talloc_asprintf(tctx, "\\\\%s", address);
+ r.in.options = 0;
+ r.in.printer_local = 123;
+ t1.version = 2;
+ t1.flags = 0;
+ t1.count = 2;
+ t1.options = talloc_zero_array(tctx, struct spoolss_NotifyOptionsArray, 2);
+ t1.options[0].type = SPOOLSS_NOTIFY_PRINTER;
+ t1.options[0].count = 1;
+ t1.options[0].fields = talloc_array(t1.options, enum spoolss_Field, 1);
+ t1.options[0].fields[0] = SPOOLSS_FIELD_SERVER_NAME;
+
+ t1.options[1].type = SPOOLSS_NOTIFY_JOB;
+ t1.options[1].count = 1;
+ t1.options[1].fields = talloc_array(t1.options, enum spoolss_Field, 1);
+ t1.options[1].fields[0] = SPOOLSS_FIELD_PRINTER_NAME;
+
+ r.in.t1 = &t1;
+ r.in.handle = &handle;
+
+ status = dcerpc_spoolss_RemoteFindFirstPrinterChangeNotifyEx(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "FFPCNEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "error return code for FFPCNEx");
+
+ cp.in.handle = &handle;
+ cp.out.handle = &handle;
+
+ torture_comment(tctx, "Testing ClosePrinter\n");
+
+ status = dcerpc_spoolss_ClosePrinter(p, tctx, &cp);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+
+ /* We should've had an incoming packet 58 (ReplyOpenPrinter) */
+ torture_assert(tctx, received_packets != NULL, "no packets received");
+ torture_assert_int_equal(tctx, received_packets->opnum, 58, "invalid opnum");
+
+ /* Shut down DCE/RPC server */
+ talloc_free(dce_ctx);
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_spoolss_notify(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SPOOLSS-NOTIFY");
+
+ struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite,
+ "notify", &ndr_table_spoolss);
+
+ torture_rpc_tcase_add_test(tcase, "testRFFPCNEx", test_RFFPCNEx);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/spoolss_win.c b/source4/torture/rpc/spoolss_win.c
new file mode 100644
index 0000000000..9ce9fb7526
--- /dev/null
+++ b/source4/torture/rpc/spoolss_win.c
@@ -0,0 +1,578 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for spoolss rpc operations as performed by various win versions
+
+ Copyright (C) Kai Blin 2007
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "rpc_server/dcerpc_server.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+
+struct test_spoolss_win_context {
+ /* EnumPrinters */
+ uint32_t printer_count;
+ union spoolss_PrinterInfo *printer_info;
+ union spoolss_PrinterInfo *current_info;
+
+ /* EnumPrinterKeys */
+ char *printer_keys;
+};
+
+/* This is a convenience function for all OpenPrinterEx calls */
+static bool test_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printer_name,
+ uint32_t access_mask)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinterEx op;
+ struct spoolss_UserLevel1 ul_1;
+
+ torture_comment(tctx, "Opening printer '%s'\n", printer_name);
+
+ op.in.printername = talloc_strdup(tctx, printer_name);
+ op.in.datatype = NULL;
+ op.in.devmode_ctr.devmode = NULL;
+ op.in.access_mask = access_mask;
+ op.in.level = 1;
+ op.in.userlevel.level1 = &ul_1;
+ op.out.handle = handle;
+
+ ul_1.size = 1234;
+ ul_1.client = "\\clientname";
+ ul_1.user = "username";
+ ul_1.build = 1;
+ ul_1.major = 2;
+ ul_1.minor = 3;
+ ul_1.processor = 4567;
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &op);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
+ torture_assert_werr_ok(tctx, op.out.result, "OpenPrinterEx failed");
+
+ return true;
+}
+
+static bool test_OpenPrinterAsAdmin(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *printername)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinterEx op;
+ struct spoolss_ClosePrinter cp;
+ struct spoolss_UserLevel1 ul_1;
+ struct policy_handle handle;
+
+ ul_1.size = 1234;
+ ul_1.client = "\\clientname";
+ ul_1.user = "username";
+ ul_1.build = 1;
+ ul_1.major = 2;
+ ul_1.minor = 3;
+ ul_1.processor = 4567;
+
+ op.in.printername = talloc_strdup(tctx, printername);
+ op.in.datatype = NULL;
+ op.in.devmode_ctr.devmode = NULL;
+ op.in.access_mask = SERVER_ALL_ACCESS;
+ op.in.level = 1;
+ op.in.userlevel.level1 = &ul_1;
+ op.out.handle = &handle;
+
+ cp.in.handle = &handle;
+ cp.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s) with admin rights\n",
+ op.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinterEx(p, tctx, &op);
+
+ if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(op.out.result)) {
+ status = dcerpc_spoolss_ClosePrinter(p, tctx, &cp);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+ }
+
+ return true;
+}
+
+
+static bool test_ClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle);
+
+/* This replicates the opening sequence of OpenPrinterEx calls XP does */
+static bool test_OpenPrinterSequence(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ bool ret;
+ char *printername = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+
+ /* First, see if we can open the printer read_only */
+ ret = test_OpenPrinterEx(tctx, p, handle, printername, 0);
+ torture_assert(tctx, ret == true, "OpenPrinterEx failed.");
+
+ ret = test_ClosePrinter(tctx, p, handle);
+ torture_assert(tctx, ret, "ClosePrinter failed");
+
+ /* Now let's see if we have admin rights to it. */
+ ret = test_OpenPrinterAsAdmin(tctx, p, printername);
+ torture_assert(tctx, ret == true,
+ "OpenPrinterEx as admin failed unexpectedly.");
+
+ ret = test_OpenPrinterEx(tctx, p, handle, printername, SERVER_EXECUTE);
+ torture_assert(tctx, ret == true, "OpenPrinterEx failed.");
+
+ return true;
+}
+
+static bool test_GetPrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *value_name,
+ WERROR expected_werr,
+ uint32_t expected_value)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterData gpd;
+
+ torture_comment(tctx, "Testing GetPrinterData(%s).\n", value_name);
+ gpd.in.handle = handle;
+ gpd.in.value_name = value_name;
+ gpd.in.offered = 4;
+
+ status = dcerpc_spoolss_GetPrinterData(p, tctx, &gpd);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed.");
+ torture_assert_werr_equal(tctx, gpd.out.result, expected_werr,
+ "GetPrinterData did not return expected error value.");
+
+ if (W_ERROR_IS_OK(expected_werr)) {
+ torture_assert_int_equal(tctx, gpd.out.data.value,
+ expected_value,
+ "GetPrinterData did not return expected value.");
+ }
+ return true;
+}
+
+static bool test_EnumPrinters(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_spoolss_win_context *ctx,
+ uint32_t initial_blob_size)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinters ep;
+ DATA_BLOB blob = data_blob_talloc_zero(ctx, initial_blob_size);
+
+ ep.in.flags = PRINTER_ENUM_NAME;
+ ep.in.server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ ep.in.level = 2;
+ ep.in.buffer = &blob;
+ ep.in.offered = initial_blob_size;
+
+ status = dcerpc_spoolss_EnumPrinters(p, ctx, &ep);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed.");
+
+ if (W_ERROR_EQUAL(ep.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(ctx, ep.out.needed);
+ ep.in.buffer = &blob;
+ ep.in.offered = ep.out.needed;
+ status = dcerpc_spoolss_EnumPrinters(p, ctx, &ep);
+ torture_assert_ntstatus_ok(tctx, status,"EnumPrinters failed.");
+ }
+
+ torture_assert_werr_ok(tctx, ep.out.result, "EnumPrinters failed.");
+
+ ctx->printer_count = ep.out.count;
+ ctx->printer_info = ep.out.info;
+
+ torture_comment(tctx, "Found %d printer(s).\n", ctx->printer_count);
+
+ return true;
+}
+
+static bool test_GetPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ struct test_spoolss_win_context *ctx,
+ uint32_t level,
+ uint32_t initial_blob_size)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinter gp;
+ DATA_BLOB blob = data_blob_talloc_zero(ctx, initial_blob_size);
+
+ torture_comment(tctx, "Test GetPrinter level %d\n", level);
+
+ gp.in.handle = handle;
+ gp.in.level = level;
+ gp.in.buffer = (initial_blob_size == 0)?NULL:&blob;
+ gp.in.offered = initial_blob_size;
+
+ status = dcerpc_spoolss_GetPrinter(p, tctx, &gp);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed");
+
+ if (W_ERROR_EQUAL(gp.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(ctx, gp.out.needed);
+ gp.in.buffer = &blob;
+ gp.in.offered = gp.out.needed;
+ status = dcerpc_spoolss_GetPrinter(p, tctx, &gp);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinter failed");
+ }
+
+ torture_assert_werr_ok(tctx, gp.out.result, "GetPrinter failed");
+
+ ctx->current_info = gp.out.info;
+ return true;
+}
+
+static bool test_EnumJobs(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_EnumJobs ej;
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, 1024);
+
+ torture_comment(tctx, "Test EnumJobs\n");
+
+ ej.in.handle = handle;
+ ej.in.level = 2;
+ ej.in.buffer = &blob;
+ ej.in.offered = 1024;
+
+ status = dcerpc_spoolss_EnumJobs(p, tctx, &ej);
+ torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
+ torture_assert_werr_ok(tctx, ej.out.result, "EnumJobs failed");
+
+ return true;
+}
+
+static bool test_GetPrinterDriver2(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterDriver2 gpd2;
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, 87424);
+
+ torture_comment(tctx, "Testing GetPrinterDriver2\n");
+
+ gpd2.in.handle = handle;
+ gpd2.in.architecture = "Windows NT x86";
+ gpd2.in.level = 101;
+ gpd2.in.buffer = &blob;
+ gpd2.in.offered = 87424;
+ gpd2.in.client_major_version = 3;
+ gpd2.in.client_minor_version = 0;
+
+ status = dcerpc_spoolss_GetPrinterDriver2(p, tctx, &gpd2);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterDriver2 failed");
+ torture_assert_werr_ok(tctx, gpd2.out.result,
+ "GetPrinterDriver2 failed.");
+
+ return true;
+}
+
+static bool test_EnumForms(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ uint32_t initial_blob_size)
+{
+ NTSTATUS status;
+ struct spoolss_EnumForms ef;
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, initial_blob_size);
+
+ torture_comment(tctx, "Testing EnumForms\n");
+
+ ef.in.handle = handle;
+ ef.in.level = 1;
+ ef.in.buffer = (initial_blob_size == 0)?NULL:&blob;
+ ef.in.offered = initial_blob_size;
+
+ status = dcerpc_spoolss_EnumForms(p, tctx, &ef);
+ torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+
+ if (W_ERROR_EQUAL(ef.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(tctx, ef.out.needed);
+ ef.in.buffer = &blob;
+ ef.in.offered = ef.out.needed;
+ status = dcerpc_spoolss_EnumForms(p, tctx, &ef);
+ torture_assert_ntstatus_ok(tctx, status, "EnumForms failed");
+ }
+
+ torture_assert_werr_ok(tctx, ef.out.result, "EnumForms failed");
+
+ return true;
+}
+
+static bool test_EnumPrinterKey(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char* key,
+ struct test_spoolss_win_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterKey epk;
+ uint32_t needed = 0;
+
+ torture_comment(tctx, "Testing EnumPrinterKey(%s)\n", key);
+
+ epk.in.handle = handle;
+ epk.in.key_name = talloc_strdup(tctx, key);
+ epk.in.needed = needed;
+
+ status = dcerpc_spoolss_EnumPrinterKey(p, tctx, &epk);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterKey failed");
+
+
+ if (W_ERROR_EQUAL(epk.out.result, WERR_MORE_DATA)) {
+ epk.in.needed = epk.out.needed;
+ status = dcerpc_spoolss_EnumPrinterKey(p, tctx, &epk);
+ torture_assert_ntstatus_ok(tctx, status,
+ "EnumPrinterKey failed");
+ }
+
+ torture_assert_werr_ok(tctx, epk.out.result, "EnumPrinterKey failed");
+
+ convert_string_talloc(ctx, lp_iconv_convenience(tctx->lp_ctx), CH_UTF16,
+ CH_UNIX, epk.out.key_buffer, epk.out.needed,
+ (void**)&ctx->printer_keys);
+
+ return true;
+}
+
+static bool test_EnumPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *key,
+ uint32_t initial_blob_size,
+ WERROR expected_error)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterDataEx epde;
+
+ torture_comment(tctx, "Testing EnumPrinterDataEx(%s)\n", key);
+
+ epde.in.handle = handle;
+ epde.in.key_name = talloc_strdup(tctx, key);
+ epde.in.offered = 0;
+
+ status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &epde);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed.");
+ if (W_ERROR_EQUAL(epde.out.result, WERR_MORE_DATA)) {
+ epde.in.offered = epde.out.needed;
+ status = dcerpc_spoolss_EnumPrinterDataEx(p, tctx, &epde);
+ torture_assert_ntstatus_ok(tctx, status,
+ "EnumPrinterDataEx failed.");
+ }
+
+ torture_assert_werr_equal(tctx, epde.out.result, expected_error,
+ "EnumPrinterDataEx failed.");
+
+ return true;
+}
+
+static bool test_ClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_ClosePrinter cp;
+
+ cp.in.handle = handle;
+ cp.out.handle = handle;
+
+ status = dcerpc_spoolss_ClosePrinter(p, tctx, &cp);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+
+ return true;
+}
+
+static bool test_WinXP(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ bool ret = true;
+ struct test_spoolss_win_context *ctx, *tmp_ctx;
+ struct policy_handle handle01, handle02, handle03, handle04;
+ /* Sometimes a handle stays unused. In order to make this clear in the
+ * code, the unused_handle structures are used for that. */
+ struct policy_handle unused_handle1, unused_handle2;
+ char *server_name;
+ char *key_pointer;
+
+ ntvfs_init(tctx->lp_ctx);
+
+ ctx = talloc_zero(tctx, struct test_spoolss_win_context);
+ tmp_ctx = talloc_zero(tctx, struct test_spoolss_win_context);
+
+ ret &= test_OpenPrinterSequence(tctx, p, &handle01);
+ ret &= test_GetPrinterData(tctx, p, &handle01,"UISingleJobStatusString",
+ WERR_INVALID_PARAM, 0);
+ torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n");
+
+ server_name = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p));
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle1, server_name, 0);
+
+ ret &= test_EnumPrinters(tctx, p, ctx, 1024);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle02, server_name, 0);
+ ret &= test_GetPrinterData(tctx, p, &handle02, "MajorVersion", WERR_OK,
+ 3);
+ ret &= test_ClosePrinter(tctx, p, &handle02);
+
+ /* If no printers were found, skip all tests that need a printer */
+ if (ctx->printer_count == 0) {
+ goto end_testWinXP;
+ }
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle02,
+ ctx->printer_info[0].info2.printername,
+ PRINTER_ACCESS_USE);
+ ret &= test_GetPrinter(tctx, p, &handle02, ctx, 2, 0);
+
+ torture_assert_str_equal(tctx, ctx->current_info->info2.printername,
+ ctx->printer_info[0].info2.printername,
+ "GetPrinter returned unexpected printername");
+ /*FIXME: Test more components of the PrinterInfo2 struct */
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle03,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_GetPrinter(tctx, p, &handle03, ctx, 0, 1164);
+ ret &= test_GetPrinter(tctx, p, &handle03, ctx, 2, 0);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle04,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_GetPrinter(tctx, p, &handle04, ctx, 2, 0);
+ ret &= test_ClosePrinter(tctx, p, &handle04);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle04,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_GetPrinter(tctx, p, &handle04, ctx, 2, 4096);
+ ret &= test_ClosePrinter(tctx, p, &handle04);
+
+ ret &= test_OpenPrinterAsAdmin(tctx, p,
+ ctx->printer_info[0].info2.printername);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle04,
+ ctx->printer_info[0].info2.printername, PRINTER_READ);
+ ret &= test_GetPrinterData(tctx, p, &handle04,"UISingleJobStatusString",
+ WERR_BADFILE, 0);
+ torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n");
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+
+ ret &= test_EnumJobs(tctx, p, &handle04);
+ ret &= test_GetPrinter(tctx, p, &handle04, ctx, 2, 4096);
+
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+ ret &= test_ClosePrinter(tctx, p, &handle04);
+
+ ret &= test_EnumPrinters(tctx, p, ctx, 1556);
+ ret &= test_GetPrinterDriver2(tctx, p, &handle03);
+ ret &= test_EnumForms(tctx, p, &handle03, 0);
+
+ ret &= test_EnumPrinterKey(tctx, p, &handle03, "", ctx);
+ key_pointer = ctx->printer_keys;
+ while(*key_pointer != '\0') {
+ char *end_pointer;
+ char *key_name;
+
+ for(end_pointer = key_pointer; *end_pointer != '\0';
+ ++end_pointer) {
+ /* Do nothing, just move the pointer */
+ }
+ key_name = talloc_strndup(tctx, key_pointer,
+ end_pointer - key_pointer);
+
+ ret &= test_EnumPrinterKey(tctx, p, &handle03, key_name,
+ tmp_ctx);
+ ret &= test_EnumPrinterDataEx(tctx, p, &handle03, key_name, 0,
+ WERR_OK);
+
+ key_pointer = ++end_pointer;
+ }
+
+ ret &= test_EnumPrinterDataEx(tctx, p, &handle03, "", 0,
+ WERR_INVALID_PARAM);
+
+ ret &= test_GetPrinter(tctx, p, &handle03, tmp_ctx, 2, 0);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_GetPrinter(tctx, p, &handle03, tmp_ctx, 2, 2556);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_GetPrinter(tctx, p, &handle03, tmp_ctx, 7, 0);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_ClosePrinter(tctx, p, &handle03);
+
+ ret &= test_OpenPrinterEx(tctx, p, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, p, &unused_handle2);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle03, server_name, 0);
+ ret &= test_GetPrinterData(tctx, p, &handle03, "W3SvcInstalled",
+ WERR_OK, 0);
+ ret &= test_ClosePrinter(tctx, p, &handle03);
+
+ ret &= test_ClosePrinter(tctx, p, &unused_handle1);
+ ret &= test_ClosePrinter(tctx, p, &handle02);
+
+ ret &= test_OpenPrinterEx(tctx, p, &handle02,
+ ctx->printer_info[0].info2.sharename, 0);
+ ret &= test_GetPrinter(tctx, p, &handle02, tmp_ctx, 2, 0);
+ ret &= test_ClosePrinter(tctx, p, &handle02);
+
+end_testWinXP:
+ ret &= test_ClosePrinter(tctx, p, &handle01);
+
+ talloc_free(tmp_ctx);
+ talloc_free(ctx);
+ return ret;
+}
+
+struct torture_suite *torture_rpc_spoolss_win(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SPOOLSS-WIN");
+
+ struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite,
+ "win", &ndr_table_spoolss);
+
+ torture_rpc_tcase_add_test(tcase, "testWinXP", test_WinXP);
+
+ return suite;
+}
+
diff --git a/source4/torture/rpc/srvsvc.c b/source4/torture/rpc/srvsvc.c
new file mode 100644
index 0000000000..1fe1221b0d
--- /dev/null
+++ b/source4/torture/rpc/srvsvc.c
@@ -0,0 +1,998 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for srvsvc rpc operations
+
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_srvsvc.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "torture/rpc/rpc.h"
+
+/**************************/
+/* srvsvc_NetCharDev */
+/**************************/
+static bool test_NetCharDevGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *devname)
+{
+ NTSTATUS status;
+ struct srvsvc_NetCharDevGetInfo r;
+ uint32_t levels[] = {0, 1};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.device_name = devname;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetCharDevGetInfo level %u on device '%s'\n",
+ r.in.level, r.in.device_name);
+ status = dcerpc_srvsvc_NetCharDevGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevGetInfo failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetCharDevGetInfo failed");
+ }
+
+ return true;
+}
+
+static bool test_NetCharDevControl(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *devname)
+{
+ NTSTATUS status;
+ struct srvsvc_NetCharDevControl r;
+ uint32_t opcodes[] = {0, 1};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.device_name = devname;
+
+ for (i=0;i<ARRAY_SIZE(opcodes);i++) {
+ ZERO_STRUCT(r.out);
+ r.in.opcode = opcodes[i];
+ torture_comment(tctx, "testing NetCharDevControl opcode %u on device '%s'\n",
+ r.in.opcode, r.in.device_name);
+ status = dcerpc_srvsvc_NetCharDevControl(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevControl failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetCharDevControl failed");
+ }
+
+ return true;
+}
+
+static bool test_NetCharDevEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetCharDevEnum r;
+ struct srvsvc_NetCharDevCtr0 c0;
+ uint32_t levels[] = {0, 1};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.ctr.ctr0 = &c0;
+ r.in.ctr.ctr0->count = 0;
+ r.in.ctr.ctr0->array = NULL;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int j;
+
+ ZERO_STRUCT(r.out);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetCharDevEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetCharDevEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetCharDevEnum failed: %s\n", win_errstr(r.out.result));
+ continue;
+ }
+
+ /* call test_NetCharDevGetInfo and test_NetCharDevControl for each returned share */
+ if (r.in.level == 1) {
+ for (j=0;j<r.out.ctr.ctr1->count;j++) {
+ const char *device;
+ device = r.out.ctr.ctr1->array[j].device;
+ if (!test_NetCharDevGetInfo(p, tctx, device)) {
+ return false;
+ }
+ if (!test_NetCharDevControl(p, tctx, device)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetCharDevQ */
+/**************************/
+static bool test_NetCharDevQGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *devicequeue)
+{
+ NTSTATUS status;
+ struct srvsvc_NetCharDevQGetInfo r;
+ uint32_t levels[] = {0, 1};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.queue_name = devicequeue;
+ r.in.user = talloc_asprintf(tctx,"Administrator");
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetCharDevQGetInfo level %u on devicequeue '%s'\n",
+ r.in.level, r.in.queue_name);
+ status = dcerpc_srvsvc_NetCharDevQGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevQGetInfo failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetCharDevQGetInfo failed");
+ }
+
+ return true;
+}
+
+#if 0
+static bool test_NetCharDevQSetInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ const char *devicequeue)
+{
+ NTSTATUS status;
+ struct srvsvc_NetCharDevQSetInfo r;
+ uint32_t parm_error;
+ uint32_t levels[] = {0, 1};
+ int i;
+ bool ret = true;
+
+ r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.queue_name = devicequeue;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ parm_error = 0;
+ r.in.level = levels[i];
+ d_printf("testing NetCharDevQSetInfo level %u on devicequeue '%s'\n",
+ r.in.level, devicequeue);
+ switch (r.in.level) {
+ case 0:
+ r.in.info.info0 = talloc(mem_ctx, struct srvsvc_NetCharDevQInfo0);
+ r.in.info.info0->device = r.in.queue_name;
+ break;
+ case 1:
+ r.in.info.info1 = talloc(mem_ctx, struct srvsvc_NetCharDevQInfo1);
+ r.in.info.info1->device = r.in.queue_name;
+ r.in.info.info1->priority = 0x000;
+ r.in.info.info1->devices = r.in.queue_name;
+ r.in.info.info1->users = 0x000;
+ r.in.info.info1->num_ahead = 0x000;
+ break;
+ default:
+ break;
+ }
+ r.in.parm_error = &parm_error;
+ status = dcerpc_srvsvc_NetCharDevQSetInfo(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("NetCharDevQSetInfo level %u on devicequeue '%s' failed - %s\n",
+ r.in.level, r.in.queue_name, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ d_printf("NetCharDevQSetInfo level %u on devicequeue '%s' failed - %s\n",
+ r.in.level, r.in.queue_name, win_errstr(r.out.result));
+ continue;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+static bool test_NetCharDevQEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetCharDevQEnum r;
+ struct srvsvc_NetCharDevQCtr0 c0;
+ uint32_t levels[] = {0, 1};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.user = talloc_asprintf(tctx,"%s","Administrator");
+ r.in.ctr.ctr0 = &c0;
+ r.in.ctr.ctr0->count = 0;
+ r.in.ctr.ctr0->array = NULL;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int j;
+
+ ZERO_STRUCT(r.out);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetCharDevQEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetCharDevQEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetCharDevQEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetCharDevQEnum failed: %s\n", win_errstr(r.out.result));
+ continue;
+ }
+
+ /* call test_NetCharDevGetInfo and test_NetCharDevControl for each returned share */
+ if (r.in.level == 1) {
+ for (j=0;j<r.out.ctr.ctr1->count;j++) {
+ const char *device;
+ device = r.out.ctr.ctr1->array[j].device;
+ if (!test_NetCharDevQGetInfo(p, tctx, device)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetConn */
+/**************************/
+static bool test_NetConnEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetConnEnum r;
+ struct srvsvc_NetConnCtr0 c0;
+ uint32_t levels[] = {0, 1};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.path = talloc_asprintf(tctx,"%s","ADMIN$");
+ r.in.ctr.ctr0 = &c0;
+ r.in.ctr.ctr0->count = 0;
+ r.in.ctr.ctr0->array = NULL;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetConnEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetConnEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetConnEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetConnEnum failed: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetFile */
+/**************************/
+static bool test_NetFileEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetFileEnum r;
+ struct srvsvc_NetFileCtr3 c3;
+ uint32_t levels[] = {2, 3};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.path = NULL;
+ r.in.user = NULL;
+ r.in.ctr.ctr3 = &c3;
+ r.in.ctr.ctr3->count = 0;
+ r.in.ctr.ctr3->array = NULL;
+ r.in.max_buffer = (uint32_t)4096;
+ r.in.resume_handle = NULL;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetFileEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetFileEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetFileEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetFileEnum failed: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetSess */
+/**************************/
+static bool test_NetSessEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetSessEnum r;
+ struct srvsvc_NetSessCtr0 c0;
+ uint32_t levels[] = {0, 1, 2, 10, 502};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.client = NULL;
+ r.in.user = NULL;
+ r.in.ctr.ctr0 = &c0;
+ r.in.ctr.ctr0->count = 0;
+ r.in.ctr.ctr0->array = NULL;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetSessEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetSessEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetSessEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetSessEnum failed: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetShare */
+/**************************/
+static bool test_NetShareCheck(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *device_name)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareCheck r;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.device_name = device_name;
+
+ torture_comment(tctx,
+ "testing NetShareCheck on device '%s'\n", r.in.device_name);
+
+ status = dcerpc_srvsvc_NetShareCheck(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_srvsvc_NetShareCheck failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetShareCheck failed");
+
+ return true;
+}
+
+static bool test_NetShareGetInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *sharename, bool admin)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareGetInfo r;
+ struct {
+ uint32_t level;
+ WERROR anon_status;
+ WERROR admin_status;
+ } levels[] = {
+ { 0, WERR_OK, WERR_OK },
+ { 1, WERR_OK, WERR_OK },
+ { 2, WERR_ACCESS_DENIED, WERR_OK },
+ { 501, WERR_OK, WERR_OK },
+ { 502, WERR_ACCESS_DENIED, WERR_OK },
+ { 1005, WERR_OK, WERR_OK },
+ };
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.share_name = sharename;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ WERROR expected;
+
+ r.in.level = levels[i].level;
+ expected = levels[i].anon_status;
+ if (admin) expected = levels[i].admin_status;
+ ZERO_STRUCT(r.out);
+
+ torture_comment(tctx, "testing NetShareGetInfo level %u on share '%s'\n",
+ r.in.level, r.in.share_name);
+
+ status = dcerpc_srvsvc_NetShareGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected, "NetShareGetInfo failed");
+
+ if (r.in.level != 2) continue;
+ if (!r.out.info.info2 || !r.out.info.info2->path) continue;
+ if (!test_NetShareCheck(p, tctx, r.out.info.info2->path)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool test_NetShareGetInfoAdminFull(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareGetInfo(tctx, p, "ADMIN$", true);
+}
+
+static bool test_NetShareGetInfoAdminAnon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareGetInfo(tctx, p, "ADMIN$", false);
+}
+
+static bool test_NetShareAddSetDel(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareAdd a;
+ struct srvsvc_NetShareSetInfo r;
+ struct srvsvc_NetShareGetInfo q;
+ struct srvsvc_NetShareDel d;
+ struct {
+ uint32_t level;
+ WERROR expected;
+ } levels[] = {
+ { 0, WERR_UNKNOWN_LEVEL },
+ { 1, WERR_OK },
+ { 2, WERR_OK },
+ { 501, WERR_UNKNOWN_LEVEL },
+ { 502, WERR_OK },
+ { 1004, WERR_OK },
+ { 1005, WERR_OK },
+ { 1006, WERR_OK },
+/* { 1007, WERR_OK }, */
+ { 1501, WERR_OK },
+ };
+ int i;
+
+ a.in.server_unc = r.in.server_unc = q.in.server_unc = d.in.server_unc =
+ talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.share_name = talloc_strdup(tctx, "testshare");
+
+ a.in.level = 2;
+ a.in.info.info2 = talloc(tctx, struct srvsvc_NetShareInfo2);
+ a.in.info.info2->name = r.in.share_name;
+ a.in.info.info2->type = STYPE_DISKTREE;
+ a.in.info.info2->comment = talloc_strdup(tctx, "test comment");
+ a.in.info.info2->permissions = 123434566;
+ a.in.info.info2->max_users = -1;
+ a.in.info.info2->current_users = 0;
+ a.in.info.info2->path = talloc_strdup(tctx, "C:\\");
+ a.in.info.info2->password = NULL;
+
+ a.in.parm_error = NULL;
+
+ status = dcerpc_srvsvc_NetShareAdd(p, tctx, &a);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareAdd level 2 on share 'testshare' failed");
+ torture_assert_werr_ok(tctx, a.out.result, "NetShareAdd level 2 on share 'testshare' failed");
+
+ r.in.parm_error = NULL;
+
+ q.in.level = 502;
+
+ for (i = 0; i < ARRAY_SIZE(levels); i++) {
+
+ r.in.level = levels[i].level;
+ ZERO_STRUCT(r.out);
+
+ torture_comment(tctx, "testing NetShareSetInfo level %u on share '%s'\n",
+ r.in.level, r.in.share_name);
+
+ switch (levels[i].level) {
+ case 0:
+ r.in.info.info0 = talloc(tctx, struct srvsvc_NetShareInfo0);
+ r.in.info.info0->name = r.in.share_name;
+ break;
+ case 1:
+ r.in.info.info1 = talloc(tctx, struct srvsvc_NetShareInfo1);
+ r.in.info.info1->name = r.in.share_name;
+ r.in.info.info1->type = STYPE_DISKTREE;
+ r.in.info.info1->comment = talloc_strdup(tctx, "test comment 1");
+ break;
+ case 2:
+ r.in.info.info2 = talloc(tctx, struct srvsvc_NetShareInfo2);
+ r.in.info.info2->name = r.in.share_name;
+ r.in.info.info2->type = STYPE_DISKTREE;
+ r.in.info.info2->comment = talloc_strdup(tctx, "test comment 2");
+ r.in.info.info2->permissions = 0;
+ r.in.info.info2->max_users = 2;
+ r.in.info.info2->current_users = 1;
+ r.in.info.info2->path = talloc_strdup(tctx, "::BLaH::"); /* "C:\\"); */
+ r.in.info.info2->password = NULL;
+ break;
+ case 501:
+ r.in.info.info501 = talloc(tctx, struct srvsvc_NetShareInfo501);
+ r.in.info.info501->name = r.in.share_name;
+ r.in.info.info501->type = STYPE_DISKTREE;
+ r.in.info.info501->comment = talloc_strdup(tctx, "test comment 501");
+ r.in.info.info501->csc_policy = 0;
+ break;
+ case 502:
+ r.in.info.info502 = talloc(tctx, struct srvsvc_NetShareInfo502);
+ r.in.info.info502->name = r.in.share_name;
+ r.in.info.info502->type = STYPE_DISKTREE;
+ r.in.info.info502->comment = talloc_strdup(tctx, "test comment 502");
+ r.in.info.info502->permissions = 0;
+ r.in.info.info502->max_users = 502;
+ r.in.info.info502->current_users = 1;
+ r.in.info.info502->path = talloc_strdup(tctx, "C:\\");
+ r.in.info.info502->password = NULL;
+ r.in.info.info502->unknown = 0;
+ r.in.info.info502->sd = NULL;
+ break;
+ case 1004:
+ r.in.info.info1004 = talloc(tctx, struct srvsvc_NetShareInfo1004);
+ r.in.info.info1004->comment = talloc_strdup(tctx, "test comment 1004");
+ break;
+ case 1005:
+ r.in.info.info1005 = talloc(tctx, struct srvsvc_NetShareInfo1005);
+ r.in.info.info1005->dfs_flags = 0;
+ break;
+ case 1006:
+ r.in.info.info1006 = talloc(tctx, struct srvsvc_NetShareInfo1006);
+ r.in.info.info1006->max_users = 1006;
+ break;
+/* case 1007:
+ r.in.info.info1007 = talloc(tctx, struct srvsvc_NetShareInfo1007);
+ r.in.info.info1007->flags = 0;
+ r.in.info.info1007->alternate_directory_name = talloc_strdup(tctx, "test");
+ break;
+*/
+ case 1501:
+ r.in.info.info1501 = talloc_zero(tctx, struct sec_desc_buf);
+ break;
+ }
+
+ status = dcerpc_srvsvc_NetShareSetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed");
+ torture_assert_werr_equal(tctx, r.out.result, levels[i].expected, "NetShareSetInfo failed");
+
+ q.in.share_name = r.in.share_name;
+
+ status = dcerpc_srvsvc_NetShareGetInfo(p, tctx, &q);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed");
+ torture_assert_werr_ok(tctx, q.out.result, "NetShareGetInfo failed");
+
+ torture_assert_str_equal(tctx, q.out.info.info502->name, r.in.share_name,
+ "share name invalid");
+
+ switch (levels[i].level) {
+ case 0:
+ break;
+ case 1:
+ torture_assert_str_equal(tctx, q.out.info.info502->comment, "test comment 1", "comment");
+ break;
+ case 2:
+ torture_assert_str_equal(tctx, q.out.info.info502->comment, "test comment 2", "comment");
+ torture_assert_int_equal(tctx, q.out.info.info2->max_users, 2, "max users");
+ torture_assert_str_equal(tctx, q.out.info.info2->path, "C:\\", "path");
+ break;
+ case 501:
+ torture_assert_str_equal(tctx, q.out.info.info501->comment, "test comment 501", "comment");
+ break;
+ case 502:
+ torture_assert_str_equal(tctx, q.out.info.info502->comment, "test comment 502", "comment");
+ torture_assert_int_equal(tctx, q.out.info.info2->max_users, 502, "max users");
+ torture_assert_str_equal(tctx, q.out.info.info2->path, "C:\\", "path");
+ break;
+ case 1004:
+ torture_assert_str_equal(tctx, q.out.info.info502->comment, "test comment 1004",
+ "comment");
+ break;
+ case 1005:
+ break;
+ case 1006:
+ torture_assert_int_equal(tctx, q.out.info.info2->max_users, 1006, "Max users");
+ break;
+/* case 1007:
+ break;
+*/
+ case 1501:
+ break;
+ }
+ }
+
+ d.in.share_name = r.in.share_name;
+ d.in.reserved = 0;
+
+ status = dcerpc_srvsvc_NetShareDel(p, tctx, &d);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareDel on share 'testshare502' failed");
+ torture_assert_werr_ok(tctx, a.out.result, "NetShareDel on share 'testshare502' failed");
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetShare */
+/**************************/
+static bool test_NetShareEnumAll(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ bool admin)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareEnumAll r;
+ struct srvsvc_NetShareCtr0 c0;
+ struct {
+ uint32_t level;
+ WERROR anon_status;
+ WERROR admin_status;
+ } levels[] = {
+ { 0, WERR_OK, WERR_OK },
+ { 1, WERR_OK, WERR_OK },
+ { 2, WERR_ACCESS_DENIED, WERR_OK },
+ { 501, WERR_ACCESS_DENIED, WERR_OK },
+ { 502, WERR_ACCESS_DENIED, WERR_OK },
+ };
+ int i;
+ uint32_t resume_handle;
+
+ ZERO_STRUCT(c0);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.ctr.ctr0 = &c0;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = &resume_handle;
+ r.out.resume_handle = &resume_handle;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int j;
+ WERROR expected;
+
+ r.in.level = levels[i].level;
+ expected = levels[i].anon_status;
+ if (admin) expected = levels[i].admin_status;
+
+ ZERO_STRUCT(r.out);
+ resume_handle = 0;
+
+ torture_comment(tctx, "testing NetShareEnumAll level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetShareEnumAll(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareEnumAll failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected, "NetShareEnumAll failed");
+
+ /* call srvsvc_NetShareGetInfo for each returned share */
+ if (r.in.level == 2 && r.out.ctr.ctr2) {
+ for (j=0;j<r.out.ctr.ctr2->count;j++) {
+ const char *name;
+ name = r.out.ctr.ctr2->array[j].name;
+ if (!test_NetShareGetInfo(tctx, p, name, admin)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_NetShareEnumAllFull(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareEnumAll(tctx, p, true);
+}
+
+static bool test_NetShareEnumAllAnon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareEnumAll(tctx, p, false);
+}
+
+static bool test_NetShareEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p, bool admin)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareEnum r;
+ struct srvsvc_NetShareCtr0 c0;
+ struct {
+ uint32_t level;
+ WERROR anon_status;
+ WERROR admin_status;
+ } levels[] = {
+ { 0, WERR_OK, WERR_OK },
+ { 1, WERR_OK, WERR_OK },
+ { 2, WERR_ACCESS_DENIED, WERR_OK },
+ { 501, WERR_UNKNOWN_LEVEL, WERR_UNKNOWN_LEVEL },
+ { 502, WERR_ACCESS_DENIED, WERR_OK },
+ };
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.ctr.ctr0 = &c0;
+ r.in.ctr.ctr0->count = 0;
+ r.in.ctr.ctr0->array = NULL;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ WERROR expected;
+
+ r.in.level = levels[i].level;
+ expected = levels[i].anon_status;
+ if (admin) expected = levels[i].admin_status;
+
+ ZERO_STRUCT(r.out);
+
+ torture_comment(tctx, "testing NetShareEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetShareEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareEnum failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected, "NetShareEnum failed");
+ }
+
+ return true;
+}
+
+static bool test_NetShareEnumFull(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareEnum(tctx, p, true);
+}
+
+static bool test_NetShareEnumAnon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareEnum(tctx, p, false);
+}
+
+/**************************/
+/* srvsvc_NetSrv */
+/**************************/
+static bool test_NetSrvGetInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetSrvGetInfo r;
+ struct srvsvc_NetSrvInfo503 i503;
+ uint32_t levels[] = {100, 101, 102, 502, 503};
+ int i;
+ uint32_t resume_handle;
+
+ ZERO_STRUCT(i503);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ resume_handle = 0;
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetSrvGetInfo level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetSrvGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetSrvGetInfo failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "NetSrvGetInfo failed: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetDisk */
+/**************************/
+static bool test_NetDiskEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetDiskEnum r;
+ uint32_t levels[] = {0};
+ int i;
+ uint32_t resume_handle=0;
+
+ ZERO_STRUCT(r.in);
+ r.in.server_unc = NULL;
+ r.in.resume_handle = &resume_handle;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetDiskEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetDiskEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetDiskEnum failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetDiskEnum failed");
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetTransport */
+/**************************/
+static bool test_NetTransportEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetTransportEnum r;
+ struct srvsvc_NetTransportCtr0 c0;
+ uint32_t levels[] = {0, 1};
+ int i;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s", dcerpc_server_name(p));
+ r.in.transports.ctr0 = &c0;
+ r.in.transports.ctr0->count = 0;
+ r.in.transports.ctr0->array = NULL;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCT(r.out);
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetTransportEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetTransportEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetTransportEnum failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "unexpected result: %s\n", win_errstr(r.out.result));
+ }
+ }
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetRemoteTOD */
+/**************************/
+static bool test_NetRemoteTOD(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetRemoteTOD r;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+
+ ZERO_STRUCT(r.out);
+ torture_comment(tctx, "testing NetRemoteTOD\n");
+ status = dcerpc_srvsvc_NetRemoteTOD(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetRemoteTOD failed");
+ torture_assert_werr_ok(tctx, r.out.result, "NetRemoteTOD failed");
+
+ return true;
+}
+
+/**************************/
+/* srvsvc_NetName */
+/**************************/
+
+static bool test_NetNameValidate(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct srvsvc_NetNameValidate r;
+ char *invalidc;
+ char *name;
+ int i, n, min, max;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.flags = 0x0;
+
+ d_printf("testing NetNameValidate\n");
+
+ /* valid path types only between 1 and 13 */
+ for (i = 1; i < 14; i++) {
+
+again:
+ /* let's limit ourselves to a maximum of 4096 bytes */
+ r.in.name = name = talloc_array(tctx, char, 4097);
+ max = 4096;
+ min = 0;
+ n = max;
+
+ while (1) {
+
+ /* Find maximum length accepted by this type */
+ ZERO_STRUCT(r.out);
+ r.in.name_type = i;
+ memset(name, 'A', n);
+ name[n] = '\0';
+
+ status = dcerpc_srvsvc_NetNameValidate(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("NetNameValidate failed while checking maximum size (%s)\n",
+ nt_errstr(status));
+ break;
+ }
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+ min = n;
+ n += (max - min + 1)/2;
+ continue;
+
+ } else {
+ if ((min + 1) >= max) break; /* found it */
+
+ max = n;
+ n -= (max - min)/2;
+ continue;
+ }
+ }
+
+ talloc_free(name);
+
+ d_printf("Maximum length for type %2d, flags %08x: %d\n", i, r.in.flags, max);
+
+ /* find invalid chars for this type check only ASCII between 0x20 and 0x7e */
+
+ invalidc = talloc_strdup(tctx, "");
+
+ for (n = 0x20; n < 0x7e; n++) {
+ r.in.name = name = talloc_asprintf(tctx, "%c", (char)n);
+
+ status = dcerpc_srvsvc_NetNameValidate(p, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("NetNameValidate failed while checking valid chars (%s)\n",
+ nt_errstr(status));
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ invalidc = talloc_asprintf_append_buffer(invalidc, "%c", (char)n);
+ }
+
+ talloc_free(name);
+ }
+
+ d_printf(" Invalid chars for type %2d, flags %08x: \"%s\"\n", i, r.in.flags, invalidc);
+
+ /* only two values are accepted for flags: 0x0 and 0x80000000 */
+ if (r.in.flags == 0x0) {
+ r.in.flags = 0x80000000;
+ goto again;
+ }
+
+ r.in.flags = 0x0;
+ }
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_srvsvc(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SRVSVC");
+ struct torture_rpc_tcase *tcase;
+ struct torture_test *test;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "srvsvc (admin access)", &ndr_table_srvsvc);
+
+ torture_rpc_tcase_add_test(tcase, "NetCharDevEnum", test_NetCharDevEnum);
+ torture_rpc_tcase_add_test(tcase, "NetCharDevQEnum", test_NetCharDevQEnum);
+ torture_rpc_tcase_add_test(tcase, "NetConnEnum", test_NetConnEnum);
+ torture_rpc_tcase_add_test(tcase, "NetFileEnum", test_NetFileEnum);
+ torture_rpc_tcase_add_test(tcase, "NetSessEnum", test_NetSessEnum);
+ torture_rpc_tcase_add_test(tcase, "NetShareEnumAll", test_NetShareEnumAllFull);
+ torture_rpc_tcase_add_test(tcase, "NetSrvGetInfo", test_NetSrvGetInfo);
+ torture_rpc_tcase_add_test(tcase, "NetDiskEnum", test_NetDiskEnum);
+ torture_rpc_tcase_add_test(tcase, "NetTransportEnum", test_NetTransportEnum);
+ torture_rpc_tcase_add_test(tcase, "NetRemoteTOD", test_NetRemoteTOD);
+ torture_rpc_tcase_add_test(tcase, "NetShareEnum", test_NetShareEnumFull);
+ torture_rpc_tcase_add_test(tcase, "NetShareGetInfo", test_NetShareGetInfoAdminFull);
+ test = torture_rpc_tcase_add_test(tcase, "NetShareAddSetDel",
+ test_NetShareAddSetDel);
+ test->dangerous = true;
+ torture_rpc_tcase_add_test(tcase, "NetNameValidate", test_NetNameValidate);
+
+ tcase = torture_suite_add_anon_rpc_iface_tcase(suite,
+ "srvsvc anonymous access",
+ &ndr_table_srvsvc);
+
+ torture_rpc_tcase_add_test(tcase, "NetShareEnumAll",
+ test_NetShareEnumAllAnon);
+ torture_rpc_tcase_add_test(tcase, "NetShareEnum",
+ test_NetShareEnumAnon);
+ torture_rpc_tcase_add_test(tcase, "NetShareGetInfo",
+ test_NetShareGetInfoAdminAnon);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/svcctl.c b/source4/torture/rpc/svcctl.c
new file mode 100644
index 0000000000..c9006baaf5
--- /dev/null
+++ b/source4/torture/rpc/svcctl.c
@@ -0,0 +1,131 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for srvsvc rpc operations
+
+ Copyright (C) Jelmer Vernooij 2004
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_svcctl_c.h"
+#include "torture/rpc/rpc.h"
+
+static bool test_OpenSCManager(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *h)
+{
+ struct svcctl_OpenSCManagerW r;
+
+ r.in.MachineName = NULL;
+ r.in.DatabaseName = NULL;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = h;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_OpenSCManagerW(p, tctx, &r),
+ "OpenSCManager failed!");
+
+ return true;
+}
+
+static bool test_CloseServiceHandle(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *h)
+{
+ struct svcctl_CloseServiceHandle r;
+
+ r.in.handle = h;
+ r.out.handle = h;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_CloseServiceHandle(p, tctx, &r),
+ "CloseServiceHandle failed");
+
+ return true;
+}
+
+static bool test_EnumServicesStatus(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct svcctl_EnumServicesStatusW r;
+ struct policy_handle h;
+ int i;
+ NTSTATUS status;
+ uint32_t resume_handle = 0;
+ struct ENUM_SERVICE_STATUS *service = NULL;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ r.in.handle = &h;
+ r.in.type = SERVICE_TYPE_WIN32;
+ r.in.state = SERVICE_STATE_ALL;
+ r.in.buf_size = 0;
+ r.in.resume_handle = &resume_handle;
+ r.out.service = NULL;
+ r.out.resume_handle = &resume_handle;
+ r.out.services_returned = 0;
+ r.out.bytes_needed = 0;
+
+ status = dcerpc_svcctl_EnumServicesStatusW(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.in.buf_size = *r.out.bytes_needed;
+ r.out.service = talloc_array(tctx, uint8_t, *r.out.bytes_needed);
+
+ status = dcerpc_svcctl_EnumServicesStatusW(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "EnumServicesStatus failed");
+
+ service = (struct ENUM_SERVICE_STATUS *)r.out.service;
+ }
+
+ for(i = 0; i < *r.out.services_returned; i++) {
+ printf("Type: %d, State: %d\n", service[i].status.type, service[i].status.state);
+ }
+
+ if (!test_CloseServiceHandle(p, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_SCManager(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct policy_handle h;
+
+ if (!test_OpenSCManager(p, tctx, &h))
+ return false;
+
+ if (!test_CloseServiceHandle(p, tctx, &h))
+ return false;
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_svcctl(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "SVCCTL");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "svcctl",
+ &ndr_table_svcctl);
+
+ torture_rpc_tcase_add_test(tcase, "SCManager",
+ test_SCManager);
+ torture_rpc_tcase_add_test(tcase, "EnumServicesStatus",
+ test_EnumServicesStatus);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/testjoin.c b/source4/torture/rpc/testjoin.c
new file mode 100644
index 0000000000..2af8c4f872
--- /dev/null
+++ b/source4/torture/rpc/testjoin.c
@@ -0,0 +1,691 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ utility code to join/leave a domain
+
+ Copyright (C) Andrew Tridgell 2004
+
+ 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/>.
+*/
+
+/*
+ this code is used by other torture modules to join/leave a domain
+ as either a member, bdc or thru a trust relationship
+*/
+
+#include "includes.h"
+#include "torture/torture.h"
+#include "system/time.h"
+#include "lib/crypto/crypto.h"
+#include "libnet/libnet.h"
+#include "lib/cmdline/popt_common.h"
+#include "lib/ldb/include/ldb.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/rpc.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+
+struct test_join {
+ struct dcerpc_pipe *p;
+ struct policy_handle user_handle;
+ struct libnet_JoinDomain *libnet_r;
+ struct dom_sid *dom_sid;
+ const char *dom_netbios_name;
+ const char *dom_dns_name;
+ struct dom_sid *user_sid;
+ struct GUID user_guid;
+ const char *netbios_name;
+};
+
+
+static NTSTATUS DeleteUser_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle, const char *name)
+{
+ NTSTATUS status;
+ struct samr_DeleteUser d;
+ struct policy_handle user_handle;
+ uint32_t rid;
+ struct samr_LookupNames n;
+ struct lsa_String sname;
+ struct samr_OpenUser r;
+
+ sname.string = name;
+
+ n.in.domain_handle = handle;
+ n.in.num_names = 1;
+ n.in.names = &sname;
+
+ status = dcerpc_samr_LookupNames(p, mem_ctx, &n);
+ if (NT_STATUS_IS_OK(status)) {
+ rid = n.out.rids.ids[0];
+ } else {
+ return status;
+ }
+
+ r.in.domain_handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = rid;
+ r.out.user_handle = &user_handle;
+
+ status = dcerpc_samr_OpenUser(p, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenUser(%s) failed - %s\n", name, nt_errstr(status));
+ return status;
+ }
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+ status = dcerpc_samr_DeleteUser(p, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ create a test user in the domain
+ an opaque pointer is returned. Pass it to torture_leave_domain()
+ when finished
+*/
+
+struct test_join *torture_create_testuser(struct torture_context *torture,
+ const char *username,
+ const char *domain,
+ uint16_t acct_type,
+ const char **random_password)
+{
+ NTSTATUS status;
+ struct samr_Connect c;
+ struct samr_CreateUser2 r;
+ struct samr_OpenDomain o;
+ struct samr_LookupDomain l;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ struct policy_handle handle;
+ struct policy_handle domain_handle;
+ uint32_t access_granted;
+ uint32_t rid;
+ DATA_BLOB session_key;
+ struct lsa_String name;
+
+ int policy_min_pw_len = 0;
+ struct test_join *join;
+ char *random_pw;
+ const char *dc_binding = torture_setting_string(torture, "dc_binding", NULL);
+
+ join = talloc(NULL, struct test_join);
+ if (join == NULL) {
+ return NULL;
+ }
+
+ ZERO_STRUCTP(join);
+
+ printf("Connecting to SAMR\n");
+
+ if (dc_binding) {
+ status = dcerpc_pipe_connect(join,
+ &join->p,
+ dc_binding,
+ &ndr_table_samr,
+ cmdline_credentials, NULL, torture->lp_ctx);
+
+ } else {
+ status = torture_rpc_connection(torture,
+ &join->p,
+ &ndr_table_samr);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ c.in.system_name = NULL;
+ c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ c.out.connect_handle = &handle;
+
+ status = dcerpc_samr_Connect(join->p, join, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NET_WRITE_FAULT)) {
+ errstr = dcerpc_errstr(join, join->p->last_fault_code);
+ }
+ printf("samr_Connect failed - %s\n", errstr);
+ return NULL;
+ }
+
+ printf("Opening domain %s\n", domain);
+
+ name.string = domain;
+ l.in.connect_handle = &handle;
+ l.in.domain_name = &name;
+
+ status = dcerpc_samr_LookupDomain(join->p, join, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ talloc_steal(join, l.out.sid);
+ join->dom_sid = l.out.sid;
+ join->dom_netbios_name = talloc_strdup(join, domain);
+ if (!join->dom_netbios_name) goto failed;
+
+ o.in.connect_handle = &handle;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.in.sid = l.out.sid;
+ o.out.domain_handle = &domain_handle;
+
+ status = dcerpc_samr_OpenDomain(join->p, join, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ printf("Creating account %s\n", username);
+
+again:
+ name.string = username;
+ r.in.domain_handle = &domain_handle;
+ r.in.account_name = &name;
+ r.in.acct_flags = acct_type;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.user_handle = &join->user_handle;
+ r.out.access_granted = &access_granted;
+ r.out.rid = &rid;
+
+ status = dcerpc_samr_CreateUser2(join->p, join, &r);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+ status = DeleteUser_byname(join->p, join, &domain_handle, name.string);
+ if (NT_STATUS_IS_OK(status)) {
+ goto again;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("CreateUser2 failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ join->user_sid = dom_sid_add_rid(join, join->dom_sid, rid);
+
+ pwp.in.user_handle = &join->user_handle;
+
+ status = dcerpc_samr_GetUserPwInfo(join->p, join, &pwp);
+ if (NT_STATUS_IS_OK(status)) {
+ policy_min_pw_len = pwp.out.info.min_password_length;
+ }
+
+ random_pw = generate_random_str(join, MAX(8, policy_min_pw_len));
+
+ printf("Setting account password '%s'\n", random_pw);
+
+ ZERO_STRUCT(u);
+ s.in.user_handle = &join->user_handle;
+ s.in.info = &u;
+ s.in.level = 24;
+
+ encode_pw_buffer(u.info24.password.data, random_pw, STR_UNICODE);
+ u.info24.pw_len = strlen(random_pw);
+
+ status = dcerpc_fetch_session_key(join->p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ torture_leave_domain(torture, join);
+ goto failed;
+ }
+
+ arcfour_crypt_blob(u.info24.password.data, 516, &session_key);
+
+ status = dcerpc_samr_SetUserInfo(join->p, join, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ ZERO_STRUCT(u);
+ s.in.user_handle = &join->user_handle;
+ s.in.info = &u;
+ s.in.level = 21;
+
+ u.info21.acct_flags = acct_type | ACB_PWNOEXP;
+ u.info21.fields_present = SAMR_FIELD_ACCT_FLAGS | SAMR_FIELD_DESCRIPTION | SAMR_FIELD_COMMENT | SAMR_FIELD_FULL_NAME;
+
+ u.info21.comment.string = talloc_asprintf(join,
+ "Tortured by Samba4: %s",
+ timestring(join, time(NULL)));
+
+ u.info21.full_name.string = talloc_asprintf(join,
+ "Torture account for Samba4: %s",
+ timestring(join, time(NULL)));
+
+ u.info21.description.string = talloc_asprintf(join,
+ "Samba4 torture account created by host %s: %s",
+ lp_netbios_name(torture->lp_ctx),
+ timestring(join, time(NULL)));
+
+ printf("Resetting ACB flags, force pw change time\n");
+
+ status = dcerpc_samr_SetUserInfo(join->p, join, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ if (random_password) {
+ *random_password = random_pw;
+ }
+
+ return join;
+
+failed:
+ torture_leave_domain(torture, join);
+ return NULL;
+}
+
+
+_PUBLIC_ struct test_join *torture_join_domain(struct torture_context *tctx,
+ const char *machine_name,
+ uint32_t acct_flags,
+ struct cli_credentials **machine_credentials)
+{
+ NTSTATUS status;
+ struct libnet_context *libnet_ctx;
+ struct libnet_JoinDomain *libnet_r;
+ struct test_join *tj;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+
+ tj = talloc(tctx, struct test_join);
+ if (!tj) return NULL;
+
+ libnet_r = talloc(tj, struct libnet_JoinDomain);
+ if (!libnet_r) {
+ talloc_free(tj);
+ return NULL;
+ }
+
+ libnet_ctx = libnet_context_init(tctx->ev, tctx->lp_ctx);
+ if (!libnet_ctx) {
+ talloc_free(tj);
+ return NULL;
+ }
+
+ tj->libnet_r = libnet_r;
+
+ libnet_ctx->cred = cmdline_credentials;
+ libnet_r->in.binding = torture_setting_string(tctx, "binding", NULL);
+ if (!libnet_r->in.binding) {
+ libnet_r->in.binding = talloc_asprintf(libnet_r, "ncacn_np:%s", torture_setting_string(tctx, "host", NULL));
+ }
+ libnet_r->in.level = LIBNET_JOINDOMAIN_SPECIFIED;
+ libnet_r->in.netbios_name = machine_name;
+ libnet_r->in.account_name = talloc_asprintf(libnet_r, "%s$", machine_name);
+ if (!libnet_r->in.account_name) {
+ talloc_free(tj);
+ return NULL;
+ }
+
+ libnet_r->in.acct_type = acct_flags;
+ libnet_r->in.recreate_account = true;
+
+ status = libnet_JoinDomain(libnet_ctx, libnet_r, libnet_r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (libnet_r->out.error_string) {
+ DEBUG(0, ("Domain join failed - %s\n", libnet_r->out.error_string));
+ } else {
+ DEBUG(0, ("Domain join failed - %s\n", nt_errstr(status)));
+ }
+ talloc_free(tj);
+ return NULL;
+ }
+ tj->p = libnet_r->out.samr_pipe;
+ tj->user_handle = *libnet_r->out.user_handle;
+ tj->dom_sid = libnet_r->out.domain_sid;
+ talloc_steal(tj, libnet_r->out.domain_sid);
+ tj->dom_netbios_name = libnet_r->out.domain_name;
+ talloc_steal(tj, libnet_r->out.domain_name);
+ tj->dom_dns_name = libnet_r->out.realm;
+ talloc_steal(tj, libnet_r->out.realm);
+ tj->user_guid = libnet_r->out.account_guid;
+ tj->netbios_name = talloc_strdup(tj, machine_name);
+ if (!tj->netbios_name) {
+ talloc_free(tj);
+ return NULL;
+ }
+
+ ZERO_STRUCT(u);
+ s.in.user_handle = &tj->user_handle;
+ s.in.info = &u;
+ s.in.level = 21;
+
+ u.info21.fields_present = SAMR_FIELD_DESCRIPTION | SAMR_FIELD_COMMENT | SAMR_FIELD_FULL_NAME;
+ u.info21.comment.string = talloc_asprintf(tj,
+ "Tortured by Samba4: %s",
+ timestring(tj, time(NULL)));
+ u.info21.full_name.string = talloc_asprintf(tj,
+ "Torture account for Samba4: %s",
+ timestring(tj, time(NULL)));
+
+ u.info21.description.string = talloc_asprintf(tj,
+ "Samba4 torture account created by host %s: %s",
+ lp_netbios_name(tctx->lp_ctx), timestring(tj, time(NULL)));
+
+ status = dcerpc_samr_SetUserInfo(tj->p, tj, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("SetUserInfo (non-critical) failed - %s\n", nt_errstr(status));
+ }
+
+ *machine_credentials = cli_credentials_init(tj);
+ cli_credentials_set_conf(*machine_credentials, tctx->lp_ctx);
+ cli_credentials_set_workstation(*machine_credentials, machine_name, CRED_SPECIFIED);
+ cli_credentials_set_domain(*machine_credentials, libnet_r->out.domain_name, CRED_SPECIFIED);
+ if (libnet_r->out.realm) {
+ cli_credentials_set_realm(*machine_credentials, libnet_r->out.realm, CRED_SPECIFIED);
+ }
+ cli_credentials_set_username(*machine_credentials, libnet_r->in.account_name, CRED_SPECIFIED);
+ cli_credentials_set_password(*machine_credentials, libnet_r->out.join_password, CRED_SPECIFIED);
+ cli_credentials_set_kvno(*machine_credentials, libnet_r->out.kvno);
+ if (acct_flags & ACB_SVRTRUST) {
+ cli_credentials_set_secure_channel_type(*machine_credentials,
+ SEC_CHAN_BDC);
+ } else if (acct_flags & ACB_WSTRUST) {
+ cli_credentials_set_secure_channel_type(*machine_credentials,
+ SEC_CHAN_WKSTA);
+ } else {
+ DEBUG(0, ("Invalid account type specificed to torture_join_domain\n"));
+ talloc_free(*machine_credentials);
+ return NULL;
+ }
+
+ return tj;
+}
+
+struct dcerpc_pipe *torture_join_samr_pipe(struct test_join *join)
+{
+ return join->p;
+}
+
+struct policy_handle *torture_join_samr_user_policy(struct test_join *join)
+{
+ return &join->user_handle;
+}
+
+static NTSTATUS torture_leave_ads_domain(struct torture_context *torture,
+ TALLOC_CTX *mem_ctx,
+ struct libnet_JoinDomain *libnet_r)
+{
+ int rtn;
+ TALLOC_CTX *tmp_ctx;
+
+ struct ldb_dn *server_dn;
+ struct ldb_context *ldb_ctx;
+
+ char *remote_ldb_url;
+
+ /* Check if we are a domain controller. If not, exit. */
+ if (!libnet_r->out.server_dn_str) {
+ return NT_STATUS_OK;
+ }
+
+ tmp_ctx = talloc_named(mem_ctx, 0, "torture_leave temporary context");
+ if (!tmp_ctx) {
+ libnet_r->out.error_string = NULL;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb_ctx = ldb_init(tmp_ctx, torture->ev);
+ if (!ldb_ctx) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* Remove CN=Servers,... entry from the AD. */
+ server_dn = ldb_dn_new(tmp_ctx, ldb_ctx, libnet_r->out.server_dn_str);
+ if (! ldb_dn_validate(server_dn)) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ remote_ldb_url = talloc_asprintf(tmp_ctx, "ldap://%s", libnet_r->out.samr_binding->host);
+ if (!remote_ldb_url) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ldb_set_opaque(ldb_ctx, "credentials", cmdline_credentials);
+
+ rtn = ldb_connect(ldb_ctx, remote_ldb_url, 0, NULL);
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rtn = ldb_delete(ldb_ctx, server_dn);
+ if (rtn != 0) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ DEBUG(0, ("%s removed successfully.\n", libnet_r->out.server_dn_str));
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+/*
+ leave the domain, deleting the machine acct
+*/
+
+_PUBLIC_ void torture_leave_domain(struct torture_context *torture, struct test_join *join)
+{
+ struct samr_DeleteUser d;
+ NTSTATUS status;
+
+ if (!join) {
+ return;
+ }
+ d.in.user_handle = &join->user_handle;
+ d.out.user_handle = &join->user_handle;
+
+ /* Delete machine account */
+ status = dcerpc_samr_DeleteUser(join->p, join, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Delete of machine account %s failed\n",
+ join->netbios_name);
+ } else {
+ printf("Delete of machine account %s was successful.\n",
+ join->netbios_name);
+ }
+
+ if (join->libnet_r) {
+ status = torture_leave_ads_domain(torture, join, join->libnet_r);
+ }
+
+ talloc_free(join);
+}
+
+/*
+ return the dom sid for a test join
+*/
+_PUBLIC_ const struct dom_sid *torture_join_sid(struct test_join *join)
+{
+ return join->dom_sid;
+}
+
+const struct dom_sid *torture_join_user_sid(struct test_join *join)
+{
+ return join->user_sid;
+}
+
+const char *torture_join_netbios_name(struct test_join *join)
+{
+ return join->netbios_name;
+}
+
+const struct GUID *torture_join_user_guid(struct test_join *join)
+{
+ return &join->user_guid;
+}
+
+const char *torture_join_dom_netbios_name(struct test_join *join)
+{
+ return join->dom_netbios_name;
+}
+
+const char *torture_join_dom_dns_name(struct test_join *join)
+{
+ return join->dom_dns_name;
+}
+
+const char *torture_join_server_dn_str(struct test_join *join)
+{
+ if (join->libnet_r) {
+ return join->libnet_r->out.server_dn_str;
+ }
+ return NULL;
+}
+
+
+#if 0 /* Left as the documentation of the join process, but see new implementation in libnet_become_dc.c */
+struct test_join_ads_dc {
+ struct test_join *join;
+};
+
+struct test_join_ads_dc *torture_join_domain_ads_dc(const char *machine_name,
+ const char *domain,
+ struct cli_credentials **machine_credentials)
+{
+ struct test_join_ads_dc *join;
+
+ join = talloc(NULL, struct test_join_ads_dc);
+ if (join == NULL) {
+ return NULL;
+ }
+
+ join->join = torture_join_domain(machine_name,
+ ACB_SVRTRUST,
+ machine_credentials);
+
+ if (!join->join) {
+ return NULL;
+ }
+
+/* W2K: */
+ /* W2K: modify userAccountControl from 4096 to 532480 */
+
+ /* W2K: modify RDN to OU=Domain Controllers and skip the $ from server name */
+
+ /* ask objectVersion of Schema Partition */
+
+ /* ask rIDManagerReferenz of the Domain Partition */
+
+ /* ask fsMORoleOwner of the RID-Manager$ object
+ * returns CN=NTDS Settings,CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ...
+ */
+
+ /* ask for dnsHostName of CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ... */
+
+ /* ask for objectGUID of CN=NTDS Settings,CN=<DC>,CN=Servers,CN=Default-First-Site-Name, ... */
+
+ /* ask for * of CN=Default-First-Site-Name, ... */
+
+ /* search (&(|(objectClass=user)(objectClass=computer))(sAMAccountName=<machine_name>$)) in Domain Partition
+ * attributes : distinguishedName, userAccountControl
+ */
+
+ /* ask * for CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
+ * should fail with noSuchObject
+ */
+
+ /* add CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
+ *
+ * objectClass = server
+ * systemFlags = 50000000
+ * serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
+ */
+
+ /* ask for * of CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ * should fail with noSuchObject
+ */
+
+ /* search for (ncname=<domain_nc>) in CN=Partitions,CN=Configuration,...
+ * attributes: ncName, dnsRoot
+ */
+
+ /* modify add CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
+ * serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
+ * should fail with attributeOrValueExists
+ */
+
+ /* modify replace CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name,...
+ * serverReferenz = CN=<machine_name>,OU=Domain Controllers,...
+ */
+
+ /* DsAddEntry to create the CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ *
+ */
+
+ /* replicate CN=Schema,CN=Configuration,...
+ * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71")
+ *
+ */
+
+ /* replicate CN=Configuration,...
+ * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71")
+ *
+ */
+
+ /* replicate Domain Partition
+ * using DRSUAPI_DS_BIND_GUID_W2K ("6abec3d1-3054-41c8-a362-5a0c5b7d5d71")
+ *
+ */
+
+ /* call DsReplicaUpdateRefs() for all partitions like this:
+ * req1: struct drsuapi_DsReplicaUpdateRefsRequest1
+ * naming_context : *
+ * naming_context: struct drsuapi_DsReplicaObjectIdentifier
+ * __ndr_size : 0x000000ae (174)
+ * __ndr_size_sid : 0x00000000 (0)
+ * guid : 00000000-0000-0000-0000-000000000000
+ * sid : S-0-0
+ * dn : 'CN=Schema,CN=Configuration,DC=w2k3,DC=vmnet1,DC=vm,DC=base'
+ * dest_dsa_dns_name : *
+ * dest_dsa_dns_name : '4a0df188-a0b8-47ea-bbe5-e614723f16dd._msdcs.w2k3.vmnet1.vm.base'
+ * dest_dsa_guid : 4a0df188-a0b8-47ea-bbe5-e614723f16dd
+ * options : 0x0000001c (28)
+ * 0: DRSUAPI_DS_REPLICA_UPDATE_ASYNCHRONOUS_OPERATION
+ * 0: DRSUAPI_DS_REPLICA_UPDATE_WRITEABLE
+ * 1: DRSUAPI_DS_REPLICA_UPDATE_ADD_REFERENCE
+ * 1: DRSUAPI_DS_REPLICA_UPDATE_DELETE_REFERENCE
+ * 1: DRSUAPI_DS_REPLICA_UPDATE_0x00000010
+ *
+ * 4a0df188-a0b8-47ea-bbe5-e614723f16dd is the objectGUID the DsAddEntry() returned for the
+ * CN=NTDS Settings,CN=<machine_name>,CN=Servers,CN=Default-First-Site-Name, ...
+ */
+
+/* W2K3: see libnet/libnet_become_dc.c */
+ return join;
+}
+
+#endif
diff --git a/source4/torture/rpc/unixinfo.c b/source4/torture/rpc/unixinfo.c
new file mode 100644
index 0000000000..cbe8cf0ff1
--- /dev/null
+++ b/source4/torture/rpc/unixinfo.c
@@ -0,0 +1,144 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for unixinfo rpc operations
+
+ Copyright (C) Volker Lendecke 2005
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "torture/rpc/rpc.h"
+#include "librpc/gen_ndr/ndr_unixinfo_c.h"
+#include "libcli/security/security.h"
+
+/**
+ test the SidToUid interface
+*/
+static bool test_sidtouid(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct unixinfo_SidToUid r;
+ struct dom_sid *sid;
+ uint64_t uid;
+
+ sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432");
+ r.in.sid = *sid;
+ r.out.uid = &uid;
+
+ status = dcerpc_unixinfo_SidToUid(p, tctx, &r);
+ if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, status)) {
+ } else torture_assert_ntstatus_ok(tctx, status, "SidToUid failed");
+
+ return true;
+}
+
+/*
+ test the UidToSid interface
+*/
+static bool test_uidtosid(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct unixinfo_UidToSid r;
+ struct dom_sid sid;
+
+ r.in.uid = 1000;
+ r.out.sid = &sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_UidToSid(p, tctx, &r),
+ "UidToSid failed");
+
+ return true;
+}
+
+static bool test_getpwuid(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ uint64_t uids[512];
+ uint32_t num_uids = ARRAY_SIZE(uids);
+ uint32_t i;
+ struct unixinfo_GetPWUid r;
+ NTSTATUS result;
+
+ for (i=0; i<num_uids; i++) {
+ uids[i] = i;
+ }
+
+ r.in.count = &num_uids;
+ r.in.uids = uids;
+ r.out.count = &num_uids;
+ r.out.infos = talloc_array(tctx, struct unixinfo_GetPWUidInfo, num_uids);
+
+ result = dcerpc_unixinfo_GetPWUid(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, result, "GetPWUid failed");
+
+ return true;
+}
+
+/*
+ test the SidToGid interface
+*/
+static bool test_sidtogid(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct unixinfo_SidToGid r;
+ struct dom_sid *sid;
+ uint64_t gid;
+
+ sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432");
+ r.in.sid = *sid;
+ r.out.gid = &gid;
+
+ status = dcerpc_unixinfo_SidToGid(p, tctx, &r);
+ if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, status)) {
+ } else torture_assert_ntstatus_ok(tctx, status, "SidToGid failed");
+
+ return true;
+}
+
+/*
+ test the GidToSid interface
+*/
+static bool test_gidtosid(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct unixinfo_GidToSid r;
+ struct dom_sid sid;
+
+ r.in.gid = 1000;
+ r.out.sid = &sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_GidToSid(p, tctx, &r),
+ "GidToSid failed");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_unixinfo(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+ struct torture_rpc_tcase *tcase;
+
+ suite = torture_suite_create(mem_ctx, "UNIXINFO");
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "unixinfo",
+ &ndr_table_unixinfo);
+
+ torture_rpc_tcase_add_test(tcase, "sidtouid", test_sidtouid);
+ torture_rpc_tcase_add_test(tcase, "uidtosid", test_uidtosid);
+ torture_rpc_tcase_add_test(tcase, "getpwuid", test_getpwuid);
+ torture_rpc_tcase_add_test(tcase, "sidtogid", test_sidtogid);
+ torture_rpc_tcase_add_test(tcase, "gidtosid", test_gidtosid);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/winreg.c b/source4/torture/rpc/winreg.c
new file mode 100644
index 0000000000..8b602ef652
--- /dev/null
+++ b/source4/torture/rpc/winreg.c
@@ -0,0 +1,1908 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for winreg rpc operations
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Günther Deschner 2007
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "torture/rpc/rpc.h"
+
+#define TEST_KEY_BASE "smbtorture test"
+#define TEST_KEY1 TEST_KEY_BASE "\\spottyfoot"
+#define TEST_KEY2 TEST_KEY_BASE "\\with a SD (#1)"
+#define TEST_KEY3 TEST_KEY_BASE "\\with a subkey"
+#define TEST_KEY4 TEST_KEY_BASE "\\sd_tests"
+#define TEST_SUBKEY TEST_KEY3 "\\subkey"
+#define TEST_SUBKEY_SD TEST_KEY4 "\\subkey_sd"
+#define TEST_SUBSUBKEY_SD TEST_KEY4 "\\subkey_sd\\subsubkey_sd"
+
+#define TEST_SID "S-1-5-21-1234567890-1234567890-1234567890-500"
+
+static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s)
+{
+ name->string = s;
+}
+
+static void init_winreg_String(struct winreg_String *name, const char *s)
+{
+ name->name = s;
+ if (s) {
+ name->name_len = 2 * (strlen_m(s) + 1);
+ name->name_size = name->name_len;
+ } else {
+ name->name_len = 0;
+ name->name_size = 0;
+ }
+}
+
+static bool test_GetVersion(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct winreg_GetVersion r;
+ uint32_t v;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.out.version = &v;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_GetVersion(p, tctx, &r),
+ "GetVersion failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetVersion failed");
+
+ return true;
+}
+
+static bool test_NotifyChangeKeyValue(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct winreg_NotifyChangeKeyValue r;
+
+ r.in.handle = handle;
+ r.in.watch_subtree = true;
+ r.in.notify_filter = 0;
+ r.in.unknown = r.in.unknown2 = 0;
+ init_winreg_String(&r.in.string1, NULL);
+ init_winreg_String(&r.in.string2, NULL);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_NotifyChangeKeyValue(p, tctx, &r),
+ "NotifyChangeKeyValue failed");
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx,
+ "NotifyChangeKeyValue failed - %s - not considering\n",
+ win_errstr(r.out.result));
+ return true;
+ }
+
+ return true;
+}
+
+static bool test_CreateKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, const char *name,
+ const char *class)
+{
+ struct winreg_CreateKey r;
+ struct policy_handle newhandle;
+ enum winreg_CreateAction action_taken = 0;
+
+ r.in.handle = handle;
+ r.out.new_handle = &newhandle;
+ init_winreg_String(&r.in.name, name);
+ init_winreg_String(&r.in.keyclass, class);
+ r.in.options = 0x0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.action_taken = r.out.action_taken = &action_taken;
+ r.in.secdesc = NULL;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CreateKey(p, tctx, &r),
+ "CreateKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "CreateKey failed");
+
+ return true;
+}
+
+
+/*
+ createkey testing with a SD
+*/
+static bool test_CreateKey_sd(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle, const char *name,
+ const char *class,
+ struct policy_handle *newhandle)
+{
+ struct winreg_CreateKey r;
+ enum winreg_CreateAction action_taken = 0;
+ struct security_descriptor *sd;
+ DATA_BLOB sdblob;
+ struct winreg_SecBuf secbuf;
+
+ sd = security_descriptor_dacl_create(tctx,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT,
+ NULL);
+
+ torture_assert_ndr_success(tctx,
+ ndr_push_struct_blob(&sdblob, tctx, NULL, sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor),
+ "Failed to push security_descriptor ?!\n");
+
+ secbuf.sd.data = sdblob.data;
+ secbuf.sd.len = sdblob.length;
+ secbuf.sd.size = sdblob.length;
+ secbuf.length = sdblob.length-10;
+ secbuf.inherit = 0;
+
+ r.in.handle = handle;
+ r.out.new_handle = newhandle;
+ init_winreg_String(&r.in.name, name);
+ init_winreg_String(&r.in.keyclass, class);
+ r.in.options = 0x0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.action_taken = r.out.action_taken = &action_taken;
+ r.in.secdesc = &secbuf;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CreateKey(p, tctx, &r),
+ "CreateKey with sd failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "CreateKey with sd failed");
+
+ return true;
+}
+
+static bool _test_GetKeySecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t *sec_info_ptr,
+ WERROR get_werr,
+ struct security_descriptor **sd_out)
+{
+ struct winreg_GetKeySecurity r;
+ struct security_descriptor *sd = NULL;
+ uint32_t sec_info;
+ DATA_BLOB sdblob;
+
+ if (sec_info_ptr) {
+ sec_info = *sec_info_ptr;
+ } else {
+ sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
+ }
+
+ ZERO_STRUCT(r);
+
+ r.in.handle = handle;
+ r.in.sec_info = sec_info;
+ r.in.sd = r.out.sd = talloc_zero(tctx, struct KeySecurityData);
+ r.in.sd->size = 0x1000;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_GetKeySecurity(p, tctx, &r),
+ "GetKeySecurity failed");
+
+ torture_assert_werr_equal(tctx, r.out.result, get_werr,
+ "GetKeySecurity failed");
+
+ sdblob.data = r.out.sd->data;
+ sdblob.length = r.out.sd->len;
+
+ sd = talloc_zero(tctx, struct security_descriptor);
+
+ torture_assert_ndr_success(tctx,
+ ndr_pull_struct_blob(&sdblob, tctx, NULL, sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor),
+ "pull_security_descriptor failed");
+
+ if (p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ }
+
+ if (sd_out) {
+ *sd_out = sd;
+ } else {
+ talloc_free(sd);
+ }
+
+ return true;
+}
+
+static bool test_GetKeySecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct security_descriptor **sd_out)
+{
+ return _test_GetKeySecurity(p, tctx, handle, NULL, WERR_OK, sd_out);
+}
+
+static bool _test_SetKeySecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t *sec_info_ptr,
+ struct security_descriptor *sd,
+ WERROR werr)
+{
+ struct winreg_SetKeySecurity r;
+ struct KeySecurityData *sdata = NULL;
+ DATA_BLOB sdblob;
+ uint32_t sec_info;
+
+ ZERO_STRUCT(r);
+
+ if (sd && (p->conn->flags & DCERPC_DEBUG_PRINT_OUT)) {
+ NDR_PRINT_DEBUG(security_descriptor, sd);
+ }
+
+ torture_assert_ndr_success(tctx,
+ ndr_push_struct_blob(&sdblob, tctx, NULL, sd,
+ (ndr_push_flags_fn_t)ndr_push_security_descriptor),
+ "push_security_descriptor failed");
+
+ sdata = talloc_zero(tctx, struct KeySecurityData);
+ sdata->data = sdblob.data;
+ sdata->size = sdblob.length;
+ sdata->len = sdblob.length;
+
+ if (sec_info_ptr) {
+ sec_info = *sec_info_ptr;
+ } else {
+ sec_info = SECINFO_UNPROTECTED_SACL |
+ SECINFO_UNPROTECTED_DACL;
+ if (sd->owner_sid) {
+ sec_info |= SECINFO_OWNER;
+ }
+ if (sd->group_sid) {
+ sec_info |= SECINFO_GROUP;
+ }
+ if (sd->sacl) {
+ sec_info |= SECINFO_SACL;
+ }
+ if (sd->dacl) {
+ sec_info |= SECINFO_DACL;
+ }
+ }
+
+ r.in.handle = handle;
+ r.in.sec_info = sec_info;
+ r.in.sd = sdata;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_SetKeySecurity(p, tctx, &r),
+ "SetKeySecurity failed");
+
+ torture_assert_werr_equal(tctx, r.out.result, werr,
+ "SetKeySecurity failed");
+
+ return true;
+}
+
+static bool test_SetKeySecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct security_descriptor *sd)
+{
+ return _test_SetKeySecurity(p, tctx, handle, NULL, sd, WERR_OK);
+}
+
+static bool test_CloseKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct winreg_CloseKey r;
+
+ r.in.handle = r.out.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CloseKey(p, tctx, &r),
+ "CloseKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "CloseKey failed");
+
+ return true;
+}
+
+static bool test_FlushKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct winreg_FlushKey r;
+
+ r.in.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_FlushKey(p, tctx, &r),
+ "FlushKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "FlushKey failed");
+
+ return true;
+}
+
+static bool _test_OpenKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *hive_handle,
+ const char *keyname, uint32_t access_mask,
+ struct policy_handle *key_handle,
+ WERROR open_werr,
+ bool *success)
+{
+ struct winreg_OpenKey r;
+
+ r.in.parent_handle = hive_handle;
+ init_winreg_String(&r.in.keyname, keyname);
+ r.in.unknown = 0x00000000;
+ r.in.access_mask = access_mask;
+ r.out.handle = key_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenKey(p, tctx, &r),
+ "OpenKey failed");
+
+ torture_assert_werr_equal(tctx, r.out.result, open_werr,
+ "OpenKey failed");
+
+ if (success && W_ERROR_EQUAL(r.out.result, WERR_OK)) {
+ *success = true;
+ }
+
+ return true;
+}
+
+static bool test_OpenKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *hive_handle,
+ const char *keyname, struct policy_handle *key_handle)
+{
+ return _test_OpenKey(p, tctx, hive_handle, keyname,
+ SEC_FLAG_MAXIMUM_ALLOWED, key_handle,
+ WERR_OK, NULL);
+}
+
+static bool test_Cleanup(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, const char *key)
+{
+ struct winreg_DeleteKey r;
+
+ r.in.handle = handle;
+
+ init_winreg_String(&r.in.key, key);
+ dcerpc_winreg_DeleteKey(p, tctx, &r);
+
+ return true;
+}
+
+static bool _test_GetSetSecurityDescriptor(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ WERROR get_werr,
+ WERROR set_werr)
+{
+ struct security_descriptor *sd = NULL;
+
+ if (!_test_GetKeySecurity(p, tctx, handle, NULL, get_werr, &sd)) {
+ return false;
+ }
+
+ if (!_test_SetKeySecurity(p, tctx, handle, NULL, sd, set_werr)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_SecurityDescriptor(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key)
+{
+ struct policy_handle new_handle;
+ bool ret = true;
+
+ torture_comment(tctx, "SecurityDescriptor get & set\n");
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle,
+ WERR_OK, WERR_OK)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(p, tctx, &new_handle)) {
+ return false;
+ }
+
+ return ret;
+}
+
+static bool _test_SecurityDescriptor(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t access_mask,
+ const char *key,
+ WERROR open_werr,
+ WERROR get_werr,
+ WERROR set_werr)
+{
+ struct policy_handle new_handle;
+ bool ret = true;
+ bool got_key = false;
+
+ if (!_test_OpenKey(p, tctx, handle, key, access_mask, &new_handle,
+ open_werr, &got_key)) {
+ return false;
+ }
+
+ if (!got_key) {
+ return true;
+ }
+
+ if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle,
+ get_werr, set_werr)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(p, tctx, &new_handle)) {
+ return false;
+ }
+
+ return ret;
+}
+
+static bool test_dacl_trustee_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const struct dom_sid *sid)
+{
+ struct security_descriptor *sd = NULL;
+ int i;
+
+ if (!test_GetKeySecurity(p, tctx, handle, &sd)) {
+ return false;
+ }
+
+ if (!sd || !sd->dacl) {
+ return false;
+ }
+
+ for (i = 0; i < sd->dacl->num_aces; i++) {
+ if (dom_sid_equal(&sd->dacl->aces[i].trustee, sid)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool _test_dacl_trustee_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key,
+ const struct dom_sid *sid)
+{
+ struct policy_handle new_handle;
+ bool ret = true;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ ret = test_dacl_trustee_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(p, tctx, &new_handle);
+
+ return ret;
+}
+
+static bool test_sacl_trustee_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const struct dom_sid *sid)
+{
+ struct security_descriptor *sd = NULL;
+ int i;
+ uint32_t sec_info = SECINFO_SACL;
+
+ if (!_test_GetKeySecurity(p, tctx, handle, &sec_info, WERR_OK, &sd)) {
+ return false;
+ }
+
+ if (!sd || !sd->sacl) {
+ return false;
+ }
+
+ for (i = 0; i < sd->sacl->num_aces; i++) {
+ if (dom_sid_equal(&sd->sacl->aces[i].trustee, sid)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool _test_sacl_trustee_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key,
+ const struct dom_sid *sid)
+{
+ struct policy_handle new_handle;
+ bool ret = true;
+
+ if (!_test_OpenKey(p, tctx, handle, key, SEC_FLAG_SYSTEM_SECURITY,
+ &new_handle, WERR_OK, NULL)) {
+ return false;
+ }
+
+ ret = test_sacl_trustee_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(p, tctx, &new_handle);
+
+ return ret;
+}
+
+static bool test_owner_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const struct dom_sid *sid)
+{
+ struct security_descriptor *sd = NULL;
+ uint32_t sec_info = SECINFO_OWNER;
+
+ if (!_test_GetKeySecurity(p, tctx, handle, &sec_info, WERR_OK, &sd)) {
+ return false;
+ }
+
+ if (!sd || !sd->owner_sid) {
+ return false;
+ }
+
+ return dom_sid_equal(sd->owner_sid, sid);
+}
+
+static bool _test_owner_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key,
+ const struct dom_sid *sid)
+{
+ struct policy_handle new_handle;
+ bool ret = true;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ ret = test_owner_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(p, tctx, &new_handle);
+
+ return ret;
+}
+
+static bool test_group_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const struct dom_sid *sid)
+{
+ struct security_descriptor *sd = NULL;
+ uint32_t sec_info = SECINFO_GROUP;
+
+ if (!_test_GetKeySecurity(p, tctx, handle, &sec_info, WERR_OK, &sd)) {
+ return false;
+ }
+
+ if (!sd || !sd->group_sid) {
+ return false;
+ }
+
+ return dom_sid_equal(sd->group_sid, sid);
+}
+
+static bool _test_group_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key,
+ const struct dom_sid *sid)
+{
+ struct policy_handle new_handle;
+ bool ret = true;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ ret = test_group_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(p, tctx, &new_handle);
+
+ return ret;
+}
+
+static bool test_dacl_trustee_flags_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const struct dom_sid *sid,
+ uint8_t flags)
+{
+ struct security_descriptor *sd = NULL;
+ int i;
+
+ if (!test_GetKeySecurity(p, tctx, handle, &sd)) {
+ return false;
+ }
+
+ if (!sd || !sd->dacl) {
+ return false;
+ }
+
+ for (i = 0; i < sd->dacl->num_aces; i++) {
+ if ((dom_sid_equal(&sd->dacl->aces[i].trustee, sid)) &&
+ (sd->dacl->aces[i].flags == flags)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool test_dacl_ace_present(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const struct security_ace *ace)
+{
+ struct security_descriptor *sd = NULL;
+ int i;
+
+ if (!test_GetKeySecurity(p, tctx, handle, &sd)) {
+ return false;
+ }
+
+ if (!sd || !sd->dacl) {
+ return false;
+ }
+
+ for (i = 0; i < sd->dacl->num_aces; i++) {
+ if (security_ace_equal(&sd->dacl->aces[i], ace)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool test_RestoreSecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key,
+ struct security_descriptor *sd)
+{
+ struct policy_handle new_handle;
+ bool ret = true;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!test_SetKeySecurity(p, tctx, &new_handle, sd)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(p, tctx, &new_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_BackupSecurity(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key,
+ struct security_descriptor **sd)
+{
+ struct policy_handle new_handle;
+ bool ret = true;
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!test_GetKeySecurity(p, tctx, &new_handle, sd)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(p, tctx, &new_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_SecurityDescriptorInheritance(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key)
+{
+ /* get sd
+ add ace SEC_ACE_FLAG_CONTAINER_INHERIT
+ set sd
+ get sd
+ check ace
+ add subkey
+ get sd
+ check ace
+ add subsubkey
+ get sd
+ check ace
+ del subsubkey
+ del subkey
+ reset sd
+ */
+
+ struct security_descriptor *sd = NULL;
+ struct security_descriptor *sd_orig = NULL;
+ struct security_ace *ace = NULL;
+ struct policy_handle new_handle;
+ NTSTATUS status;
+ bool ret = true;
+
+ torture_comment(tctx, "SecurityDescriptor inheritance\n");
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!_test_GetKeySecurity(p, tctx, &new_handle, NULL, WERR_OK, &sd)) {
+ return false;
+ }
+
+ sd_orig = security_descriptor_copy(tctx, sd);
+ if (sd_orig == NULL) {
+ return false;
+ }
+
+ ace = security_ace_create(tctx,
+ TEST_SID,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_REQUIRED,
+ SEC_ACE_FLAG_CONTAINER_INHERIT);
+
+ status = security_descriptor_dacl_add(sd, ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to add ace: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ /* FIXME: add further tests for these flags */
+ sd->type |= SEC_DESC_DACL_AUTO_INHERIT_REQ |
+ SEC_DESC_SACL_AUTO_INHERITED;
+
+ if (!test_SetKeySecurity(p, tctx, &new_handle, sd)) {
+ return false;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("new ACE not present!\n");
+ return false;
+ }
+
+ if (!test_CloseKey(p, tctx, &new_handle)) {
+ return false;
+ }
+
+ if (!test_CreateKey(p, tctx, handle, TEST_SUBKEY_SD, NULL)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_OpenKey(p, tctx, handle, TEST_SUBKEY_SD, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("inherited ACE not present!\n");
+ ret = false;
+ goto out;
+ }
+
+ test_CloseKey(p, tctx, &new_handle);
+ if (!test_CreateKey(p, tctx, handle, TEST_SUBSUBKEY_SD, NULL)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_OpenKey(p, tctx, handle, TEST_SUBSUBKEY_SD, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("inherited ACE not present!\n");
+ ret = false;
+ goto out;
+ }
+
+ out:
+ test_CloseKey(p, tctx, &new_handle);
+ test_Cleanup(p, tctx, handle, TEST_SUBKEY_SD);
+ test_RestoreSecurity(p, tctx, handle, key, sd_orig);
+
+ return true;
+}
+
+static bool test_SecurityDescriptorBlockInheritance(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key)
+{
+ /* get sd
+ add ace SEC_ACE_FLAG_NO_PROPAGATE_INHERIT
+ set sd
+ add subkey/subkey
+ get sd
+ check ace
+ get sd from subkey
+ check ace
+ del subkey/subkey
+ del subkey
+ reset sd
+ */
+
+ struct security_descriptor *sd = NULL;
+ struct security_descriptor *sd_orig = NULL;
+ struct security_ace *ace = NULL;
+ struct policy_handle new_handle;
+ struct dom_sid *sid = NULL;
+ NTSTATUS status;
+ bool ret = true;
+ uint8_t ace_flags = 0x0;
+
+ torture_comment(tctx, "SecurityDescriptor inheritance block\n");
+
+ if (!test_OpenKey(p, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!_test_GetKeySecurity(p, tctx, &new_handle, NULL, WERR_OK, &sd)) {
+ return false;
+ }
+
+ sd_orig = security_descriptor_copy(tctx, sd);
+ if (sd_orig == NULL) {
+ return false;
+ }
+
+ ace = security_ace_create(tctx,
+ TEST_SID,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_REQUIRED,
+ SEC_ACE_FLAG_CONTAINER_INHERIT |
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
+
+ status = security_descriptor_dacl_add(sd, ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("failed to add ace: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!_test_SetKeySecurity(p, tctx, &new_handle, NULL, sd, WERR_OK)) {
+ return false;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("new ACE not present!\n");
+ return false;
+ }
+
+ if (!test_CloseKey(p, tctx, &new_handle)) {
+ return false;
+ }
+
+ if (!test_CreateKey(p, tctx, handle, TEST_SUBSUBKEY_SD, NULL)) {
+ return false;
+ }
+
+ if (!test_OpenKey(p, tctx, handle, TEST_SUBSUBKEY_SD, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("inherited ACE present but should not!\n");
+ ret = false;
+ goto out;
+ }
+
+ sid = dom_sid_parse_talloc(tctx, TEST_SID);
+ if (sid == NULL) {
+ return false;
+ }
+
+ if (test_dacl_trustee_present(p, tctx, &new_handle, sid)) {
+ printf("inherited trustee SID present but should not!\n");
+ ret = false;
+ goto out;
+ }
+
+ test_CloseKey(p, tctx, &new_handle);
+
+ if (!test_OpenKey(p, tctx, handle, TEST_SUBKEY_SD, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ printf("inherited ACE present but should not!\n");
+ ret = false;
+ goto out;
+ }
+
+ if (!test_dacl_trustee_flags_present(p, tctx, &new_handle, sid, ace_flags)) {
+ printf("inherited trustee SID with flags 0x%02x not present!\n",
+ ace_flags);
+ ret = false;
+ goto out;
+ }
+
+ out:
+ test_CloseKey(p, tctx, &new_handle);
+ test_Cleanup(p, tctx, handle, TEST_SUBKEY_SD);
+ test_RestoreSecurity(p, tctx, handle, key, sd_orig);
+
+ return ret;
+}
+
+static bool test_SecurityDescriptorsMasks(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key)
+{
+ bool ret = true;
+ int i;
+
+ struct winreg_mask_result_table {
+ uint32_t access_mask;
+ WERROR open_werr;
+ WERROR get_werr;
+ WERROR set_werr;
+ } sd_mask_tests[] = {
+ { 0,
+ WERR_ACCESS_DENIED, WERR_BADFILE, WERR_FOOBAR },
+ { SEC_FLAG_MAXIMUM_ALLOWED,
+ WERR_OK, WERR_OK, WERR_OK },
+ { SEC_STD_WRITE_DAC,
+ WERR_OK, WERR_ACCESS_DENIED, WERR_FOOBAR },
+ { SEC_FLAG_SYSTEM_SECURITY,
+ WERR_OK, WERR_ACCESS_DENIED, WERR_FOOBAR }
+ };
+
+ /* FIXME: before this test can ever run successfully we need a way to
+ * correctly read a NULL security_descritpor in ndr, get the required
+ * length, requery, etc.
+ */
+
+ return true;
+
+ for (i=0; i < ARRAY_SIZE(sd_mask_tests); i++) {
+
+ torture_comment(tctx,
+ "SecurityDescriptor get & set with access_mask: 0x%08x\n",
+ sd_mask_tests[i].access_mask);
+ torture_comment(tctx,
+ "expecting: open %s, get: %s, set: %s\n",
+ win_errstr(sd_mask_tests[i].open_werr),
+ win_errstr(sd_mask_tests[i].get_werr),
+ win_errstr(sd_mask_tests[i].set_werr));
+
+ if (_test_SecurityDescriptor(p, tctx, handle,
+ sd_mask_tests[i].access_mask, key,
+ sd_mask_tests[i].open_werr,
+ sd_mask_tests[i].get_werr,
+ sd_mask_tests[i].set_werr)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+typedef bool (*secinfo_verify_fn)(struct dcerpc_pipe *,
+ struct torture_context *,
+ struct policy_handle *,
+ const char *,
+ const struct dom_sid *);
+
+static bool test_SetSecurityDescriptor_SecInfo(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key,
+ const char *test,
+ uint32_t access_mask,
+ uint32_t sec_info,
+ struct security_descriptor *sd,
+ WERROR set_werr,
+ bool expect_present,
+ bool (*fn) (struct dcerpc_pipe *,
+ struct torture_context *,
+ struct policy_handle *,
+ const char *,
+ const struct dom_sid *),
+ const struct dom_sid *sid)
+{
+ struct policy_handle new_handle;
+ bool open_success = false;
+
+ torture_comment(tctx, "SecurityDescriptor (%s) sets for secinfo: "
+ "0x%08x, access_mask: 0x%08x\n",
+ test, sec_info, access_mask);
+
+ if (!_test_OpenKey(p, tctx, handle, key,
+ access_mask,
+ &new_handle,
+ WERR_OK,
+ &open_success)) {
+ return false;
+ }
+
+ if (!open_success) {
+ printf("key did not open\n");
+ test_CloseKey(p, tctx, &new_handle);
+ return false;
+ }
+
+ if (!_test_SetKeySecurity(p, tctx, &new_handle, &sec_info,
+ sd,
+ set_werr)) {
+ torture_warning(tctx,
+ "SetKeySecurity with secinfo: 0x%08x has failed\n",
+ sec_info);
+ smb_panic("");
+ test_CloseKey(p, tctx, &new_handle);
+ return false;
+ }
+
+ test_CloseKey(p, tctx, &new_handle);
+
+ if (W_ERROR_IS_OK(set_werr)) {
+ bool present;
+ present = fn(p, tctx, handle, key, sid);
+ if ((expect_present) && (!present)) {
+ torture_warning(tctx,
+ "%s sid is not present!\n",
+ test);
+ return false;
+ }
+ if ((!expect_present) && (present)) {
+ torture_warning(tctx,
+ "%s sid is present but not expected!\n",
+ test);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool test_SecurityDescriptorsSecInfo(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key)
+{
+ struct security_descriptor *sd_orig = NULL;
+ struct dom_sid *sid = NULL;
+ bool ret = true;
+ int i, a;
+
+ struct security_descriptor *sd_owner =
+ security_descriptor_dacl_create(tctx,
+ 0,
+ TEST_SID, NULL, NULL);
+
+ struct security_descriptor *sd_group =
+ security_descriptor_dacl_create(tctx,
+ 0,
+ NULL, TEST_SID, NULL);
+
+ struct security_descriptor *sd_dacl =
+ security_descriptor_dacl_create(tctx,
+ 0,
+ NULL, NULL,
+ TEST_SID,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ 0,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ 0,
+ NULL);
+
+ struct security_descriptor *sd_sacl =
+ security_descriptor_sacl_create(tctx,
+ 0,
+ NULL, NULL,
+ TEST_SID,
+ SEC_ACE_TYPE_SYSTEM_AUDIT,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_SUCCESSFUL_ACCESS,
+ NULL);
+
+ struct winreg_secinfo_table {
+ struct security_descriptor *sd;
+ uint32_t sec_info;
+ WERROR set_werr;
+ bool sid_present;
+ secinfo_verify_fn fn;
+ };
+
+ struct winreg_secinfo_table sec_info_owner_tests[] = {
+ { sd_owner, 0, WERR_OK,
+ false, (secinfo_verify_fn)_test_owner_present },
+ { sd_owner, SECINFO_OWNER, WERR_OK,
+ true, (secinfo_verify_fn)_test_owner_present },
+ { sd_owner, SECINFO_GROUP, WERR_INVALID_PARAM },
+ { sd_owner, SECINFO_DACL, WERR_OK,
+ true, (secinfo_verify_fn)_test_owner_present },
+ { sd_owner, SECINFO_SACL, WERR_ACCESS_DENIED },
+ };
+
+ uint32_t sd_owner_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ /* SEC_STD_WRITE_OWNER, */
+ };
+
+ struct winreg_secinfo_table sec_info_group_tests[] = {
+ { sd_group, 0, WERR_OK,
+ false, (secinfo_verify_fn)_test_group_present },
+ { sd_group, SECINFO_OWNER, WERR_INVALID_PARAM },
+ { sd_group, SECINFO_GROUP, WERR_OK,
+ true, (secinfo_verify_fn)_test_group_present },
+ { sd_group, SECINFO_DACL, WERR_OK,
+ true, (secinfo_verify_fn)_test_group_present },
+ { sd_group, SECINFO_SACL, WERR_ACCESS_DENIED },
+ };
+
+ uint32_t sd_group_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ };
+
+ struct winreg_secinfo_table sec_info_dacl_tests[] = {
+ { sd_dacl, 0, WERR_OK,
+ false, (secinfo_verify_fn)_test_dacl_trustee_present },
+ { sd_dacl, SECINFO_OWNER, WERR_INVALID_PARAM },
+ { sd_dacl, SECINFO_GROUP, WERR_INVALID_PARAM },
+ { sd_dacl, SECINFO_DACL, WERR_OK,
+ true, (secinfo_verify_fn)_test_dacl_trustee_present },
+ { sd_dacl, SECINFO_SACL, WERR_ACCESS_DENIED },
+ };
+
+ uint32_t sd_dacl_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ SEC_STD_WRITE_DAC,
+ };
+
+ struct winreg_secinfo_table sec_info_sacl_tests[] = {
+ { sd_sacl, 0, WERR_OK,
+ false, (secinfo_verify_fn)_test_sacl_trustee_present },
+ { sd_sacl, SECINFO_OWNER, WERR_INVALID_PARAM },
+ { sd_sacl, SECINFO_GROUP, WERR_INVALID_PARAM },
+ { sd_sacl, SECINFO_DACL, WERR_OK,
+ false, (secinfo_verify_fn)_test_sacl_trustee_present },
+ { sd_sacl, SECINFO_SACL, WERR_OK,
+ true, (secinfo_verify_fn)_test_sacl_trustee_present },
+ };
+
+ uint32_t sd_sacl_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED | SEC_FLAG_SYSTEM_SECURITY,
+ /* SEC_FLAG_SYSTEM_SECURITY, */
+ };
+
+ sid = dom_sid_parse_talloc(tctx, TEST_SID);
+ if (sid == NULL) {
+ return false;
+ }
+
+ if (!test_BackupSecurity(p, tctx, handle, key, &sd_orig)) {
+ return false;
+ }
+
+ /* OWNER */
+
+ for (i=0; i < ARRAY_SIZE(sec_info_owner_tests); i++) {
+
+ for (a=0; a < ARRAY_SIZE(sd_owner_good_access_masks); a++) {
+
+ if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle,
+ key,
+ "OWNER",
+ sd_owner_good_access_masks[a],
+ sec_info_owner_tests[i].sec_info,
+ sec_info_owner_tests[i].sd,
+ sec_info_owner_tests[i].set_werr,
+ sec_info_owner_tests[i].sid_present,
+ sec_info_owner_tests[i].fn,
+ sid))
+ {
+ printf("test_SetSecurityDescriptor_SecInfo failed for OWNER\n");
+ ret = false;
+ goto out;
+ }
+ }
+ }
+
+ /* GROUP */
+
+ for (i=0; i < ARRAY_SIZE(sec_info_group_tests); i++) {
+
+ for (a=0; a < ARRAY_SIZE(sd_group_good_access_masks); a++) {
+
+ if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle,
+ key,
+ "GROUP",
+ sd_group_good_access_masks[a],
+ sec_info_group_tests[i].sec_info,
+ sec_info_group_tests[i].sd,
+ sec_info_group_tests[i].set_werr,
+ sec_info_group_tests[i].sid_present,
+ sec_info_group_tests[i].fn,
+ sid))
+ {
+ printf("test_SetSecurityDescriptor_SecInfo failed for GROUP\n");
+ ret = false;
+ goto out;
+ }
+ }
+ }
+
+ /* DACL */
+
+ for (i=0; i < ARRAY_SIZE(sec_info_dacl_tests); i++) {
+
+ for (a=0; a < ARRAY_SIZE(sd_dacl_good_access_masks); a++) {
+
+ if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle,
+ key,
+ "DACL",
+ sd_dacl_good_access_masks[a],
+ sec_info_dacl_tests[i].sec_info,
+ sec_info_dacl_tests[i].sd,
+ sec_info_dacl_tests[i].set_werr,
+ sec_info_dacl_tests[i].sid_present,
+ sec_info_dacl_tests[i].fn,
+ sid))
+ {
+ printf("test_SetSecurityDescriptor_SecInfo failed for DACL\n");
+ ret = false;
+ goto out;
+ }
+ }
+ }
+
+ /* SACL */
+
+ for (i=0; i < ARRAY_SIZE(sec_info_sacl_tests); i++) {
+
+ for (a=0; a < ARRAY_SIZE(sd_sacl_good_access_masks); a++) {
+
+ if (!test_SetSecurityDescriptor_SecInfo(p, tctx, handle,
+ key,
+ "SACL",
+ sd_sacl_good_access_masks[a],
+ sec_info_sacl_tests[i].sec_info,
+ sec_info_sacl_tests[i].sd,
+ sec_info_sacl_tests[i].set_werr,
+ sec_info_sacl_tests[i].sid_present,
+ sec_info_sacl_tests[i].fn,
+ sid))
+ {
+ printf("test_SetSecurityDescriptor_SecInfo failed for SACL\n");
+ ret = false;
+ goto out;
+ }
+ }
+ }
+
+ out:
+ test_RestoreSecurity(p, tctx, handle, key, sd_orig);
+
+ return ret;
+}
+
+static bool test_SecurityDescriptors(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key)
+{
+ bool ret = true;
+
+ if (!test_SecurityDescriptor(p, tctx, handle, key)) {
+ printf("test_SecurityDescriptor failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorInheritance(p, tctx, handle, key)) {
+ printf("test_SecurityDescriptorInheritance failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorBlockInheritance(p, tctx, handle, key)) {
+ printf("test_SecurityDescriptorBlockInheritance failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorsSecInfo(p, tctx, handle, key)) {
+ printf("test_SecurityDescriptorsSecInfo failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorsMasks(p, tctx, handle, key)) {
+ printf("test_SecurityDescriptorsMasks failed\n");
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_DeleteKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, const char *key)
+{
+ NTSTATUS status;
+ struct winreg_DeleteKey r;
+
+ r.in.handle = handle;
+ init_winreg_String(&r.in.key, key);
+
+ status = dcerpc_winreg_DeleteKey(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "DeleteKey failed");
+ torture_assert_werr_ok(tctx, r.out.result, "DeleteKey failed");
+
+ return true;
+}
+
+static bool test_QueryInfoKey(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle, char *class)
+{
+ struct winreg_QueryInfoKey r;
+ uint32_t num_subkeys, max_subkeylen, max_subkeysize,
+ num_values, max_valnamelen, max_valbufsize,
+ secdescsize;
+ NTTIME last_changed_time;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.out.num_subkeys = &num_subkeys;
+ r.out.max_subkeylen = &max_subkeylen;
+ r.out.max_subkeysize = &max_subkeysize;
+ r.out.num_values = &num_values;
+ r.out.max_valnamelen = &max_valnamelen;
+ r.out.max_valbufsize = &max_valbufsize;
+ r.out.secdescsize = &secdescsize;
+ r.out.last_changed_time = &last_changed_time;
+
+ r.out.classname = talloc(tctx, struct winreg_String);
+
+ r.in.classname = talloc(tctx, struct winreg_String);
+ init_winreg_String(r.in.classname, class);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryInfoKey(p, tctx, &r),
+ "QueryInfoKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "QueryInfoKey failed");
+
+ return true;
+}
+
+static bool test_key(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, int depth,
+ bool test_security);
+
+static bool test_EnumKey(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, int depth,
+ bool test_security)
+{
+ struct winreg_EnumKey r;
+ struct winreg_StringBuf class, name;
+ NTSTATUS status;
+ NTTIME t = 0;
+
+ class.name = "";
+ class.size = 1024;
+
+ r.in.handle = handle;
+ r.in.enum_index = 0;
+ r.in.name = &name;
+ r.in.keyclass = &class;
+ r.out.name = &name;
+ r.in.last_changed_time = &t;
+
+ do {
+ name.name = NULL;
+ name.size = 1024;
+
+ status = dcerpc_winreg_EnumKey(p, tctx, &r);
+
+ if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) {
+ struct policy_handle key_handle;
+
+ torture_comment(tctx, "EnumKey: %d: %s\n",
+ r.in.enum_index,
+ r.out.name->name);
+
+ if (!test_OpenKey(p, tctx, handle, r.out.name->name,
+ &key_handle)) {
+ } else {
+ test_key(p, tctx, &key_handle,
+ depth + 1, test_security);
+ }
+ }
+
+ r.in.enum_index++;
+
+ } while (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result));
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumKey failed");
+
+ if (!W_ERROR_IS_OK(r.out.result) &&
+ !W_ERROR_EQUAL(r.out.result, WERR_NO_MORE_ITEMS)) {
+ torture_fail(tctx, "EnumKey failed");
+ }
+
+ return true;
+}
+
+static bool test_QueryMultipleValues(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *valuename)
+{
+ struct winreg_QueryMultipleValues r;
+ NTSTATUS status;
+ uint32_t bufsize=0;
+
+ r.in.key_handle = handle;
+ r.in.values = r.out.values = talloc_array(tctx, struct QueryMultipleValue, 1);
+ r.in.values[0].name = talloc(tctx, struct winreg_String);
+ r.in.values[0].name->name = valuename;
+ r.in.values[0].offset = 0;
+ r.in.values[0].length = 0;
+ r.in.values[0].type = 0;
+
+ r.in.num_values = 1;
+ r.in.buffer_size = r.out.buffer_size = talloc(tctx, uint32_t);
+ *r.in.buffer_size = bufsize;
+ do {
+ *r.in.buffer_size = bufsize;
+ r.in.buffer = r.out.buffer = talloc_zero_array(tctx, uint8_t,
+ *r.in.buffer_size);
+
+ status = dcerpc_winreg_QueryMultipleValues(p, tctx, &r);
+
+ if(NT_STATUS_IS_ERR(status))
+ torture_fail(tctx, "QueryMultipleValues failed");
+
+ talloc_free(r.in.buffer);
+ bufsize += 0x20;
+ } while (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA));
+
+ torture_assert_werr_ok(tctx, r.out.result, "QueryMultipleValues failed");
+
+ return true;
+}
+
+static bool test_QueryValue(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *valuename)
+{
+ struct winreg_QueryValue r;
+ NTSTATUS status;
+ enum winreg_Type zero_type = 0;
+ uint32_t offered = 0xfff;
+ uint32_t zero = 0;
+
+ r.in.handle = handle;
+ r.in.data = NULL;
+ r.in.value_name.name = valuename;
+ r.in.type = &zero_type;
+ r.in.size = &offered;
+ r.in.length = &zero;
+
+ status = dcerpc_winreg_QueryValue(p, tctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ torture_fail(tctx, "QueryValue failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "QueryValue failed");
+
+ return true;
+}
+
+static bool test_EnumValue(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, int max_valnamelen,
+ int max_valbufsize)
+{
+ struct winreg_EnumValue r;
+ enum winreg_Type type = 0;
+ uint32_t size = max_valbufsize, zero = 0;
+ bool ret = true;
+ uint8_t buf8;
+ struct winreg_StringBuf name;
+
+ name.name = "";
+ name.size = 1024;
+
+ r.in.handle = handle;
+ r.in.enum_index = 0;
+ r.in.name = &name;
+ r.out.name = &name;
+ r.in.type = &type;
+ r.in.value = &buf8;
+ r.in.length = &zero;
+ r.in.size = &size;
+
+ do {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_EnumValue(p, tctx, &r),
+ "EnumValue failed");
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+ ret &= test_QueryValue(p, tctx, handle,
+ r.out.name->name);
+ ret &= test_QueryMultipleValues(p, tctx, handle,
+ r.out.name->name);
+ }
+
+ r.in.enum_index++;
+ } while (W_ERROR_IS_OK(r.out.result));
+
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NO_MORE_ITEMS,
+ "EnumValue failed");
+
+ return ret;
+}
+
+static bool test_AbortSystemShutdown(struct dcerpc_pipe *p,
+ struct torture_context *tctx)
+{
+ struct winreg_AbortSystemShutdown r;
+ uint16_t server = 0x0;
+
+ r.in.server = &server;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_AbortSystemShutdown(p, tctx, &r),
+ "AbortSystemShutdown failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "AbortSystemShutdown failed");
+
+ return true;
+}
+
+static bool test_InitiateSystemShutdown(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct winreg_InitiateSystemShutdown r;
+ uint16_t hostname = 0x0;
+
+ r.in.hostname = &hostname;
+ r.in.message = talloc(tctx, struct lsa_StringLarge);
+ init_lsa_StringLarge(r.in.message, "spottyfood");
+ r.in.force_apps = 1;
+ r.in.timeout = 30;
+ r.in.reboot = 1;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_InitiateSystemShutdown(p, tctx, &r),
+ "InitiateSystemShutdown failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "InitiateSystemShutdown failed");
+
+ return test_AbortSystemShutdown(p, tctx);
+}
+
+
+static bool test_InitiateSystemShutdownEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct winreg_InitiateSystemShutdownEx r;
+ uint16_t hostname = 0x0;
+
+ r.in.hostname = &hostname;
+ r.in.message = talloc(tctx, struct lsa_StringLarge);
+ init_lsa_StringLarge(r.in.message, "spottyfood");
+ r.in.force_apps = 1;
+ r.in.timeout = 30;
+ r.in.reboot = 1;
+ r.in.reason = 0;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_InitiateSystemShutdownEx(p, tctx, &r),
+ "InitiateSystemShutdownEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "InitiateSystemShutdownEx failed");
+
+ return test_AbortSystemShutdown(p, tctx);
+}
+#define MAX_DEPTH 2 /* Only go this far down the tree */
+
+static bool test_key(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, int depth,
+ bool test_security)
+{
+ if (depth == MAX_DEPTH)
+ return true;
+
+ if (!test_QueryInfoKey(p, tctx, handle, NULL)) {
+ }
+
+ if (!test_NotifyChangeKeyValue(p, tctx, handle)) {
+ }
+
+ if (test_security && !test_GetKeySecurity(p, tctx, handle, NULL)) {
+ }
+
+ if (!test_EnumKey(p, tctx, handle, depth, test_security)) {
+ }
+
+ if (!test_EnumValue(p, tctx, handle, 0xFF, 0xFFFF)) {
+ }
+
+ test_CloseKey(p, tctx, handle);
+
+ return true;
+}
+
+typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_pipe *, TALLOC_CTX *, void *);
+
+static bool test_Open_Security(struct torture_context *tctx,
+ struct dcerpc_pipe *p, void *userdata)
+{
+ struct policy_handle handle, newhandle;
+ bool ret = true, created2 = false;
+ bool created4 = false;
+ struct winreg_OpenHKLM r;
+
+ winreg_open_fn open_fn = userdata;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx, open_fn(p, tctx, &r),
+ "open");
+
+ test_Cleanup(p, tctx, &handle, TEST_KEY_BASE);
+
+ if (!test_CreateKey(p, tctx, &handle, TEST_KEY_BASE, NULL)) {
+ torture_comment(tctx,
+ "CreateKey (TEST_KEY_BASE) failed\n");
+ }
+
+ if (test_CreateKey_sd(p, tctx, &handle, TEST_KEY2,
+ NULL, &newhandle)) {
+ created2 = true;
+ }
+
+ if (created2 && !test_CloseKey(p, tctx, &newhandle)) {
+ printf("CloseKey failed\n");
+ ret = false;
+ }
+
+ if (test_CreateKey_sd(p, tctx, &handle, TEST_KEY4, NULL, &newhandle)) {
+ created4 = true;
+ }
+
+ if (created4 && !test_CloseKey(p, tctx, &newhandle)) {
+ printf("CloseKey failed\n");
+ ret = false;
+ }
+
+ if (created4 && !test_SecurityDescriptors(p, tctx, &handle, TEST_KEY4)) {
+ ret = false;
+ }
+
+ if (created4 && !test_DeleteKey(p, tctx, &handle, TEST_KEY4)) {
+ printf("DeleteKey failed\n");
+ ret = false;
+ }
+
+ if (created2 && !test_DeleteKey(p, tctx, &handle, TEST_KEY2)) {
+ printf("DeleteKey failed\n");
+ ret = false;
+ }
+
+ /* The HKCR hive has a very large fanout */
+ if (open_fn == (void *)dcerpc_winreg_OpenHKCR) {
+ if(!test_key(p, tctx, &handle, MAX_DEPTH - 1, true)) {
+ ret = false;
+ }
+ } else {
+ if (!test_key(p, tctx, &handle, 0, true)) {
+ ret = false;
+ }
+ }
+
+ test_Cleanup(p, tctx, &handle, TEST_KEY_BASE);
+
+ return ret;
+}
+
+static bool test_Open(struct torture_context *tctx, struct dcerpc_pipe *p,
+ void *userdata)
+{
+ struct policy_handle handle, newhandle;
+ bool ret = true, created = false, deleted = false;
+ bool created3 = false, created_subkey = false;
+ struct winreg_OpenHKLM r;
+
+ winreg_open_fn open_fn = userdata;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx, open_fn(p, tctx, &r),
+ "open");
+
+ test_Cleanup(p, tctx, &handle, TEST_KEY_BASE);
+
+ if (!test_CreateKey(p, tctx, &handle, TEST_KEY_BASE, NULL)) {
+ torture_comment(tctx,
+ "CreateKey (TEST_KEY_BASE) failed\n");
+ }
+
+ if (!test_CreateKey(p, tctx, &handle, TEST_KEY1, NULL)) {
+ torture_comment(tctx,
+ "CreateKey failed - not considering a failure\n");
+ } else {
+ created = true;
+ }
+
+ if (created && !test_FlushKey(p, tctx, &handle)) {
+ torture_comment(tctx, "FlushKey failed\n");
+ ret = false;
+ }
+
+ if (created && !test_OpenKey(p, tctx, &handle, TEST_KEY1, &newhandle))
+ torture_fail(tctx,
+ "CreateKey failed (OpenKey after Create didn't work)\n");
+
+ if (created && !test_CloseKey(p, tctx, &newhandle))
+ torture_fail(tctx,
+ "CreateKey failed (CloseKey after Open didn't work)\n");
+
+ if (created && !test_DeleteKey(p, tctx, &handle, TEST_KEY1)) {
+ torture_comment(tctx, "DeleteKey failed\n");
+ ret = false;
+ } else {
+ deleted = true;
+ }
+
+ if (created && !test_FlushKey(p, tctx, &handle)) {
+ torture_comment(tctx, "FlushKey failed\n");
+ ret = false;
+ }
+
+ if (created && deleted &&
+ !_test_OpenKey(p, tctx, &handle, TEST_KEY1,
+ SEC_FLAG_MAXIMUM_ALLOWED, &newhandle,
+ WERR_BADFILE, NULL)) {
+ torture_comment(tctx,
+ "DeleteKey failed (OpenKey after Delete "
+ "did not return WERR_BADFILE)\n");
+ ret = false;
+ }
+
+ if (!test_GetVersion(p, tctx, &handle)) {
+ torture_comment(tctx, "GetVersion failed\n");
+ ret = false;
+ }
+
+ if (created && test_CreateKey(p, tctx, &handle, TEST_KEY3, NULL)) {
+ created3 = true;
+ }
+
+ if (created3 &&
+ test_CreateKey(p, tctx, &handle, TEST_SUBKEY, NULL)) {
+ created_subkey = true;
+ }
+
+ if (created_subkey &&
+ !test_DeleteKey(p, tctx, &handle, TEST_KEY3)) {
+ printf("DeleteKey failed\n");
+ ret = false;
+ }
+
+ /* The HKCR hive has a very large fanout */
+ if (open_fn == (void *)dcerpc_winreg_OpenHKCR) {
+ if(!test_key(p, tctx, &handle, MAX_DEPTH - 1, false)) {
+ ret = false;
+ }
+ } else {
+ if (!test_key(p, tctx, &handle, 0, false)) {
+ ret = false;
+ }
+ }
+
+ test_Cleanup(p, tctx, &handle, TEST_KEY_BASE);
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_winreg(TALLOC_CTX *mem_ctx)
+{
+ struct torture_rpc_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "WINREG");
+ struct torture_test *test;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "winreg",
+ &ndr_table_winreg);
+
+ test = torture_rpc_tcase_add_test(tcase, "InitiateSystemShutdown",
+ test_InitiateSystemShutdown);
+ test->dangerous = true;
+
+ test = torture_rpc_tcase_add_test(tcase, "InitiateSystemShutdownEx",
+ test_InitiateSystemShutdownEx);
+ test->dangerous = true;
+
+ /* Basic tests without security descriptors */
+ torture_rpc_tcase_add_test_ex(tcase, "HKLM-basic",
+ test_Open,
+ (winreg_open_fn)dcerpc_winreg_OpenHKLM);
+ torture_rpc_tcase_add_test_ex(tcase, "HKU-basic",
+ test_Open,
+ (winreg_open_fn)dcerpc_winreg_OpenHKU);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCR-basic",
+ test_Open,
+ (winreg_open_fn)dcerpc_winreg_OpenHKCR);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCU-basic",
+ test_Open,
+ (winreg_open_fn)dcerpc_winreg_OpenHKCU);
+
+ /* Security descriptor tests */
+ torture_rpc_tcase_add_test_ex(tcase, "HKLM-security",
+ test_Open_Security,
+ (winreg_open_fn)dcerpc_winreg_OpenHKLM);
+ torture_rpc_tcase_add_test_ex(tcase, "HKU-security",
+ test_Open_Security,
+ (winreg_open_fn)dcerpc_winreg_OpenHKU);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCR-security",
+ test_Open_Security,
+ (winreg_open_fn)dcerpc_winreg_OpenHKCR);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCU-security",
+ test_Open_Security,
+ (winreg_open_fn)dcerpc_winreg_OpenHKCU);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/wkssvc.c b/source4/torture/rpc/wkssvc.c
new file mode 100644
index 0000000000..b212a44b33
--- /dev/null
+++ b/source4/torture/rpc/wkssvc.c
@@ -0,0 +1,1452 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for wkssvc rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Günther Deschner 2007
+
+ 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 "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_wkssvc_c.h"
+#include "torture/rpc/rpc.h"
+#include "lib/cmdline/popt_common.h"
+#include "param/param.h"
+#include "lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+
+#define SMBTORTURE_MACHINE_NAME "smbtrt_name"
+#define SMBTORTURE_ALTERNATE_NAME "smbtrt_altname"
+#define SMBTORTURE_TRANSPORT_NAME "\\Device\\smbtrt_transport_name"
+#define SMBTORTURE_USE_NAME "S:"
+#define SMBTORTURE_MESSAGE "You are currently tortured by Samba"
+
+static bool test_NetWkstaGetInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetWkstaGetInfo r;
+ union wkssvc_NetWkstaInfo info;
+ uint16_t levels[] = {100, 101, 102, 502};
+ int i;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.out.info = &info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+ torture_comment(tctx, "testing NetWkstaGetInfo level %u\n",
+ r.in.level);
+ status = dcerpc_wkssvc_NetWkstaGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx, "NetWkstaGetInfo level %u failed",
+ r.in.level));
+ torture_assert_werr_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "NetWkstaGetInfo level %u failed",
+ r.in.level));
+ }
+
+ return true;
+}
+
+static bool test_NetWkstaTransportEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetWkstaTransportEnum r;
+ uint32_t resume_handle = 0;
+ struct wkssvc_NetWkstaTransportInfo info;
+ union wkssvc_NetWkstaTransportCtr ctr;
+ struct wkssvc_NetWkstaTransportCtr0 ctr0;
+ uint32_t total_entries = 0;
+
+ ZERO_STRUCT(ctr0);
+ ctr.ctr0 = &ctr0;
+
+ info.level = 0;
+ info.ctr = ctr;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.info = &info;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = &resume_handle;
+ r.out.total_entries = &total_entries;
+ r.out.info = &info;
+ r.out.resume_handle = &resume_handle;
+
+ torture_comment(tctx, "testing NetWkstaTransportEnum level 0\n");
+
+ status = dcerpc_wkssvc_NetWkstaTransportEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetWkstaTransportEnum failed");
+ torture_assert_werr_ok(tctx, r.out.result, talloc_asprintf(tctx,
+ "NetWkstaTransportEnum level %u failed",
+ info.level));
+
+ return true;
+}
+
+static bool test_NetrWkstaTransportAdd(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrWkstaTransportAdd r;
+ struct wkssvc_NetWkstaTransportInfo0 info0;
+ uint32_t parm_err = 0;
+
+ ZERO_STRUCT(info0);
+
+ info0.quality_of_service = 0xffff;
+ info0.vc_count = 0;
+ info0.name = SMBTORTURE_TRANSPORT_NAME;
+ info0.address = "000000000000";
+ info0.wan_link = 0x400;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.level = 0;
+ r.in.info0 = &info0;
+ r.in.parm_err = r.out.parm_err = &parm_err;
+
+ torture_comment(tctx, "testing NetrWkstaTransportAdd level 0\n");
+
+ status = dcerpc_wkssvc_NetrWkstaTransportAdd(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrWkstaTransportAdd failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_INVALID_PARAM,
+ "NetrWkstaTransportAdd level 0 failed");
+
+ return true;
+}
+
+static bool test_NetrWkstaTransportDel(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrWkstaTransportDel r;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.transport_name = SMBTORTURE_TRANSPORT_NAME;
+ r.in.unknown3 = 0;
+
+ torture_comment(tctx, "testing NetrWkstaTransportDel\n");
+
+ status = dcerpc_wkssvc_NetrWkstaTransportDel(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrWkstaTransportDel failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrWkstaTransportDel");
+
+ return true;
+}
+
+static bool test_NetWkstaEnumUsers(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetWkstaEnumUsers r;
+ uint32_t handle = 0;
+ uint32_t entries_read = 0;
+ struct wkssvc_NetWkstaEnumUsersInfo info;
+ struct wkssvc_NetWkstaEnumUsersCtr0 *user0;
+ struct wkssvc_NetWkstaEnumUsersCtr1 *user1;
+ uint32_t levels[] = { 0, 1 };
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ ZERO_STRUCT(info);
+
+ info.level = levels[i];
+ switch (info.level) {
+ case 0:
+ user0 = talloc_zero(tctx,
+ struct wkssvc_NetWkstaEnumUsersCtr0);
+ info.ctr.user0 = user0;
+ break;
+ case 1:
+ user1 = talloc_zero(tctx,
+ struct wkssvc_NetWkstaEnumUsersCtr1);
+ info.ctr.user1 = user1;
+ break;
+ default:
+ break;
+ }
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.prefmaxlen = (uint32_t)-1;
+ r.in.info = r.out.info = &info;
+ r.in.resume_handle = r.out.resume_handle = &handle;
+
+ r.out.entries_read = &entries_read;
+
+ torture_comment(tctx, "testing NetWkstaEnumUsers level %u\n",
+ levels[i]);
+
+ status = dcerpc_wkssvc_NetWkstaEnumUsers(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetWkstaEnumUsers failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetWkstaEnumUsers failed");
+ }
+
+ return true;
+}
+
+static bool test_NetrWkstaUserGetInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrWkstaUserGetInfo r;
+ union wkssvc_NetrWkstaUserInfo info;
+ const char *dom = lp_workgroup(tctx->lp_ctx);
+ struct cli_credentials *creds = cmdline_credentials;
+ const char *user = cli_credentials_get_username(creds);
+ int i;
+
+ const struct {
+ const char *unknown;
+ uint32_t level;
+ WERROR result;
+ } tests[] = {
+ { NULL, 0, WERR_NO_SUCH_LOGON_SESSION },
+ { NULL, 1, WERR_NO_SUCH_LOGON_SESSION },
+ { NULL, 1101, WERR_OK },
+ { dom, 0, WERR_INVALID_PARAM },
+ { dom, 1, WERR_INVALID_PARAM },
+ { dom, 1101, WERR_INVALID_PARAM },
+ { user, 0, WERR_INVALID_PARAM },
+ { user, 1, WERR_INVALID_PARAM },
+ { user, 1101, WERR_INVALID_PARAM },
+ };
+
+ for (i=0; i<ARRAY_SIZE(tests); i++) {
+ r.in.unknown = tests[i].unknown;
+ r.in.level = tests[i].level;
+ r.out.info = &info;
+
+ torture_comment(tctx, "testing NetrWkstaUserGetInfo level %u\n",
+ r.in.level);
+
+ status = dcerpc_wkssvc_NetrWkstaUserGetInfo(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrWkstaUserGetInfo failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ tests[i].result,
+ "NetrWkstaUserGetInfo failed");
+ }
+
+ return true;
+}
+
+static bool test_NetrUseEnum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrUseEnum r;
+ uint32_t handle = 0;
+ uint32_t entries_read = 0;
+ struct wkssvc_NetrUseEnumInfo info;
+ struct wkssvc_NetrUseEnumCtr0 *use0;
+ struct wkssvc_NetrUseEnumCtr1 *use1;
+ struct wkssvc_NetrUseEnumCtr2 *use2;
+ uint32_t levels[] = { 0, 1, 2 };
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ ZERO_STRUCT(info);
+
+ info.level = levels[i];
+ switch (info.level) {
+ case 0:
+ use0 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr0);
+ info.ctr.ctr0 = use0;
+ break;
+ case 1:
+ use1 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr1);
+ info.ctr.ctr1 = use1;
+ break;
+ case 2:
+ use2 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr2);
+ info.ctr.ctr2 = use2;
+ break;
+ default:
+ break;
+ }
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.prefmaxlen = (uint32_t)-1;
+ r.in.info = r.out.info = &info;
+ r.in.resume_handle = r.out.resume_handle = &handle;
+
+ r.out.entries_read = &entries_read;
+
+ torture_comment(tctx, "testing NetrUseEnum level %u\n",
+ levels[i]);
+
+ status = dcerpc_wkssvc_NetrUseEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUseEnum failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrUseEnum failed");
+ }
+
+ return true;
+}
+
+static bool test_NetrUseAdd(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrUseAdd r;
+ struct wkssvc_NetrUseInfo0 info0;
+ struct wkssvc_NetrUseInfo1 info1;
+ union wkssvc_NetrUseGetInfoCtr *ctr;
+ uint32_t parm_err = 0;
+
+ ctr = talloc(tctx, union wkssvc_NetrUseGetInfoCtr);
+
+ ZERO_STRUCT(info0);
+
+ info0.local = SMBTORTURE_USE_NAME;
+ info0.remote = "\\\\localhost\\c$";
+
+ ctr->info0 = &info0;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.level = 0;
+ r.in.ctr = ctr;
+ r.in.parm_err = r.out.parm_err = &parm_err;
+
+ torture_comment(tctx, "testing NetrUseAdd level %u\n",
+ r.in.level);
+
+ status = dcerpc_wkssvc_NetrUseAdd(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUseAdd failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_LEVEL,
+ "NetrUseAdd failed");
+
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(info1);
+
+ info1.local = SMBTORTURE_USE_NAME;
+ info1.remote = "\\\\localhost\\sysvol";
+ info1.password = NULL;
+
+ ctr->info1 = &info1;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.level = 1;
+ r.in.ctr = ctr;
+ r.in.parm_err = r.out.parm_err = &parm_err;
+
+ torture_comment(tctx, "testing NetrUseAdd level %u\n",
+ r.in.level);
+
+ status = dcerpc_wkssvc_NetrUseAdd(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUseAdd failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrUseAdd failed");
+
+ return true;
+}
+
+static bool test_NetrUseDel(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrUseDel r;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.use_name = SMBTORTURE_USE_NAME;
+ r.in.force_cond = 0;
+
+ torture_comment(tctx, "testing NetrUseDel\n");
+
+ status = dcerpc_wkssvc_NetrUseDel(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUseDel failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrUseDel failed");
+ return true;
+}
+
+static bool test_NetrUseGetInfo_level(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *use_name,
+ uint32_t level,
+ WERROR werr)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrUseGetInfo r;
+ union wkssvc_NetrUseGetInfoCtr ctr;
+
+ ZERO_STRUCT(ctr);
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.use_name = use_name;
+ r.in.level = level;
+ r.out.ctr = &ctr;
+ status = dcerpc_wkssvc_NetrUseGetInfo(p, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUseGetInfo failed");
+ torture_assert_werr_equal(tctx, r.out.result, werr,
+ "NetrUseGetInfo failed");
+ return true;
+}
+
+static bool test_NetrUseGetInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrUseEnum r;
+ uint32_t handle = 0;
+ uint32_t entries_read = 0;
+ struct wkssvc_NetrUseEnumInfo info;
+ struct wkssvc_NetrUseEnumCtr0 *use0;
+ uint32_t levels[] = { 0, 1, 2 };
+ const char *use_name = NULL;
+ int i, k;
+
+ ZERO_STRUCT(info);
+
+ info.level = 0;
+ use0 = talloc_zero(tctx, struct wkssvc_NetrUseEnumCtr0);
+ info.ctr.ctr0 = use0;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.prefmaxlen = (uint32_t)-1;
+ r.in.info = r.out.info = &info;
+ r.in.resume_handle = r.out.resume_handle = &handle;
+ r.out.entries_read = &entries_read;
+
+ status = dcerpc_wkssvc_NetrUseEnum(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUseEnum failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrUseEnum failed");
+
+ for (k=0; k < r.out.info->ctr.ctr0->count; k++) {
+
+ use_name = r.out.info->ctr.ctr0->array[k].local;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ if (!test_NetrUseGetInfo_level(tctx, p, use_name,
+ levels[i],
+ WERR_OK))
+ {
+ if (levels[i] != 0) {
+ return false;
+ }
+ }
+ }
+
+ use_name = r.out.info->ctr.ctr0->array[k].remote;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ if (!test_NetrUseGetInfo_level(tctx, p, use_name,
+ levels[i],
+ WERR_NOT_CONNECTED))
+ {
+ if (levels[i] != 0) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_NetrLogonDomainNameAdd(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrLogonDomainNameAdd r;
+
+ r.in.domain_name = lp_workgroup(tctx->lp_ctx);
+
+ torture_comment(tctx, "testing NetrLogonDomainNameAdd\n");
+
+ status = dcerpc_wkssvc_NetrLogonDomainNameAdd(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrLogonDomainNameAdd failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "NetrLogonDomainNameAdd failed");
+ return true;
+}
+
+static bool test_NetrLogonDomainNameDel(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrLogonDomainNameDel r;
+
+ r.in.domain_name = lp_workgroup(tctx->lp_ctx);
+
+ torture_comment(tctx, "testing NetrLogonDomainNameDel\n");
+
+ status = dcerpc_wkssvc_NetrLogonDomainNameDel(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrLogonDomainNameDel failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "NetrLogonDomainNameDel failed");
+ return true;
+}
+
+static bool test_NetrEnumerateComputerNames_level(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ uint16_t level,
+ const char ***names,
+ int *num_names)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrEnumerateComputerNames r;
+ struct wkssvc_ComputerNamesCtr *ctr;
+ int i;
+
+ ctr = talloc_zero(tctx, struct wkssvc_ComputerNamesCtr);
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.name_type = level;
+ r.in.Reserved = 0;
+ r.out.ctr = &ctr;
+
+ torture_comment(tctx, "testing NetrEnumerateComputerNames level %u\n",
+ r.in.name_type);
+
+ status = dcerpc_wkssvc_NetrEnumerateComputerNames(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrEnumerateComputerNames failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrEnumerateComputerNames failed");
+
+ if ((level == NetPrimaryComputerName) && ctr->count != 1) {
+ torture_comment(tctx,
+ "NetrEnumerateComputerNames did not return one "
+ "name but %u\n", ctr->count);
+ return false;
+ }
+
+ if (names && num_names) {
+ *num_names = 0;
+ *names = NULL;
+ for (i=0; i<ctr->count; i++) {
+ if (!add_string_to_array(tctx,
+ ctr->computer_name[i].string,
+ names,
+ num_names))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_NetrEnumerateComputerNames(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ uint16_t levels[] = {0,1,2};
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ if (!test_NetrEnumerateComputerNames_level(tctx,
+ p,
+ levels[i],
+ NULL, NULL))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool test_NetrValidateName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrValidateName r;
+ uint16_t levels[] = {0,1,2,3,4,5};
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.name = lp_workgroup(tctx->lp_ctx);
+ r.in.Account = NULL;
+ r.in.Password = NULL;
+ r.in.name_type = levels[i];
+
+ torture_comment(tctx, "testing NetrValidateName level %u\n",
+ r.in.name_type);
+
+ status = dcerpc_wkssvc_NetrValidateName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrValidateName failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_NOT_SUPPORTED,
+ "NetrValidateName failed");
+ }
+
+ return true;
+}
+
+static bool test_NetrValidateName2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrValidateName2 r;
+ uint16_t levels[] = {0,1,2,3,4,5};
+ int i;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.name = lp_workgroup(tctx->lp_ctx);
+ r.in.Account = NULL;
+ r.in.EncryptedPassword = NULL;
+ r.in.name_type = levels[i];
+
+ torture_comment(tctx, "testing NetrValidateName2 level %u\n",
+ r.in.name_type);
+
+ status = dcerpc_wkssvc_NetrValidateName2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrValidateName2 failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_RPC_E_REMOTE_DISABLED,
+ "NetrValidateName2 failed");
+ }
+
+ return true;
+}
+
+static bool test_NetrAddAlternateComputerName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrAddAlternateComputerName r;
+ const char **names = NULL;
+ int num_names = 0;
+ int i;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.NewAlternateMachineName = SMBTORTURE_ALTERNATE_NAME;
+ r.in.Account = NULL;
+ r.in.EncryptedPassword = NULL;
+ r.in.Reserved = 0;
+
+ torture_comment(tctx, "testing NetrAddAlternateComputerName\n");
+
+ status = dcerpc_wkssvc_NetrAddAlternateComputerName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrAddAlternateComputerName failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrAddAlternateComputerName failed");
+
+ if (!test_NetrEnumerateComputerNames_level(tctx, p,
+ NetAlternateComputerNames,
+ &names, &num_names))
+ {
+ return false;
+ }
+
+ for (i=0; i<num_names; i++) {
+ if (strequal(names[i], SMBTORTURE_ALTERNATE_NAME)) {
+ return true;
+ }
+ }
+
+ torture_comment(tctx, "new alternate name not set\n");
+
+ return false;
+}
+
+static bool test_NetrRemoveAlternateComputerName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrRemoveAlternateComputerName r;
+ const char **names = NULL;
+ int num_names = 0;
+ int i;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.AlternateMachineNameToRemove = SMBTORTURE_ALTERNATE_NAME;
+ r.in.Account = NULL;
+ r.in.EncryptedPassword = NULL;
+ r.in.Reserved = 0;
+
+ torture_comment(tctx, "testing NetrRemoveAlternateComputerName\n");
+
+ status = dcerpc_wkssvc_NetrRemoveAlternateComputerName(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrRemoveAlternateComputerName failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrRemoveAlternateComputerName failed");
+
+ if (!test_NetrEnumerateComputerNames_level(tctx, p,
+ NetAlternateComputerNames,
+ &names, &num_names))
+ {
+ return false;
+ }
+
+ for (i=0; i<num_names; i++) {
+ if (strequal(names[i], SMBTORTURE_ALTERNATE_NAME)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool test_NetrSetPrimaryComputername_name(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrSetPrimaryComputername r;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.primary_name = name;
+ r.in.Account = NULL;
+ r.in.EncryptedPassword = NULL;
+ r.in.Reserved = 0;
+
+ status = dcerpc_wkssvc_NetrSetPrimaryComputername(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrSetPrimaryComputername failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrSetPrimaryComputername failed");
+ return true;
+}
+
+
+static bool test_NetrSetPrimaryComputername(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ /*
+ add alternate,
+ check if there
+ store old primary
+ set new primary (alternate)
+ check if there
+ later: check if del is possible
+ set primary back to origin
+ check if there
+ del alternate
+ */
+
+ const char **names_o = NULL, **names = NULL;
+ int num_names_o = 0, num_names = 0;
+
+ torture_comment(tctx, "testing NetrSetPrimaryComputername\n");
+
+ if (!test_NetrAddAlternateComputerName(tctx, p)) {
+ return false;
+ }
+
+ if (!test_NetrEnumerateComputerNames_level(tctx, p,
+ NetPrimaryComputerName,
+ &names_o, &num_names_o))
+ {
+ return false;
+ }
+
+ if (num_names_o != 1) {
+ return false;
+ }
+
+ if (!test_NetrSetPrimaryComputername_name(tctx, p,
+ SMBTORTURE_ALTERNATE_NAME))
+ {
+ return false;
+ }
+
+ if (!test_NetrEnumerateComputerNames_level(tctx, p,
+ NetPrimaryComputerName,
+ &names, &num_names))
+ {
+ return false;
+ }
+
+ if (num_names != 1) {
+ return false;
+ }
+
+ if (!strequal(names[0], SMBTORTURE_ALTERNATE_NAME)) {
+ torture_comment(tctx,
+ "name mismatch (%s != %s) after NetrSetPrimaryComputername!\n",
+ names[0], SMBTORTURE_ALTERNATE_NAME);
+ /*return false */;
+ }
+
+ if (!test_NetrSetPrimaryComputername_name(tctx, p,
+ names_o[0]))
+ {
+ return false;
+ }
+
+ if (!test_NetrRemoveAlternateComputerName(tctx, p)) {
+ return false;
+ }
+
+
+ return true;
+}
+
+static bool test_NetrRenameMachineInDomain(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrRenameMachineInDomain r;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.NewMachineName = SMBTORTURE_MACHINE_NAME;
+ r.in.Account = NULL;
+ r.in.password = NULL;
+ r.in.RenameOptions = 0;
+
+ torture_comment(tctx, "testing NetrRenameMachineInDomain\n");
+
+ status = dcerpc_wkssvc_NetrRenameMachineInDomain(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrRenameMachineInDomain failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "NetrRenameMachineInDomain failed");
+ return true;
+}
+
+static bool test_NetrRenameMachineInDomain2_name(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *new_name)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrRenameMachineInDomain2 r;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.NewMachineName = new_name;
+ r.in.Account = NULL;
+ r.in.EncryptedPassword = NULL;
+ r.in.RenameOptions = 0;
+
+ status = dcerpc_wkssvc_NetrRenameMachineInDomain2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrRenameMachineInDomain2 failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrRenameMachineInDomain2 failed");
+ return true;
+}
+
+static bool test_NetrRenameMachineInDomain2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ const char **names_o = NULL, **names = NULL;
+ int num_names_o = 0, num_names = 0;
+
+ torture_comment(tctx, "testing NetrRenameMachineInDomain2\n");
+
+ if (!test_NetrEnumerateComputerNames_level(tctx, p,
+ NetPrimaryComputerName,
+ &names_o, &num_names_o))
+ {
+ return false;
+ }
+
+ if (num_names_o != 1) {
+ return false;
+ }
+
+ if (!test_NetrRenameMachineInDomain2_name(tctx, p,
+ SMBTORTURE_MACHINE_NAME))
+ {
+ return false;
+ }
+
+ if (!test_NetrEnumerateComputerNames_level(tctx, p,
+ NetPrimaryComputerName,
+ &names, &num_names))
+ {
+ return false;
+ }
+
+ if (num_names != 1) {
+ return false;
+ }
+
+ if (strequal(names[0], names_o[0])) {
+ test_NetrRenameMachineInDomain2_name(tctx, p, names_o[0]);
+ return false;
+ }
+
+ if (!strequal(names[0], SMBTORTURE_MACHINE_NAME)) {
+ test_NetrRenameMachineInDomain2_name(tctx, p, names_o[0]);
+ return false;
+ }
+
+ if (!test_NetrRenameMachineInDomain2_name(tctx, p, names_o[0]))
+ {
+ return false;
+ }
+
+ if (!test_NetrEnumerateComputerNames_level(tctx, p,
+ NetPrimaryComputerName,
+ &names, &num_names))
+ {
+ return false;
+ }
+
+ if (num_names != 1) {
+ return false;
+ }
+
+ if (!strequal(names[0], names_o[0])) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_NetrWorkstationStatisticsGet(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrWorkstationStatisticsGet r;
+ struct wkssvc_NetrWorkstationStatistics *info;
+
+ ZERO_STRUCT(r);
+
+ info = talloc_zero(tctx, struct wkssvc_NetrWorkstationStatistics);
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.out.info = &info;
+
+ torture_comment(tctx, "testing NetrWorkstationStatisticsGet\n");
+
+ status = dcerpc_wkssvc_NetrWorkstationStatisticsGet(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrWorkstationStatisticsGet failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrWorkstationStatisticsGet failed");
+ return true;
+}
+
+/* only succeeds as long as the local messenger service is running - Guenther */
+
+static bool test_NetrMessageBufferSend(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrMessageBufferSend r;
+ const char *message = SMBTORTURE_MESSAGE;
+ size_t size;
+ uint8_t *msg;
+
+ size = push_ucs2_talloc(tctx, lp_iconv_convenience(tctx->lp_ctx),
+ (void **)&msg, message);
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.message_name = dcerpc_server_name(p);
+ r.in.message_sender_name = dcerpc_server_name(p);
+ r.in.message_buffer = msg;
+ r.in.message_size = size;
+
+ torture_comment(tctx, "testing NetrMessageBufferSend\n");
+
+ status = dcerpc_wkssvc_NetrMessageBufferSend(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrMessageBufferSend failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrMessageBufferSend failed");
+ return true;
+}
+
+static bool test_NetrGetJoinInformation(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrGetJoinInformation r;
+ enum wkssvc_NetJoinStatus join_status;
+ const char *name_buffer = "";
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.name_buffer = r.out.name_buffer = &name_buffer;
+ r.out.name_type = &join_status;
+
+ torture_comment(tctx, "testing NetrGetJoinInformation\n");
+
+ status = dcerpc_wkssvc_NetrGetJoinInformation(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrGetJoinInformation failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrGetJoinInformation failed");
+ return true;
+}
+
+static bool test_GetJoinInformation(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ enum wkssvc_NetJoinStatus *join_status_p,
+ const char **name)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrGetJoinInformation r;
+ enum wkssvc_NetJoinStatus join_status;
+ const char *name_buffer = "";
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.name_buffer = r.out.name_buffer = &name_buffer;
+ r.out.name_type = &join_status;
+
+ status = dcerpc_wkssvc_NetrGetJoinInformation(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrGetJoinInformation failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetrGetJoinInformation failed");
+
+ if (join_status_p) {
+ *join_status_p = join_status;
+ }
+
+ if (*name) {
+ *name = talloc_strdup(tctx, name_buffer);
+ }
+
+ return true;
+
+}
+
+static bool test_NetrGetJoinableOus(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrGetJoinableOus r;
+ uint32_t num_ous = 0;
+ const char **ous = NULL;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = lp_workgroup(tctx->lp_ctx);
+ r.in.Account = NULL;
+ r.in.unknown = NULL;
+ r.in.num_ous = r.out.num_ous = &num_ous;
+ r.out.ous = &ous;
+
+ torture_comment(tctx, "testing NetrGetJoinableOus\n");
+
+ status = dcerpc_wkssvc_NetrGetJoinableOus(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetrGetJoinableOus failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_NOT_SUPPORTED,
+ "NetrGetJoinableOus failed");
+
+ return true;
+}
+
+static bool test_NetrGetJoinableOus2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrGetJoinableOus2 r;
+ uint32_t num_ous = 0;
+ const char **ous = NULL;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = lp_workgroup(tctx->lp_ctx);
+ r.in.Account = NULL;
+ r.in.EncryptedPassword = NULL;
+ r.in.num_ous = r.out.num_ous = &num_ous;
+ r.out.ous = &ous;
+
+ torture_comment(tctx, "testing NetrGetJoinableOus2\n");
+
+ status = dcerpc_wkssvc_NetrGetJoinableOus2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetrGetJoinableOus2 failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_RPC_E_REMOTE_DISABLED,
+ "NetrGetJoinableOus2 failed");
+
+ return true;
+}
+
+static bool test_NetrUnjoinDomain(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrUnjoinDomain r;
+ struct cli_credentials *creds = cmdline_credentials;
+ const char *user = cli_credentials_get_username(creds);
+ const char *admin_account = NULL;
+
+ admin_account = talloc_asprintf(tctx, "%s\\%s",
+ lp_workgroup(tctx->lp_ctx),
+ user);
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.Account = admin_account;
+ r.in.password = NULL;
+ r.in.unjoin_flags = 0;
+
+ torture_comment(tctx, "testing NetrUnjoinDomain\n");
+
+ status = dcerpc_wkssvc_NetrUnjoinDomain(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUnjoinDomain failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "NetrUnjoinDomain failed");
+ return true;
+}
+
+static bool test_NetrJoinDomain(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrJoinDomain r;
+ struct cli_credentials *creds = cmdline_credentials;
+ const char *user = cli_credentials_get_username(creds);
+ const char *admin_account = NULL;
+
+ admin_account = talloc_asprintf(tctx, "%s\\%s",
+ lp_workgroup(tctx->lp_ctx),
+ user);
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = lp_realm(tctx->lp_ctx);
+ r.in.account_ou = NULL;
+ r.in.Account = admin_account;
+ r.in.password = NULL;
+ r.in.join_flags = 0;
+
+ torture_comment(tctx, "testing NetrJoinDomain\n");
+
+ status = dcerpc_wkssvc_NetrJoinDomain(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrJoinDomain failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "NetrJoinDomain failed");
+ return true;
+}
+
+/* encode a wkssvc_PasswordBuffer for remote joining/unjoining:
+ *
+ * similar to samr_CryptPasswordEx. Different: 8byte confounder (instead of
+ * 16byte), confounder in front of the 516 byte buffer (instead of after that
+ * buffer), calling MD5Update() first with session_key and then with confounder
+ * (vice versa in samr) - Guenther */
+
+static void encode_wkssvc_join_password_buffer(TALLOC_CTX *mem_ctx,
+ const char *pwd,
+ DATA_BLOB *session_key,
+ struct wkssvc_PasswordBuffer *pwd_buf)
+{
+ uint8_t buffer[516];
+ struct MD5Context ctx;
+
+ DATA_BLOB confounded_session_key = data_blob_talloc(mem_ctx, NULL, 16);
+
+ int confounder_len = 8;
+ uint8_t confounder[8];
+
+ encode_pw_buffer(buffer, pwd, STR_UNICODE);
+
+ generate_random_buffer((uint8_t *)confounder, confounder_len);
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, session_key->data, session_key->length);
+ MD5Update(&ctx, confounder, confounder_len);
+ MD5Final(confounded_session_key.data, &ctx);
+
+ arcfour_crypt_blob(buffer, 516, &confounded_session_key);
+
+ memcpy(&pwd_buf->data[0], confounder, confounder_len);
+ memcpy(&pwd_buf->data[8], buffer, 516);
+
+ data_blob_free(&confounded_session_key);
+}
+
+/*
+ * prerequisites for remotely joining an unjoined XP SP2 workstation:
+ * - firewall needs to be disabled (or open for ncacn_np access)
+ * - HKLM\System\CurrentControlSet\Control\Lsa\forceguest needs to 0
+ * see also:
+ * http://support.microsoft.com/kb/294355/EN-US/ and
+ * http://support.microsoft.com/kb/290403/EN-US/
+ */
+
+static bool test_NetrJoinDomain2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrJoinDomain2 r;
+ const char *domain_admin_account = NULL;
+ const char *domain_admin_password = NULL;
+ const char *domain_name = NULL;
+ struct wkssvc_PasswordBuffer pwd_buf;
+ enum wkssvc_NetJoinStatus join_status;
+ const char *join_name = NULL;
+ WERROR expected_err;
+ DATA_BLOB session_key;
+
+ /* FIXME: this test assumes to join workstations / servers and does not
+ * handle DCs (WERR_SETUP_DOMAIN_CONTROLLER) */
+
+ if (!test_GetJoinInformation(tctx, p, &join_status, &join_name))
+ {
+ return false;
+ }
+
+ switch (join_status) {
+ case NetSetupDomainName:
+ expected_err = WERR_SETUP_ALREADY_JOINED;
+ break;
+ case NetSetupUnknownStatus:
+ case NetSetupUnjoined:
+ case NetSetupWorkgroupName:
+ default:
+ expected_err = WERR_OK;
+ break;
+ }
+
+ domain_admin_account = torture_setting_string(tctx, "domain_admin_account", NULL);
+
+ domain_admin_password = torture_setting_string(tctx, "domain_admin_password", NULL);
+
+ domain_name = torture_setting_string(tctx, "domain_name", NULL);
+
+ if ((domain_admin_account == NULL) ||
+ (domain_admin_password == NULL) ||
+ (domain_name == NULL)) {
+ torture_comment(tctx, "not enough input parameter\n");
+ return false;
+ }
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ encode_wkssvc_join_password_buffer(tctx, domain_admin_password,
+ &session_key, &pwd_buf);
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = domain_name;
+ r.in.account_ou = NULL;
+ r.in.admin_account = domain_admin_account;
+ r.in.encrypted_password = &pwd_buf;
+ r.in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+ WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE;
+
+ torture_comment(tctx, "testing NetrJoinDomain2 (assuming non-DC)\n");
+
+ status = dcerpc_wkssvc_NetrJoinDomain2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrJoinDomain2 failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_err,
+ "NetrJoinDomain2 failed");
+
+ if (!test_GetJoinInformation(tctx, p, &join_status, &join_name))
+ {
+ return false;
+ }
+
+ if (join_status != NetSetupDomainName) {
+ torture_comment(tctx,
+ "Join verify failed: got %d\n", join_status);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_NetrUnjoinDomain2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrUnjoinDomain2 r;
+ const char *domain_admin_account = NULL;
+ const char *domain_admin_password = NULL;
+ struct wkssvc_PasswordBuffer pwd_buf;
+ enum wkssvc_NetJoinStatus join_status;
+ const char *join_name = NULL;
+ WERROR expected_err;
+ DATA_BLOB session_key;
+
+ /* FIXME: this test assumes to join workstations / servers and does not
+ * handle DCs (WERR_SETUP_DOMAIN_CONTROLLER) */
+
+ if (!test_GetJoinInformation(tctx, p, &join_status, &join_name))
+ {
+ return false;
+ }
+
+ switch (join_status) {
+ case NetSetupUnjoined:
+ expected_err = WERR_SETUP_NOT_JOINED;
+ break;
+ case NetSetupDomainName:
+ case NetSetupUnknownStatus:
+ case NetSetupWorkgroupName:
+ default:
+ expected_err = WERR_OK;
+ break;
+ }
+
+ domain_admin_account = torture_setting_string(tctx, "domain_admin_account", NULL);
+
+ domain_admin_password = torture_setting_string(tctx, "domain_admin_password", NULL);
+
+ if ((domain_admin_account == NULL) ||
+ (domain_admin_password == NULL)) {
+ torture_comment(tctx, "not enough input parameter\n");
+ return false;
+ }
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ encode_wkssvc_join_password_buffer(tctx, domain_admin_password,
+ &session_key, &pwd_buf);
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.account = domain_admin_account;
+ r.in.encrypted_password = &pwd_buf;
+ r.in.unjoin_flags = 0;
+
+ torture_comment(tctx, "testing NetrUnjoinDomain2 (assuming non-DC)\n");
+
+ status = dcerpc_wkssvc_NetrUnjoinDomain2(p, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUnjoinDomain2 failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_err,
+ "NetrUnjoinDomain2 failed");
+
+ if (!test_GetJoinInformation(tctx, p, &join_status, &join_name))
+ {
+ return false;
+ }
+
+ switch (join_status) {
+ case NetSetupUnjoined:
+ case NetSetupWorkgroupName:
+ break;
+ case NetSetupUnknown:
+ case NetSetupDomainName:
+ default:
+ torture_comment(tctx,
+ "Unjoin verify failed: got %d\n", join_status);
+ return false;
+ }
+
+ return true;
+}
+
+
+struct torture_suite *torture_rpc_wkssvc(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+ struct torture_rpc_tcase *tcase;
+ struct torture_test *test;
+
+ suite = torture_suite_create(mem_ctx, "WKSSVC");
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "wkssvc",
+ &ndr_table_wkssvc);
+
+ torture_rpc_tcase_add_test(tcase, "NetWkstaGetInfo",
+ test_NetWkstaGetInfo);
+
+ torture_rpc_tcase_add_test(tcase, "NetWkstaTransportEnum",
+ test_NetWkstaTransportEnum);
+ torture_rpc_tcase_add_test(tcase, "NetrWkstaTransportDel",
+ test_NetrWkstaTransportDel);
+ torture_rpc_tcase_add_test(tcase, "NetrWkstaTransportAdd",
+ test_NetrWkstaTransportAdd);
+
+ torture_rpc_tcase_add_test(tcase, "NetWkstaEnumUsers",
+ test_NetWkstaEnumUsers);
+ torture_rpc_tcase_add_test(tcase, "NetrWkstaUserGetInfo",
+ test_NetrWkstaUserGetInfo);
+
+ torture_rpc_tcase_add_test(tcase, "NetrUseDel",
+ test_NetrUseDel);
+ torture_rpc_tcase_add_test(tcase, "NetrUseGetInfo",
+ test_NetrUseGetInfo);
+ torture_rpc_tcase_add_test(tcase, "NetrUseEnum",
+ test_NetrUseEnum);
+ torture_rpc_tcase_add_test(tcase, "NetrUseAdd",
+ test_NetrUseAdd);
+
+ torture_rpc_tcase_add_test(tcase, "NetrValidateName",
+ test_NetrValidateName);
+ torture_rpc_tcase_add_test(tcase, "NetrValidateName2",
+ test_NetrValidateName2);
+ torture_rpc_tcase_add_test(tcase, "NetrLogonDomainNameDel",
+ test_NetrLogonDomainNameDel);
+ torture_rpc_tcase_add_test(tcase, "NetrLogonDomainNameAdd",
+ test_NetrLogonDomainNameAdd);
+ torture_rpc_tcase_add_test(tcase, "NetrRemoveAlternateComputerName",
+ test_NetrRemoveAlternateComputerName);
+ torture_rpc_tcase_add_test(tcase, "NetrAddAlternateComputerName",
+ test_NetrAddAlternateComputerName);
+ test = torture_rpc_tcase_add_test(tcase, "NetrSetPrimaryComputername",
+ test_NetrSetPrimaryComputername);
+ test->dangerous = true;
+ test = torture_rpc_tcase_add_test(tcase, "NetrRenameMachineInDomain",
+ test_NetrRenameMachineInDomain);
+ test->dangerous = true;
+ test = torture_rpc_tcase_add_test(tcase, "NetrRenameMachineInDomain2",
+ test_NetrRenameMachineInDomain2);
+ test->dangerous = true;
+ torture_rpc_tcase_add_test(tcase, "NetrEnumerateComputerNames",
+ test_NetrEnumerateComputerNames);
+
+ test = torture_rpc_tcase_add_test(tcase, "NetrJoinDomain2",
+ test_NetrJoinDomain2);
+ test->dangerous = true;
+ test = torture_rpc_tcase_add_test(tcase, "NetrUnjoinDomain2",
+ test_NetrUnjoinDomain2);
+ test->dangerous = true;
+
+ torture_rpc_tcase_add_test(tcase, "NetrJoinDomain",
+ test_NetrJoinDomain);
+ test->dangerous = true;
+ torture_rpc_tcase_add_test(tcase, "NetrUnjoinDomain",
+ test_NetrUnjoinDomain);
+ test->dangerous = true;
+ torture_rpc_tcase_add_test(tcase, "NetrGetJoinInformation",
+ test_NetrGetJoinInformation);
+ torture_rpc_tcase_add_test(tcase, "NetrGetJoinableOus",
+ test_NetrGetJoinableOus);
+ torture_rpc_tcase_add_test(tcase, "NetrGetJoinableOus2",
+ test_NetrGetJoinableOus2);
+
+ torture_rpc_tcase_add_test(tcase, "NetrWorkstationStatisticsGet",
+ test_NetrWorkstationStatisticsGet);
+ torture_rpc_tcase_add_test(tcase, "NetrMessageBufferSend",
+ test_NetrMessageBufferSend);
+
+ return suite;
+}