summaryrefslogtreecommitdiffstats
path: root/source4/torture/rpc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--source4/torture/rpc/alter_context.c112
-rw-r--r--source4/torture/rpc/async_bind.c86
-rw-r--r--source4/torture/rpc/atsvc.c138
-rw-r--r--source4/torture/rpc/backupkey.c2390
-rw-r--r--source4/torture/rpc/bench.c152
-rw-r--r--source4/torture/rpc/bind.c245
-rw-r--r--source4/torture/rpc/browser.c124
-rw-r--r--source4/torture/rpc/clusapi.c4185
-rw-r--r--source4/torture/rpc/countcalls.c131
-rw-r--r--source4/torture/rpc/dfs.c651
-rw-r--r--source4/torture/rpc/drsuapi.c1049
-rw-r--r--source4/torture/rpc/drsuapi.h94
-rw-r--r--source4/torture/rpc/drsuapi_cracknames.c1087
-rw-r--r--source4/torture/rpc/drsuapi_w2k8.c334
-rw-r--r--source4/torture/rpc/dsgetinfo.c452
-rw-r--r--source4/torture/rpc/dssetup.c64
-rw-r--r--source4/torture/rpc/echo.c474
-rw-r--r--source4/torture/rpc/epmapper.c679
-rw-r--r--source4/torture/rpc/eventlog.c501
-rw-r--r--source4/torture/rpc/forest_trust.c914
-rw-r--r--source4/torture/rpc/frsapi.c276
-rw-r--r--source4/torture/rpc/fsrvp.c968
-rw-r--r--source4/torture/rpc/handles.c622
-rw-r--r--source4/torture/rpc/initshutdown.c116
-rw-r--r--source4/torture/rpc/iremotewinspool.c855
-rw-r--r--source4/torture/rpc/iremotewinspool_common.c247
-rw-r--r--source4/torture/rpc/iremotewinspool_common.h101
-rw-r--r--source4/torture/rpc/iremotewinspool_driver.c840
-rw-r--r--source4/torture/rpc/join.c86
-rw-r--r--source4/torture/rpc/lsa.c5466
-rw-r--r--source4/torture/rpc/lsa_lookup.c428
-rw-r--r--source4/torture/rpc/mdssvc.c1040
-rw-r--r--source4/torture/rpc/mgmt.c322
-rw-r--r--source4/torture/rpc/netlogon.c6038
-rw-r--r--source4/torture/rpc/netlogon.h37
-rw-r--r--source4/torture/rpc/netlogon_crypto.c274
-rw-r--r--source4/torture/rpc/ntsvcs.c189
-rw-r--r--source4/torture/rpc/object_uuid.c85
-rw-r--r--source4/torture/rpc/oxidresolve.c263
-rw-r--r--source4/torture/rpc/remact.c104
-rw-r--r--source4/torture/rpc/remote_pac.c1372
-rw-r--r--source4/torture/rpc/rpc.c663
-rw-r--r--source4/torture/rpc/samba3rpc.c4729
-rw-r--r--source4/torture/rpc/samlogon.c2121
-rw-r--r--source4/torture/rpc/samr.c9468
-rw-r--r--source4/torture/rpc/samr_accessmask.c1197
-rw-r--r--source4/torture/rpc/samr_handletype.c217
-rw-r--r--source4/torture/rpc/samr_priv.c580
-rw-r--r--source4/torture/rpc/samsync.c1798
-rw-r--r--source4/torture/rpc/scanner.c187
-rw-r--r--source4/torture/rpc/schannel.c1338
-rw-r--r--source4/torture/rpc/session_key.c250
-rw-r--r--source4/torture/rpc/spoolss.c11704
-rw-r--r--source4/torture/rpc/spoolss_access.c905
-rw-r--r--source4/torture/rpc/spoolss_notify.c636
-rw-r--r--source4/torture/rpc/spoolss_win.c612
-rw-r--r--source4/torture/rpc/srvsvc.c1206
-rw-r--r--source4/torture/rpc/svcctl.c736
-rw-r--r--source4/torture/rpc/testjoin.c915
-rw-r--r--source4/torture/rpc/torture_rpc.h126
-rw-r--r--source4/torture/rpc/unixinfo.c149
-rw-r--r--source4/torture/rpc/winreg.c3245
-rw-r--r--source4/torture/rpc/witness.c911
-rw-r--r--source4/torture/rpc/wkssvc.c1458
64 files changed, 78742 insertions, 0 deletions
diff --git a/source4/torture/rpc/alter_context.c b/source4/torture/rpc/alter_context.c
new file mode 100644
index 0000000..22abd97
--- /dev/null
+++ b/source4/torture/rpc/alter_context.c
@@ -0,0 +1,112 @@
+/*
+ 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 "librpc/gen_ndr/ndr_lsa.h"
+#include "librpc/gen_ndr/ndr_dssetup.h"
+#include "torture/rpc/torture_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;
+ 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");
+
+ torture_comment(torture, "Testing change of primary context\n");
+ status = dcerpc_alter_context(p, torture, &p->syntax, &p->transfer_syntax);
+ torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed");
+
+ if (!test_lsa_OpenPolicy2(p->binding_handle, torture, &handle)) {
+ ret = false;
+ }
+
+ torture_comment(torture, "Testing change of primary context\n");
+ status = dcerpc_alter_context(p, torture, &p->syntax, &p->transfer_syntax);
+ torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed");
+
+ 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");
+
+ torture_comment(torture, "Testing change of primary context\n");
+ status = dcerpc_alter_context(p2, torture, &p2->syntax, &p2->transfer_syntax);
+ 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->binding_handle, torture, handle);
+ }
+
+ torture_comment(torture, "Testing change of primary context\n");
+ status = dcerpc_alter_context(p, torture, &p->syntax, &p->transfer_syntax);
+ torture_assert_ntstatus_ok(torture, status, "dcerpc_alter_context failed");
+
+ ret &= test_lsa_OpenPolicy2(p->binding_handle, torture, &handle);
+
+ if (handle) {
+ ret &= test_lsa_Close(p->binding_handle, torture, handle);
+ }
+
+ torture_comment(torture, "Testing change of primary context\n");
+ status = dcerpc_alter_context(p, torture, &p2->syntax, &p2->transfer_syntax);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
+
+ ret &= test_lsa_OpenPolicy2_ex(p->binding_handle, torture, &handle,
+ NT_STATUS_CONNECTION_DISCONNECTED,
+ NT_STATUS_CONNECTION_RESET);
+
+ torture_assert(torture, !dcerpc_binding_handle_is_connected(p->binding_handle),
+ "dcerpc disonnected");
+
+ return ret;
+ }
+ 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_RPC_BAD_STUB_DATA);
+
+ ret &= test_lsa_OpenPolicy2(p->binding_handle, torture, &handle);
+
+ if (handle) {
+ ret &= test_lsa_Close(p->binding_handle, 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 0000000..e86d0ab
--- /dev/null
+++ b/source4/torture/rpc/async_bind.c
@@ -0,0 +1,86 @@
+/*
+ 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 "librpc/gen_ndr/ndr_lsa.h"
+#include "lib/cmdline/cmdline.h"
+#include "torture/rpc/torture_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 **pipes;
+ 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;
+ pipes = talloc_array(torture, struct dcerpc_pipe*, torture_numasync);
+ if (pipes == NULL) return false;
+ table = talloc_array(torture, const struct ndr_interface_table*, torture_numasync);
+ if (table == NULL) return false;
+
+ /* credentials */
+ creds = samba_cmdline_get_creds();
+
+ /* 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, &pipes[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 0000000..729a7ca
--- /dev/null
+++ b/source4/torture/rpc/atsvc.c
@@ -0,0 +1,138 @@
+/*
+ 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 "librpc/gen_ndr/ndr_atsvc_c.h"
+#include "torture/rpc/torture_rpc.h"
+
+static bool test_JobGetInfo(struct dcerpc_pipe *p, struct torture_context *tctx, uint32_t job_id)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_atsvc_JobGetInfo_r(b, tctx, &r),
+ "JobGetInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "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)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_atsvc_JobDel_r(b, tctx, &r),
+ "JobDel failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "JobDel failed");
+
+ return true;
+}
+
+static bool test_JobEnum(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_atsvc_JobEnum_r(b, tctx, &r),
+ "JobEnum failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "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)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_atsvc_JobAdd_r(b, tctx, &r),
+ "JobAdd failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "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/backupkey.c b/source4/torture/rpc/backupkey.c
new file mode 100644
index 0000000..c84e628
--- /dev/null
+++ b/source4/torture/rpc/backupkey.c
@@ -0,0 +1,2390 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for backupkey remote protocol rpc operations
+
+ Copyright (C) Matthieu Patou 2010-2011
+ Copyright (C) Andreas Schneider <asn@samba.org> 2015
+
+ 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/security/security.h"
+
+#include "torture/rpc/torture_rpc.h"
+#include "torture/ndr/ndr.h"
+
+#include "librpc/gen_ndr/ndr_backupkey_c.h"
+#include "librpc/gen_ndr/ndr_backupkey.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "lib/cmdline/cmdline.h"
+#include "libcli/auth/proto.h"
+#include <system/network.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+
+enum test_wrong {
+ WRONG_MAGIC,
+ WRONG_R2,
+ WRONG_PAYLOAD_LENGTH,
+ WRONG_CIPHERTEXT_LENGTH,
+ SHORT_PAYLOAD_LENGTH,
+ SHORT_CIPHERTEXT_LENGTH,
+ ZERO_PAYLOAD_LENGTH,
+ ZERO_CIPHERTEXT_LENGTH,
+ RIGHT_KEY,
+ WRONG_KEY,
+ WRONG_SID,
+};
+
+/* Our very special and valued secret */
+/* No need to put const as we cast the array in uint8_t
+ * we will get a warning about the discared const
+ */
+static const char secret[] = "tata yoyo mais qu'est ce qu'il y a sous ton grand chapeau ?";
+
+/* Get the SID from a user */
+static struct dom_sid *get_user_sid(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ const char *user)
+{
+ 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_RefDomainList *domains = NULL;
+ struct lsa_String lsa_name;
+ uint32_t count = 0;
+ struct dom_sid *result;
+ TALLOC_CTX *tmp_ctx;
+ struct dcerpc_pipe *p2;
+ struct dcerpc_binding_handle *b;
+
+ const char *domain = cli_credentials_get_domain(
+ samba_cmdline_get_creds());
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_lsarpc),
+ "could not open lsarpc pipe");
+ b = p2->binding_handle;
+
+ 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_r(b, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx,
+ "OpenPolicy2 failed - %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx,
+ "OpenPolicy2_ failed - %s\n",
+ nt_errstr(r.out.result));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ lsa_name.string = talloc_asprintf(tmp_ctx, "%s\\%s", domain, user);
+
+ 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;
+ l.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupNames_r(b, tmp_ctx, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx,
+ "LookupNames of %s failed - %s\n",
+ lsa_name.string,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ if (domains->count == 0) {
+ return NULL;
+ }
+
+ result = dom_sid_add_rid(mem_ctx,
+ domains->domains[0].sid,
+ l.out.sids->sids[0].rid);
+ c.in.handle = &handle;
+ c.out.handle = &handle;
+
+ status = dcerpc_lsa_Close_r(b, tmp_ctx, &c);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx,
+ "dcerpc_lsa_Close failed - %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ if (!NT_STATUS_IS_OK(c.out.result)) {
+ torture_comment(tctx,
+ "dcerpc_lsa_Close failed - %s\n",
+ nt_errstr(c.out.result));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ talloc_free(tmp_ctx);
+ talloc_free(p2);
+
+ torture_comment(tctx, "Get_user_sid finished\n");
+ return result;
+}
+
+/*
+ * Create a bkrp_encrypted_secret_vX structure
+ * the version depends on the version parameter
+ * the structure is returned as a blob.
+ * The broken flag is to indicate if we want
+ * to create a non conform to specification structre
+ */
+static DATA_BLOB *create_unencryptedsecret(TALLOC_CTX *mem_ctx,
+ bool broken,
+ int version)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ DATA_BLOB *blob = talloc_zero(mem_ctx, DATA_BLOB);
+ enum ndr_err_code ndr_err;
+
+ if (version == 2) {
+ struct bkrp_encrypted_secret_v2 unenc_sec;
+
+ ZERO_STRUCT(unenc_sec);
+ unenc_sec.secret_len = sizeof(secret);
+ unenc_sec.secret = discard_const_p(uint8_t, secret);
+ generate_random_buffer(unenc_sec.payload_key,
+ sizeof(unenc_sec.payload_key));
+
+ ndr_err = ndr_push_struct_blob(blob, blob, &unenc_sec,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_encrypted_secret_v2);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NULL;
+ }
+
+ if (broken) {
+ /* The magic value is correctly set by the NDR push
+ * but we want to test the behavior of the server
+ * if a differrent value is provided
+ */
+ ((uint8_t*)blob->data)[4] = 79; /* A great year !!! */
+ }
+ }
+
+ if (version == 3) {
+ struct bkrp_encrypted_secret_v3 unenc_sec;
+
+ ZERO_STRUCT(unenc_sec);
+ unenc_sec.secret_len = sizeof(secret);
+ unenc_sec.secret = discard_const_p(uint8_t, secret);
+ generate_random_buffer(unenc_sec.payload_key,
+ sizeof(unenc_sec.payload_key));
+
+ ndr_err = ndr_push_struct_blob(blob, blob, &unenc_sec,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_encrypted_secret_v3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NULL;
+ }
+
+ if (broken) {
+ /*
+ * The magic value is correctly set by the NDR push
+ * but we want to test the behavior of the server
+ * if a differrent value is provided
+ */
+ ((uint8_t*)blob->data)[4] = 79; /* A great year !!! */
+ }
+ }
+ talloc_free(tmp_ctx);
+ return blob;
+}
+
+/*
+ * Create an access check structure, the format depends on the version parameter.
+ * If broken is specified then we create a stucture that isn't conform to the
+ * specification.
+ *
+ * If the structure can't be created then NULL is returned.
+ */
+static DATA_BLOB *create_access_check(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *user,
+ bool broken,
+ uint32_t version)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ DATA_BLOB *blob = talloc_zero(mem_ctx, DATA_BLOB);
+ enum ndr_err_code ndr_err;
+ const struct dom_sid *sid = get_user_sid(tctx, tmp_ctx, user);
+
+ if (sid == NULL) {
+ return NULL;
+ }
+
+ if (version == 2) {
+ struct bkrp_access_check_v2 access_struct;
+ gnutls_hash_hd_t dig_ctx;
+ uint8_t nonce[32];
+
+ ZERO_STRUCT(access_struct);
+ generate_random_buffer(nonce, sizeof(nonce));
+ access_struct.nonce_len = sizeof(nonce);
+ access_struct.nonce = nonce;
+ access_struct.sid = *sid;
+
+ ndr_err = ndr_push_struct_blob(blob, blob, &access_struct,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_access_check_v2);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NULL;
+ }
+
+ /*
+ * We pushed the whole structure including a null hash
+ * but the hash need to be calculated only up to the hash field
+ * so we reduce the size of what has to be calculated
+ */
+
+ gnutls_hash_init(&dig_ctx, GNUTLS_DIG_SHA1);
+ gnutls_hash(dig_ctx,
+ blob->data,
+ blob->length - sizeof(access_struct.hash));
+ gnutls_hash_deinit(dig_ctx,
+ blob->data + blob->length - sizeof(access_struct.hash));
+
+ /* Altering the SHA */
+ if (broken) {
+ blob->data[blob->length - 1]++;
+ }
+ }
+
+ if (version == 3) {
+ struct bkrp_access_check_v3 access_struct;
+ gnutls_hash_hd_t dig_ctx;
+ uint8_t nonce[32];
+
+ ZERO_STRUCT(access_struct);
+ generate_random_buffer(nonce, sizeof(nonce));
+ access_struct.nonce_len = sizeof(nonce);
+ access_struct.nonce = nonce;
+ access_struct.sid = *sid;
+
+ ndr_err = ndr_push_struct_blob(blob, blob, &access_struct,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_access_check_v3);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NULL;
+ }
+
+ /*We pushed the whole structure including a null hash
+ * but the hash need to be calculated only up to the hash field
+ * so we reduce the size of what has to be calculated
+ */
+
+ gnutls_hash_init(&dig_ctx, GNUTLS_DIG_SHA512);
+ gnutls_hash(dig_ctx,
+ blob->data,
+ blob->length - sizeof(access_struct.hash));
+ gnutls_hash_deinit(dig_ctx,
+ blob->data + blob->length - sizeof(access_struct.hash));
+
+ /* Altering the SHA */
+ if (broken) {
+ blob->data[blob->length -1]++;
+ }
+ }
+ talloc_free(tmp_ctx);
+ return blob;
+}
+
+
+static DATA_BLOB *encrypt_blob(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *key,
+ DATA_BLOB *iv,
+ DATA_BLOB *to_encrypt,
+ gnutls_cipher_algorithm_t cipher_algo)
+{
+ gnutls_cipher_hd_t cipher_handle = { 0 };
+ gnutls_datum_t gkey = {
+ .data = key->data,
+ .size = key->length,
+ };
+ gnutls_datum_t giv = {
+ .data = iv->data,
+ .size = iv->length,
+ };
+ DATA_BLOB *blob;
+ int rc;
+
+ blob = talloc(mem_ctx, DATA_BLOB);
+ if (blob == NULL) {
+ return NULL;
+ }
+
+ *blob = data_blob_talloc_zero(mem_ctx, to_encrypt->length);
+ if (blob->data == NULL) {
+ talloc_free(blob);
+ return NULL;
+ }
+
+ rc = gnutls_cipher_init(&cipher_handle,
+ cipher_algo,
+ &gkey,
+ &giv);
+ if (rc != GNUTLS_E_SUCCESS) {
+ torture_comment(tctx,
+ "gnutls_cipher_init failed: %s\n",
+ gnutls_strerror(rc));
+ talloc_free(blob);
+ return NULL;
+ }
+
+ rc = gnutls_cipher_encrypt2(cipher_handle,
+ to_encrypt->data,
+ to_encrypt->length,
+ blob->data,
+ blob->length);
+ gnutls_cipher_deinit(cipher_handle);
+ if (rc != GNUTLS_E_SUCCESS) {
+ torture_comment(tctx,
+ "gnutls_cipher_decrypt2 failed: %s\n",
+ gnutls_strerror(rc));
+ return NULL;
+ }
+
+ return blob;
+}
+
+/*
+ * Certs used for this protocol have a GUID in the issuer_uniq_id field.
+ * This function fetch it.
+ */
+static struct GUID *get_cert_guid(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *cert_data,
+ uint32_t cert_len)
+{
+ gnutls_x509_crt_t x509_cert = NULL;
+ gnutls_datum_t x509_crt_data = {
+ .data = cert_data,
+ .size = cert_len,
+ };
+ uint8_t dummy[1] = {0};
+ DATA_BLOB issuer_unique_id = {
+ .data = dummy,
+ .length = 0,
+ };
+ struct GUID *guid = talloc_zero(mem_ctx, struct GUID);
+ NTSTATUS status;
+ int rc;
+
+ rc = gnutls_x509_crt_init(&x509_cert);
+ if (rc != GNUTLS_E_SUCCESS) {
+ torture_comment(tctx,
+ "gnutls_x509_crt_init failed - %s",
+ gnutls_strerror(rc));
+ return NULL;
+ }
+
+ rc = gnutls_x509_crt_import(x509_cert,
+ &x509_crt_data,
+ GNUTLS_X509_FMT_DER);
+ if (rc != GNUTLS_E_SUCCESS) {
+ torture_comment(tctx,
+ "gnutls_x509_crt_import failed - %s",
+ gnutls_strerror(rc));
+ gnutls_x509_crt_deinit(x509_cert);
+ return NULL;
+ }
+
+ /* Get the buffer size */
+ rc = gnutls_x509_crt_get_issuer_unique_id(x509_cert,
+ (char *)issuer_unique_id.data,
+ &issuer_unique_id.length);
+ if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER ||
+ issuer_unique_id.length == 0) {
+ gnutls_x509_crt_deinit(x509_cert);
+ return NULL;
+ }
+
+ issuer_unique_id = data_blob_talloc_zero(mem_ctx,
+ issuer_unique_id.length);
+ if (issuer_unique_id.data == NULL) {
+ gnutls_x509_crt_deinit(x509_cert);
+ return NULL;
+ }
+
+ rc = gnutls_x509_crt_get_issuer_unique_id(x509_cert,
+ (char *)issuer_unique_id.data,
+ &issuer_unique_id.length);
+ gnutls_x509_crt_deinit(x509_cert);
+ if (rc != GNUTLS_E_SUCCESS) {
+ torture_comment(tctx,
+ "gnutls_x509_crt_get_issuer_unique_id failed - %s",
+ gnutls_strerror(rc));
+ return NULL;
+ }
+
+ status = GUID_from_data_blob(&issuer_unique_id, guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ return guid;
+}
+
+/*
+ * Encrypt a blob with the private key of the certificate
+ * passed as a parameter.
+ */
+static DATA_BLOB *encrypt_blob_pk(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *cert_data,
+ uint32_t cert_len,
+ DATA_BLOB *to_encrypt)
+{
+ gnutls_x509_crt_t x509_cert;
+ gnutls_datum_t x509_crt_data = {
+ .data = cert_data,
+ .size = cert_len,
+ };
+ gnutls_pubkey_t pubkey;
+ gnutls_datum_t plaintext = {
+ .data = to_encrypt->data,
+ .size = to_encrypt->length,
+ };
+ gnutls_datum_t ciphertext = {
+ .data = NULL,
+ };
+ DATA_BLOB *blob;
+ int rc;
+
+ rc = gnutls_x509_crt_init(&x509_cert);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return NULL;
+ }
+
+ rc = gnutls_x509_crt_import(x509_cert,
+ &x509_crt_data,
+ GNUTLS_X509_FMT_DER);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_x509_crt_deinit(x509_cert);
+ return NULL;
+ }
+
+ rc = gnutls_pubkey_init(&pubkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_x509_crt_deinit(x509_cert);
+ return NULL;
+ }
+
+ rc = gnutls_pubkey_import_x509(pubkey,
+ x509_cert,
+ 0);
+ gnutls_x509_crt_deinit(x509_cert);
+ if (rc != GNUTLS_E_SUCCESS) {
+ gnutls_pubkey_deinit(pubkey);
+ return NULL;
+ }
+
+ rc = gnutls_pubkey_encrypt_data(pubkey,
+ 0,
+ &plaintext,
+ &ciphertext);
+ gnutls_pubkey_deinit(pubkey);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return NULL;
+ }
+
+ blob = talloc_zero(mem_ctx, DATA_BLOB);
+ if (blob == NULL) {
+ gnutls_pubkey_deinit(pubkey);
+ return NULL;
+ }
+
+ *blob = data_blob_talloc(blob, ciphertext.data, ciphertext.size);
+ gnutls_free(ciphertext.data);
+ if (blob->data == NULL) {
+ gnutls_pubkey_deinit(pubkey);
+ return NULL;
+ }
+
+ return blob;
+}
+
+static struct bkrp_BackupKey *createRetrieveBackupKeyGUIDStruct(struct torture_context *tctx,
+ struct dcerpc_pipe *p, int version, DATA_BLOB *out)
+{
+ struct dcerpc_binding *binding;
+ struct bkrp_client_side_wrapped data;
+ struct GUID *g = talloc(tctx, struct GUID);
+ struct bkrp_BackupKey *r = talloc_zero(tctx, struct bkrp_BackupKey);
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ NTSTATUS status;
+
+ if (r == NULL) {
+ return NULL;
+ }
+
+ binding = dcerpc_binding_dup(tctx, p->binding);
+ if (binding == NULL) {
+ return NULL;
+ }
+
+ status = dcerpc_binding_set_flags(binding, DCERPC_SEAL|DCERPC_AUTH_SPNEGO, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ ZERO_STRUCT(data);
+ status = GUID_from_string(BACKUPKEY_RETRIEVE_BACKUP_KEY_GUID, g);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ r->in.guidActionAgent = g;
+ data.version = version;
+ ndr_err = ndr_push_struct_blob(&blob, tctx, &data,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_client_side_wrapped);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NULL;
+ }
+ r->in.data_in = blob.data;
+ r->in.data_in_len = blob.length;
+ r->out.data_out = &out->data;
+ r->out.data_out_len = talloc(r, uint32_t);
+ return r;
+}
+
+static struct bkrp_BackupKey *createRestoreGUIDStruct(struct torture_context *tctx,
+ struct dcerpc_pipe *p, int version, DATA_BLOB *out,
+ bool norevert,
+ bool broken_version,
+ bool broken_user,
+ bool broken_magic_secret,
+ bool broken_magic_access,
+ bool broken_hash_access,
+ bool broken_cert_guid)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct bkrp_client_side_wrapped data;
+ DATA_BLOB *xs;
+ DATA_BLOB *sec;
+ DATA_BLOB *enc_sec = NULL;
+ DATA_BLOB *enc_xs = NULL;
+ DATA_BLOB *blob2;
+ DATA_BLOB enc_sec_reverted;
+ DATA_BLOB key;
+ DATA_BLOB iv;
+ DATA_BLOB out_blob;
+ struct GUID *guid, *g;
+ int t;
+ uint32_t size;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+ const char *user;
+ gnutls_cipher_algorithm_t cipher_algo;
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, version, &out_blob);
+ if (r == NULL) {
+ return NULL;
+ }
+
+ if (broken_user) {
+ /* we take a fake user*/
+ user = "guest";
+ } else {
+ user = cli_credentials_get_username(
+ samba_cmdline_get_creds());
+ }
+
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ "Get GUID");
+ torture_assert_werr_ok(tctx, r->out.result,
+ "Get GUID");
+
+ /*
+ * We have to set it outside of the function createRetrieveBackupKeyGUIDStruct
+ * the len of the blob, this is due to the fact that they don't have the
+ * same size (one is 32bits the other 64bits)
+ */
+ out_blob.length = *r->out.data_out_len;
+
+ sec = create_unencryptedsecret(tctx, broken_magic_secret, version);
+ if (sec == NULL) {
+ return NULL;
+ }
+
+ xs = create_access_check(tctx, p, tctx, user, broken_hash_access, version);
+ if (xs == NULL) {
+ return NULL;
+ }
+
+ if (broken_magic_access){
+ /* The start of the access_check structure contains the
+ * GUID of the certificate
+ */
+ xs->data[0]++;
+ }
+
+ enc_sec = encrypt_blob_pk(tctx, tctx, out_blob.data, out_blob.length, sec);
+ if (!enc_sec) {
+ return NULL;
+ }
+ enc_sec_reverted.data = talloc_array(tctx, uint8_t, enc_sec->length);
+ if (enc_sec_reverted.data == NULL) {
+ return NULL;
+ }
+ enc_sec_reverted.length = enc_sec->length;
+
+ /*
+ * We DO NOT revert the array on purpose it's in order to check that
+ * when the server is not able to decrypt then it answer the correct error
+ */
+ if (norevert) {
+ for(t=0; t< enc_sec->length; t++) {
+ enc_sec_reverted.data[t] = ((uint8_t*)enc_sec->data)[t];
+ }
+ } else {
+ for(t=0; t< enc_sec->length; t++) {
+ enc_sec_reverted.data[t] = ((uint8_t*)enc_sec->data)[enc_sec->length - t -1];
+ }
+ }
+
+ size = sec->length;
+ switch (version) {
+ case 2:
+ cipher_algo = GNUTLS_CIPHER_3DES_CBC;
+ break;
+ case 3:
+ cipher_algo = GNUTLS_CIPHER_AES_256_CBC;
+ break;
+ default:
+ return NULL;
+ }
+ iv.length = gnutls_cipher_get_iv_size(cipher_algo);
+ iv.data = sec->data + (size - iv.length);
+
+ key.length = gnutls_cipher_get_key_size(cipher_algo);
+ key.data = sec->data + (size - (key.length + iv.length));
+
+ enc_xs = encrypt_blob(tctx, tctx, &key, &iv, xs, cipher_algo);
+ if (!enc_xs) {
+ return NULL;
+ }
+
+ /* To cope with the fact that heimdal do padding at the end for the moment */
+ enc_xs->length = xs->length;
+
+ guid = get_cert_guid(tctx, tctx, out_blob.data, out_blob.length);
+ if (guid == NULL) {
+ return NULL;
+ }
+
+ if (broken_version) {
+ data.version = 1;
+ } else {
+ data.version = version;
+ }
+
+ data.guid = *guid;
+ data.encrypted_secret = enc_sec_reverted.data;
+ data.access_check = enc_xs->data;
+ data.encrypted_secret_len = enc_sec->length;
+ data.access_check_len = enc_xs->length;
+
+ /* We want the blob to persist after this function so we don't
+ * allocate it in the stack
+ */
+ blob2 = talloc(tctx, DATA_BLOB);
+ if (blob2 == NULL) {
+ return NULL;
+ }
+
+ ndr_err = ndr_push_struct_blob(blob2, tctx, &data,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_client_side_wrapped);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return NULL;
+ }
+
+ if (broken_cert_guid) {
+ blob2->data[12]++;
+ }
+
+ ZERO_STRUCT(*r);
+
+ g = talloc(tctx, struct GUID);
+ if (g == NULL) {
+ return NULL;
+ }
+
+ status = GUID_from_string(BACKUPKEY_RESTORE_GUID, g);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ r->in.guidActionAgent = g;
+ r->in.data_in = blob2->data;
+ r->in.data_in_len = blob2->length;
+ r->in.param = 0;
+ r->out.data_out = &(out->data);
+ r->out.data_out_len = talloc(r, uint32_t);
+ return r;
+}
+
+/* Check that we are able to receive the certificate of the DCs
+ * used for client wrap version of the backup key protocol
+ */
+static bool test_RetrieveBackupKeyGUID(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ if (r == NULL) {
+ return false;
+ }
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ "Get GUID");
+
+ out_blob.length = *r->out.data_out_len;
+ torture_assert_werr_equal(tctx,
+ r->out.result,
+ WERR_OK,
+ "Wrong dce/rpc error code");
+ } else {
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED,
+ "Get GUID");
+ }
+ return true;
+}
+
+/* Test to check the failure to recover a secret because the
+ * secret blob is not reversed
+ */
+static bool test_RestoreGUID_ko(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ enum ndr_err_code ndr_err;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_client_side_unwrapped resp;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob,
+ true, false, false, false, false, false, false);
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped);
+ torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped");
+ torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_PARAMETER, "Wrong error code");
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_RestoreGUID_wrongversion(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ enum ndr_err_code ndr_err;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_client_side_unwrapped resp;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob,
+ false, true, false, false, false, false, false);
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped);
+ torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped");
+ torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_PARAMETER, "Wrong error code on wrong version");
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_RestoreGUID_wronguser(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ enum ndr_err_code ndr_err;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_client_side_unwrapped resp;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob,
+ false, false, true, false, false, false, false);
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped);
+ torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped");
+ torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_ACCESS, "Restore GUID");
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_RestoreGUID_v3(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ enum ndr_err_code ndr_err;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_client_side_unwrapped resp;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob,
+ false, false, false, false, false, false, false);
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped);
+ torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 1, "Unable to unmarshall bkrp_client_side_unwrapped");
+ torture_assert_werr_equal(tctx, r->out.result, WERR_OK, "Restore GUID");
+ torture_assert_str_equal(tctx, (char*)resp.secret.data, secret, "Wrong secret");
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_RestoreGUID(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_client_side_unwrapped resp;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob,
+ false, false, false, false, false, false, false);
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ torture_assert_werr_equal(tctx, r->out.result, WERR_OK, "Restore GUID");
+ torture_assert_ndr_err_equal(tctx,
+ ndr_pull_struct_blob(&out_blob, tctx, &resp,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped),
+ NDR_ERR_SUCCESS,
+ "Unable to unmarshall bkrp_client_side_unwrapped");
+ torture_assert_str_equal(tctx, (char*)resp.secret.data, secret, "Wrong secret");
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_RestoreGUID_badmagiconsecret(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ enum ndr_err_code ndr_err;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_client_side_unwrapped resp;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob,
+ false, false, false, true, false, false, false);
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped);
+ torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped");
+ torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Wrong error code while providing bad magic in secret");
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_RestoreGUID_emptyrequest(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob,
+ false, false, false, true, false, false, true);
+
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed");
+ r->in.data_in = talloc(tctx, uint8_t);
+ r->in.data_in_len = 0;
+ r->in.param = 0;
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_PARAMETER, "Bad error code on wrong has in access check");
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_RestoreGUID_badcertguid(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ enum ndr_err_code ndr_err;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_client_side_unwrapped resp;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 3, &out_blob,
+ false, false, false, false, false, false, true);
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct() failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped);
+ torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped");
+
+ /*
+ * Windows 2012R2 has, presumably, a programming error
+ * returning an NTSTATUS code on this interface
+ */
+ if (W_ERROR_V(r->out.result) != NT_STATUS_V(NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Bad error code on wrong has in access check");
+ }
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_RestoreGUID_badmagicaccesscheck(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ enum ndr_err_code ndr_err;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_client_side_unwrapped resp;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob,
+ false, false, false, false, true, false, false);
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped);
+ torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped");
+ torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Bad error code on wrong has in access check");
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_RestoreGUID_badhashaccesscheck(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ enum ndr_err_code ndr_err;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_client_side_unwrapped resp;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ struct bkrp_BackupKey *r = createRestoreGUIDStruct(tctx, p, 2, &out_blob,
+ false, false, false, false, false, true, false);
+ torture_assert(tctx, r != NULL, "createRestoreGUIDStruct failed");
+ torture_assert_ntstatus_ok(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r), "Restore GUID");
+ out_blob.length = *r->out.data_out_len;
+ ndr_err = ndr_pull_struct_blob(&out_blob, tctx, &resp, (ndr_pull_flags_fn_t)ndr_pull_bkrp_client_side_unwrapped);
+ torture_assert_int_equal(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), 0, "Unable to unmarshall bkrp_client_side_unwrapped");
+ torture_assert_werr_equal(tctx, r->out.result, WERR_INVALID_DATA, "Bad error code on wrong has in access check");
+ } else {
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ torture_assert_ntstatus_equal(tctx, dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED, "Get GUID");
+ }
+
+ return true;
+}
+
+/*
+ * Check that the RSA modulus in the certificate of the DCs has 2048 bits.
+ */
+static bool test_RetrieveBackupKeyGUID_validate(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB out_blob;
+ struct bkrp_BackupKey *r = createRetrieveBackupKeyGUIDStruct(tctx, p, 2, &out_blob);
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ torture_assert(tctx, r != NULL, "test_RetrieveBackupKeyGUID_validate failed");
+
+ if (r == NULL) {
+ return false;
+ }
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ gnutls_x509_crt_t x509_cert = NULL;
+ gnutls_pubkey_t pubkey = NULL;
+ gnutls_datum_t x509_crt_data;
+ gnutls_pk_algorithm_t pubkey_algo;
+ uint8_t dummy[1] = {0};
+ DATA_BLOB subject_unique_id = {
+ .data = dummy,
+ .length = 0,
+ };
+ DATA_BLOB issuer_unique_id = {
+ .data = dummy,
+ .length = 0,
+ };
+ DATA_BLOB reversed = {
+ .data = dummy,
+ .length = 0,
+ };
+ DATA_BLOB serial_number;
+ unsigned int RSA_returned_bits = 0;
+ int version;
+ size_t i;
+ int cmp;
+ int rc;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ "Get GUID");
+
+ torture_assert_werr_ok(tctx, r->out.result,
+ "Get GUID");
+
+ out_blob.length = *r->out.data_out_len;
+
+ x509_crt_data.data = out_blob.data;
+ x509_crt_data.size = out_blob.length;
+
+ rc = gnutls_x509_crt_init(&x509_cert);
+ if (rc != GNUTLS_E_SUCCESS) {
+ return NULL;
+ }
+
+ rc = gnutls_x509_crt_import(x509_cert,
+ &x509_crt_data,
+ GNUTLS_X509_FMT_DER);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_x509_crt_import failed");
+
+ /* Compare unique ids */
+
+ /* Get buffer size */
+ rc = gnutls_x509_crt_get_subject_unique_id(x509_cert,
+ (char *)subject_unique_id.data,
+ &subject_unique_id.length);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SHORT_MEMORY_BUFFER,
+ "gnutls_x509_crt_get_subject_unique_id "
+ "get buffer size failed");
+
+ subject_unique_id = data_blob_talloc_zero(tctx,
+ subject_unique_id.length);
+
+ rc = gnutls_x509_crt_get_subject_unique_id(x509_cert,
+ (char *)subject_unique_id.data,
+ &subject_unique_id.length);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_x509_crt_get_subject_unique_id failed");
+
+ rc = gnutls_x509_crt_get_issuer_unique_id(x509_cert,
+ (char *)issuer_unique_id.data,
+ &issuer_unique_id.length);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SHORT_MEMORY_BUFFER,
+ "gnutls_x509_crt_get_issuer_unique_id "
+ "get buffer size failed");
+
+ issuer_unique_id = data_blob_talloc_zero(tctx,
+ issuer_unique_id.length);
+
+ rc = gnutls_x509_crt_get_issuer_unique_id(x509_cert,
+ (char *)issuer_unique_id.data,
+ &issuer_unique_id.length);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_x509_crt_get_issuer_unique_id failed");
+
+ cmp = data_blob_cmp(&subject_unique_id, &issuer_unique_id);
+ torture_assert(tctx,
+ cmp == 0,
+ "The GUID to identify the public key is not "
+ "identical");
+
+ rc = gnutls_x509_crt_get_serial(x509_cert,
+ reversed.data,
+ &reversed.length);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SHORT_MEMORY_BUFFER,
+ "gnutls_x509_crt_get_serial "
+ "get buffer size failed");
+
+ reversed = data_blob_talloc_zero(tctx,
+ reversed.length);
+
+ rc = gnutls_x509_crt_get_serial(x509_cert,
+ reversed.data,
+ &reversed.length);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_x509_crt_get_serial failed");
+
+ /*
+ * Heimdal sometimes adds a leading byte to the data buffer of
+ * the serial number. So lets uses the subject_unique_id size
+ * and ignore the leading byte.
+ */
+ serial_number = data_blob_talloc_zero(tctx,
+ subject_unique_id.length);
+
+ for (i = 0; i < serial_number.length; i++) {
+ serial_number.data[i] = reversed.data[reversed.length - i - 1];
+ }
+
+ cmp = data_blob_cmp(&subject_unique_id, &serial_number);
+ torture_assert(tctx,
+ cmp == 0,
+ "The GUID to identify the public key is not "
+ "identical");
+
+ /* Check certificate version */
+ version = gnutls_x509_crt_get_version(x509_cert);
+ torture_assert_int_equal(tctx,
+ version,
+ 3,
+ "Invalid certificate version");
+
+ /* Get the public key */
+ rc = gnutls_pubkey_init(&pubkey);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_pubkey_init failed");
+
+ rc = gnutls_pubkey_import_x509(pubkey,
+ x509_cert,
+ 0);
+ gnutls_x509_crt_deinit(x509_cert);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_pubkey_import_x509 failed");
+
+ pubkey_algo = gnutls_pubkey_get_pk_algorithm(pubkey,
+ &RSA_returned_bits);
+ gnutls_pubkey_deinit(pubkey);
+ torture_assert_int_equal(tctx,
+ pubkey_algo,
+ GNUTLS_PK_RSA,
+ "gnutls_pubkey_get_pk_algorithm did "
+ "not return a RSA key");
+ torture_assert_int_equal(tctx,
+ RSA_returned_bits,
+ 2048,
+ "RSA Key doesn't have 2048 bits");
+ } else {
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, r),
+ NT_STATUS_ACCESS_DENIED,
+ "Get GUID");
+ }
+
+ return true;
+}
+
+static bool test_ServerWrap_encrypt_decrypt(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct bkrp_BackupKey r;
+ struct GUID guid;
+ DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret));
+ DATA_BLOB encrypted;
+ uint32_t enclen;
+ DATA_BLOB decrypted;
+ uint32_t declen;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+ ZERO_STRUCT(r);
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ /* Encrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_BACKUP_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = plaintext.data;
+ r.in.data_in_len = plaintext.length;
+ r.in.param = 0;
+ r.out.data_out = &encrypted.data;
+ r.out.data_out_len = &enclen;
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "encrypt");
+ } else {
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ NT_STATUS_ACCESS_DENIED,
+ "encrypt");
+ return true;
+ }
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "encrypt");
+ encrypted.length = *r.out.data_out_len;
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = encrypted.data;
+ r.in.data_in_len = encrypted.length;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "decrypt");
+ decrypted.length = *r.out.data_out_len;
+
+ /* Compare */
+ torture_assert_data_blob_equal(tctx, plaintext, decrypted, "Decrypt failed");
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = encrypted.data;
+ r.in.data_in_len = encrypted.length;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "decrypt");
+ decrypted.length = *r.out.data_out_len;
+
+ /* Compare */
+ torture_assert_data_blob_equal(tctx, plaintext, decrypted, "Decrypt failed");
+ return true;
+}
+
+static bool test_ServerWrap_decrypt_wrong_keyGUID(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct bkrp_BackupKey r;
+ struct GUID guid;
+ DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret));
+ DATA_BLOB encrypted;
+ uint32_t enclen;
+ DATA_BLOB decrypted;
+ uint32_t declen;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ enum ndr_err_code ndr_err;
+ struct bkrp_server_side_wrapped server_side_wrapped;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+ ZERO_STRUCT(r);
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ /* Encrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_BACKUP_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = plaintext.data;
+ r.in.data_in_len = plaintext.length;
+ r.in.param = 0;
+ r.out.data_out = &encrypted.data;
+ r.out.data_out_len = &enclen;
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "encrypt");
+ } else {
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ NT_STATUS_ACCESS_DENIED,
+ "encrypt");
+ return true;
+ }
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "encrypt");
+ encrypted.length = *r.out.data_out_len;
+
+ ndr_err = ndr_pull_struct_blob(&encrypted, tctx, &server_side_wrapped,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped);
+ torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "pull of server_side_wrapped");
+
+ /* Change the GUID */
+ server_side_wrapped.guid = GUID_random();
+
+ ndr_err = ndr_push_struct_blob(&encrypted, tctx, &server_side_wrapped,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_server_side_wrapped);
+ torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "push of server_side_wrapped");
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = encrypted.data;
+ r.in.data_in_len = encrypted.length;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_DATA,
+ "decrypt should fail with WERR_INVALID_DATA");
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = encrypted.data;
+ r.in.data_in_len = encrypted.length;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_DATA,
+ "decrypt should fail with WERR_INVALID_DATA");
+
+ return true;
+}
+
+static bool test_ServerWrap_decrypt_empty_request(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct bkrp_BackupKey r;
+ struct GUID guid;
+ DATA_BLOB decrypted;
+ uint32_t declen;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint8_t short_request[4] = { 1, 0, 0, 0 };
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+ ZERO_STRUCT(r);
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = short_request;
+ r.in.data_in_len = 0;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "encrypt");
+ } else {
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ NT_STATUS_ACCESS_DENIED,
+ "encrypt");
+ return true;
+ }
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "decrypt should fail with WERR_INVALID_PARAMETER");
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = short_request;
+ r.in.data_in_len = 0;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "decrypt should fail with WERR_INVALID_PARAMETER");
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = NULL;
+ r.in.data_in_len = 0;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ NT_STATUS_INVALID_PARAMETER_MIX,
+ "decrypt");
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = NULL;
+ r.in.data_in_len = 0;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ NT_STATUS_INVALID_PARAMETER_MIX,
+ "decrypt");
+
+ return true;
+}
+
+
+static bool test_ServerWrap_decrypt_short_request(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct bkrp_BackupKey r;
+ struct GUID guid;
+ DATA_BLOB decrypted;
+ uint32_t declen;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint8_t short_request[4] = { 1, 0, 0, 0 };
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+ ZERO_STRUCT(r);
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = short_request;
+ r.in.data_in_len = 4;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "encrypt");
+ } else {
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ NT_STATUS_ACCESS_DENIED,
+ "encrypt");
+ return true;
+ }
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "decrypt should fail with WERR_INVALID_PARM");
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = short_request;
+ r.in.data_in_len = 4;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "decrypt should fail with WERR_INVALID_PARAMETER");
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = short_request;
+ r.in.data_in_len = 1;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "decrypt should fail with WERR_INVALID_PARAMETER");
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = short_request;
+ r.in.data_in_len = 1;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "decrypt should fail with WERR_INVALID_PARAMETER");
+
+ return true;
+}
+
+static bool test_ServerWrap_encrypt_decrypt_manual(struct torture_context *tctx,
+ struct bkrp_server_side_wrapped *server_side_wrapped,
+ enum test_wrong wrong)
+{
+ char *lsa_binding_string = NULL;
+ struct dcerpc_binding *lsa_binding = NULL;
+ struct dcerpc_pipe *lsa_p = NULL;
+ struct dcerpc_binding_handle *lsa_b = NULL;
+ struct lsa_OpenSecret r_secret;
+ struct lsa_QuerySecret r_query_secret;
+ struct policy_handle *handle, sec_handle;
+ struct bkrp_BackupKey r;
+ struct GUID preferred_key_guid;
+ DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret));
+ DATA_BLOB preferred_key, preferred_key_clear, session_key,
+ decrypt_key, decrypt_key_clear, encrypted_blob,
+ sid_blob;
+ struct bkrp_dc_serverwrap_key server_key;
+ struct lsa_DATA_BUF_PTR bufp1;
+ char *key_guid_string;
+ struct bkrp_rc4encryptedpayload rc4payload;
+ struct dom_sid *caller_sid;
+ uint8_t symkey[20]; /* SHA-1 hash len */
+ uint8_t mackey[20]; /* SHA-1 hash len */
+ uint8_t mac[20]; /* SHA-1 hash len */
+ gnutls_hmac_hd_t hmac_hnd;
+ gnutls_cipher_hd_t cipher_hnd;
+ gnutls_datum_t cipher_key;
+ int rc;
+
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(r_secret);
+ ZERO_STRUCT(r_query_secret);
+
+ /* Now read BCKUPKEY_P and prove we can do a matching decrypt and encrypt */
+
+ /* lsa_OpenSecret only works with ncacn_np and AUTH_LEVEL_NONE */
+ lsa_binding_string = talloc_asprintf(tctx, "ncacn_np:%s",
+ torture_setting_string(tctx, "host", NULL));
+ torture_assert(tctx, lsa_binding_string != NULL, "lsa_binding_string");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_parse_binding(tctx, lsa_binding_string, &lsa_binding),
+ "Failed to parse dcerpc binding");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &lsa_p,
+ lsa_binding, &ndr_table_lsarpc,
+ samba_cmdline_get_creds(),
+ tctx->ev, tctx->lp_ctx),
+ "Opening LSA pipe");
+ lsa_b = lsa_p->binding_handle;
+
+ torture_assert(tctx, test_lsa_OpenPolicy2(lsa_b, tctx, &handle), "OpenPolicy failed");
+ r_secret.in.name.string = "G$BCKUPKEY_P";
+
+ r_secret.in.handle = handle;
+ r_secret.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r_secret.out.sec_handle = &sec_handle;
+
+ torture_comment(tctx, "Testing OpenSecret\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(lsa_b, tctx, &r_secret),
+ "OpenSecret failed");
+ torture_assert_ntstatus_ok(tctx, r_secret.out.result,
+ "OpenSecret failed");
+
+ r_query_secret.in.sec_handle = &sec_handle;
+ r_query_secret.in.new_val = &bufp1;
+ bufp1.buf = NULL;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(lsa_b, tctx, &r_query_secret),
+ "QuerySecret failed");
+ torture_assert_ntstatus_ok(tctx, r_query_secret.out.result,
+ "QuerySecret failed");
+
+
+ preferred_key.data = r_query_secret.out.new_val->buf->data;
+ preferred_key.length = r_query_secret.out.new_val->buf->size;
+ torture_assert_ntstatus_ok(tctx, dcerpc_fetch_session_key(lsa_p, &session_key),
+ "dcerpc_fetch_session_key failed");
+
+ torture_assert_ntstatus_ok(tctx,
+ sess_decrypt_blob(tctx,
+ &preferred_key, &session_key, &preferred_key_clear),
+ "sess_decrypt_blob failed");
+
+ torture_assert_ntstatus_ok(tctx, GUID_from_ndr_blob(&preferred_key_clear, &preferred_key_guid),
+ "GUID parse failed");
+
+ torture_assert_guid_equal(tctx, server_side_wrapped->guid,
+ preferred_key_guid,
+ "GUID didn't match value pointed at by G$BCKUPKEY_P");
+
+ /* And read BCKUPKEY_<guid> and get the actual key */
+
+ key_guid_string = GUID_string(tctx, &server_side_wrapped->guid);
+ r_secret.in.name.string = talloc_asprintf(tctx, "G$BCKUPKEY_%s", key_guid_string);
+
+ r_secret.in.handle = handle;
+ r_secret.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r_secret.out.sec_handle = &sec_handle;
+
+ torture_comment(tctx, "Testing OpenSecret\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(lsa_b, tctx, &r_secret),
+ "OpenSecret failed");
+ torture_assert_ntstatus_ok(tctx, r_secret.out.result,
+ "OpenSecret failed");
+
+ r_query_secret.in.sec_handle = &sec_handle;
+ r_query_secret.in.new_val = &bufp1;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(lsa_b, tctx, &r_query_secret),
+ "QuerySecret failed");
+ torture_assert_ntstatus_ok(tctx, r_query_secret.out.result,
+ "QuerySecret failed");
+
+
+ decrypt_key.data = r_query_secret.out.new_val->buf->data;
+ decrypt_key.length = r_query_secret.out.new_val->buf->size;
+
+ torture_assert_ntstatus_ok(tctx,
+ sess_decrypt_blob(tctx,
+ &decrypt_key, &session_key, &decrypt_key_clear),
+ "sess_decrypt_blob failed");
+
+ torture_assert_ndr_err_equal(tctx, ndr_pull_struct_blob(&decrypt_key_clear, tctx, &server_key,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_dc_serverwrap_key),
+ NDR_ERR_SUCCESS, "Failed to parse server_key");
+
+ torture_assert_int_equal(tctx, server_key.magic, 1, "Failed to correctly decrypt server key");
+
+ /*
+ * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1
+ * BACKUPKEY_BACKUP_GUID, it really is the whole key
+ */
+ gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_SHA1,
+ server_key.key,
+ sizeof(server_key.key));
+ gnutls_hmac(hmac_hnd,
+ server_side_wrapped->r2,
+ sizeof(server_side_wrapped->r2));
+ gnutls_hmac_output(hmac_hnd, symkey);
+
+ /* rc4 decrypt sid and secret using sym key */
+ cipher_key.data = symkey;
+ cipher_key.size = sizeof(symkey);
+
+ encrypted_blob = data_blob_talloc(tctx, server_side_wrapped->rc4encryptedpayload,
+ server_side_wrapped->ciphertext_length);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &cipher_key,
+ NULL);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_cipher_init failed");
+ rc = gnutls_cipher_encrypt2(cipher_hnd,
+ encrypted_blob.data,
+ encrypted_blob.length,
+ encrypted_blob.data,
+ encrypted_blob.length);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_cipher_encrypt failed");
+ gnutls_cipher_deinit(cipher_hnd);
+
+ torture_assert_ndr_err_equal(tctx, ndr_pull_struct_blob(&encrypted_blob, tctx, &rc4payload,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_rc4encryptedpayload),
+ NDR_ERR_SUCCESS, "Failed to parse rc4encryptedpayload");
+
+ torture_assert_int_equal(tctx, rc4payload.secret_data.length,
+ server_side_wrapped->payload_length,
+ "length of decrypted payload not the length declared in surrounding structure");
+
+ /*
+ * This is *not* the leading 64 bytes, as indicated in MS-BKRP 3.1.4.1.1
+ * BACKUPKEY_BACKUP_GUID, it really is the whole key
+ */
+ gnutls_hmac(hmac_hnd,
+ rc4payload.r3,
+ sizeof(rc4payload.r3));
+ gnutls_hmac_deinit(hmac_hnd, mackey);
+
+ torture_assert_ndr_err_equal(tctx, ndr_push_struct_blob(&sid_blob, tctx, &rc4payload.sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid),
+ NDR_ERR_SUCCESS, "unable to push SID");
+
+ gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_SHA1,
+ mackey,
+ sizeof(mackey));
+ /* SID field */
+ gnutls_hmac(hmac_hnd,
+ sid_blob.data,
+ sid_blob.length);
+ /* Secret field */
+ gnutls_hmac(hmac_hnd,
+ rc4payload.secret_data.data,
+ rc4payload.secret_data.length);
+ gnutls_hmac_output(hmac_hnd, mac);
+
+ torture_assert_mem_equal(tctx, mac, rc4payload.mac, sizeof(mac), "mac not correct");
+ torture_assert_int_equal(tctx, rc4payload.secret_data.length,
+ plaintext.length, "decrypted data is not correct length");
+ torture_assert_mem_equal(tctx, rc4payload.secret_data.data,
+ plaintext.data, plaintext.length,
+ "decrypted data is not correct");
+
+ /* Not strictly correct all the time, but good enough for this test */
+ caller_sid = get_user_sid(tctx, tctx,
+ cli_credentials_get_username(
+ samba_cmdline_get_creds()));
+
+ torture_assert_sid_equal(tctx, &rc4payload.sid, caller_sid, "Secret saved with wrong SID");
+
+
+ /* RE-encrypt */
+
+ if (wrong == WRONG_SID) {
+ rc4payload.sid.sub_auths[rc4payload.sid.num_auths - 1] = DOMAIN_RID_KRBTGT;
+ }
+
+ dump_data_pw("mackey: \n", mackey, sizeof(mackey));
+
+ torture_assert_ndr_err_equal(tctx,
+ ndr_push_struct_blob(&sid_blob, tctx, &rc4payload.sid,
+ (ndr_push_flags_fn_t)ndr_push_dom_sid),
+ NDR_ERR_SUCCESS,
+ "push of sid failed");
+
+ /* SID field */
+ gnutls_hmac(hmac_hnd,
+ sid_blob.data,
+ sid_blob.length);
+ /* Secret field */
+ gnutls_hmac(hmac_hnd,
+ rc4payload.secret_data.data,
+ rc4payload.secret_data.length);
+ gnutls_hmac_deinit(hmac_hnd, rc4payload.mac);
+
+ dump_data_pw("rc4payload.mac: \n", rc4payload.mac, sizeof(rc4payload.mac));
+
+ torture_assert_ndr_err_equal(tctx,
+ ndr_push_struct_blob(&encrypted_blob, tctx, &rc4payload,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_rc4encryptedpayload),
+ NDR_ERR_SUCCESS,
+ "push of rc4payload failed");
+
+ if (wrong == WRONG_KEY) {
+ symkey[0] = 78;
+ symkey[1] = 78;
+ symkey[2] = 78;
+ }
+
+ /* rc4 encrypt sid and secret using sym key */
+ cipher_key.data = symkey;
+ cipher_key.size = sizeof(symkey);
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &cipher_key,
+ NULL);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_cipher_init failed");
+ rc = gnutls_cipher_encrypt2(cipher_hnd,
+ encrypted_blob.data,
+ encrypted_blob.length,
+ encrypted_blob.data,
+ encrypted_blob.length);
+ torture_assert_int_equal(tctx,
+ rc,
+ GNUTLS_E_SUCCESS,
+ "gnutls_cipher_encrypt failed");
+ gnutls_cipher_deinit(cipher_hnd);
+
+
+ /* re-create server wrap structure */
+
+ torture_assert_int_equal(tctx, encrypted_blob.length,
+ server_side_wrapped->ciphertext_length,
+ "expected encrypted length not to change");
+ if (wrong == RIGHT_KEY) {
+ torture_assert_mem_equal(tctx, server_side_wrapped->rc4encryptedpayload,
+ encrypted_blob.data,
+ encrypted_blob.length,
+ "expected encrypted data not to change");
+ }
+
+ server_side_wrapped->payload_length = rc4payload.secret_data.length;
+ server_side_wrapped->ciphertext_length = encrypted_blob.length;
+ server_side_wrapped->rc4encryptedpayload = encrypted_blob.data;
+
+ return true;
+}
+
+
+static bool test_ServerWrap_decrypt_wrong_stuff(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ enum test_wrong wrong)
+{
+ struct bkrp_BackupKey r;
+ struct GUID guid;
+ DATA_BLOB plaintext = data_blob_const(secret, sizeof(secret));
+ DATA_BLOB encrypted;
+ uint32_t enclen;
+ DATA_BLOB decrypted;
+ uint32_t declen;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ enum ndr_err_code ndr_err;
+ struct bkrp_server_side_wrapped server_side_wrapped;
+ bool repush = false;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+ ZERO_STRUCT(r);
+
+ dcerpc_binding_handle_auth_info(b, &auth_type, &auth_level);
+
+ /* Encrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_BACKUP_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = plaintext.data;
+ r.in.data_in_len = plaintext.length;
+ r.in.param = 0;
+ r.out.data_out = &encrypted.data;
+ r.out.data_out_len = &enclen;
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "encrypt");
+ } else {
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ NT_STATUS_ACCESS_DENIED,
+ "encrypt");
+ return true;
+ }
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "encrypt");
+ encrypted.length = *r.out.data_out_len;
+
+ ndr_err = ndr_pull_struct_blob(&encrypted, tctx, &server_side_wrapped,
+ (ndr_pull_flags_fn_t)ndr_pull_bkrp_server_side_wrapped);
+ torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "pull of server_side_wrapped");
+
+ torture_assert_int_equal(tctx, server_side_wrapped.payload_length, plaintext.length,
+ "wrong payload length");
+
+ switch (wrong) {
+ case WRONG_MAGIC:
+ /* Change the magic. Forced by our NDR layer, so do it raw */
+ SIVAL(encrypted.data, 0, 78); /* valid values are 1-3 */
+ break;
+ case WRONG_R2:
+ server_side_wrapped.r2[0] = 78;
+ server_side_wrapped.r2[1] = 78;
+ server_side_wrapped.r2[3] = 78;
+ repush = true;
+ break;
+ case WRONG_PAYLOAD_LENGTH:
+ server_side_wrapped.payload_length = UINT32_MAX - 8;
+ repush = true;
+ break;
+ case WRONG_CIPHERTEXT_LENGTH:
+ /*
+ * Change the ciphertext len. We can't push this if
+ * we have it wrong, so do it raw
+ */
+ SIVAL(encrypted.data, 8, UINT32_MAX - 8); /* valid values are 1-3 */
+ break;
+ case SHORT_PAYLOAD_LENGTH:
+ server_side_wrapped.payload_length = server_side_wrapped.payload_length - 8;
+ repush = true;
+ break;
+ case SHORT_CIPHERTEXT_LENGTH:
+ /*
+ * Change the ciphertext len. We can't push this if
+ * we have it wrong, so do it raw
+ */
+ SIVAL(encrypted.data, 8, server_side_wrapped.ciphertext_length - 8); /* valid values are 1-3 */
+ break;
+ case ZERO_PAYLOAD_LENGTH:
+ server_side_wrapped.payload_length = 0;
+ repush = true;
+ break;
+ case ZERO_CIPHERTEXT_LENGTH:
+ /*
+ * Change the ciphertext len. We can't push this if
+ * we have it wrong, so do it raw
+ */
+ SIVAL(encrypted.data, 8, 0); /* valid values are 1-3 */
+ break;
+
+ case RIGHT_KEY:
+ case WRONG_KEY:
+ case WRONG_SID:
+ torture_assert(tctx,
+ test_ServerWrap_encrypt_decrypt_manual(tctx, &server_side_wrapped, wrong),
+ "test_ServerWrap_encrypt_decrypt_manual failed");
+ repush = true;
+ break;
+ }
+
+ if (repush) {
+ ndr_err = ndr_push_struct_blob(&encrypted, tctx, &server_side_wrapped,
+ (ndr_push_flags_fn_t)ndr_push_bkrp_server_side_wrapped);
+ torture_assert_ndr_err_equal(tctx, ndr_err, NDR_ERR_SUCCESS, "push of server_side_wrapped");
+ }
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = encrypted.data;
+ r.in.data_in_len = encrypted.length;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+
+ if ((wrong == WRONG_R2 || wrong == WRONG_KEY)
+ && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) {
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_SID,
+ "decrypt should fail with WERR_INVALID_SID or WERR_INVALID_PARAMETER");
+ } else if (wrong == RIGHT_KEY) {
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_OK,
+ "decrypt should succeed!");
+ } else if (wrong == WRONG_SID) {
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_ACCESS,
+ "decrypt should fail with WERR_INVALID_ACCESS");
+ } else {
+ if (!W_ERROR_EQUAL(r.out.result, WERR_INVALID_ACCESS)
+ && !W_ERROR_EQUAL(r.out.result, WERR_INVALID_PARAMETER)) {
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_INVALID_DATA,
+ "decrypt should fail with WERR_INVALID_ACCESS, WERR_INVALID_PARAMETER or WERR_INVALID_DATA");
+ }
+ }
+
+ /* Decrypt */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(BACKUPKEY_RESTORE_GUID_WIN2K, &guid),
+ "obtain GUID");
+
+ r.in.guidActionAgent = &guid;
+ r.in.data_in = encrypted.data;
+ r.in.data_in_len = encrypted.length;
+ r.in.param = 0;
+ r.out.data_out = &(decrypted.data);
+ r.out.data_out_len = &declen;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_bkrp_BackupKey_r(b, tctx, &r),
+ "decrypt");
+
+ if ((wrong == WRONG_R2 || wrong == WRONG_KEY)
+ && W_ERROR_EQUAL(r.out.result, WERR_INVALID_SID)) {
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_SID,
+ "decrypt should fail with WERR_INVALID_SID or WERR_INVALID_PARAMETER");
+ } else if (wrong == RIGHT_KEY) {
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_OK,
+ "decrypt should succeed!");
+ } else if (wrong == WRONG_SID) {
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_ACCESS,
+ "decrypt should fail with WERR_INVALID_ACCESS");
+ } else {
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "decrypt should fail with WERR_INVALID_PARAMETER");
+ }
+
+ return true;
+}
+
+static bool test_ServerWrap_decrypt_wrong_magic(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_MAGIC);
+}
+
+static bool test_ServerWrap_decrypt_wrong_r2(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_R2);
+}
+
+static bool test_ServerWrap_decrypt_wrong_payload_length(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_PAYLOAD_LENGTH);
+}
+
+static bool test_ServerWrap_decrypt_short_payload_length(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, SHORT_PAYLOAD_LENGTH);
+}
+
+static bool test_ServerWrap_decrypt_zero_payload_length(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, ZERO_PAYLOAD_LENGTH);
+}
+
+static bool test_ServerWrap_decrypt_wrong_ciphertext_length(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_CIPHERTEXT_LENGTH);
+}
+
+static bool test_ServerWrap_decrypt_short_ciphertext_length(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, SHORT_CIPHERTEXT_LENGTH);
+}
+
+static bool test_ServerWrap_decrypt_zero_ciphertext_length(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, ZERO_CIPHERTEXT_LENGTH);
+}
+
+static bool test_ServerWrap_encrypt_decrypt_remote_key(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, RIGHT_KEY);
+}
+
+static bool test_ServerWrap_encrypt_decrypt_wrong_key(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_KEY);
+}
+
+static bool test_ServerWrap_encrypt_decrypt_wrong_sid(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_ServerWrap_decrypt_wrong_stuff(tctx, p, WRONG_SID);
+}
+
+struct torture_suite *torture_rpc_backupkey(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "backupkey");
+
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "backupkey",
+ &ndr_table_backupkey);
+
+ torture_rpc_tcase_add_test(tcase, "retreive_backup_key_guid",
+ test_RetrieveBackupKeyGUID);
+
+ torture_rpc_tcase_add_test(tcase, "restore_guid",
+ test_RestoreGUID);
+
+ torture_rpc_tcase_add_test(tcase, "restore_guid version 3",
+ test_RestoreGUID_v3);
+
+/* We double the test in order to be sure that we don't mess stuff (ie. freeing static stuff) */
+
+ torture_rpc_tcase_add_test(tcase, "restore_guid_2nd",
+ test_RestoreGUID);
+
+ torture_rpc_tcase_add_test(tcase, "unable_to_decrypt_secret",
+ test_RestoreGUID_ko);
+
+ torture_rpc_tcase_add_test(tcase, "wrong_user_restore_guid",
+ test_RestoreGUID_wronguser);
+
+ torture_rpc_tcase_add_test(tcase, "wrong_version_restore_guid",
+ test_RestoreGUID_wrongversion);
+
+ torture_rpc_tcase_add_test(tcase, "bad_magic_on_secret_restore_guid",
+ test_RestoreGUID_badmagiconsecret);
+
+ torture_rpc_tcase_add_test(tcase, "bad_hash_on_secret_restore_guid",
+ test_RestoreGUID_badhashaccesscheck);
+
+ torture_rpc_tcase_add_test(tcase, "bad_magic_on_accesscheck_restore_guid",
+ test_RestoreGUID_badmagicaccesscheck);
+
+ torture_rpc_tcase_add_test(tcase, "bad_cert_guid_restore_guid",
+ test_RestoreGUID_badcertguid);
+
+ torture_rpc_tcase_add_test(tcase, "empty_request_restore_guid",
+ test_RestoreGUID_emptyrequest);
+
+ torture_rpc_tcase_add_test(tcase, "retreive_backup_key_guid_validate",
+ test_RetrieveBackupKeyGUID_validate);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt",
+ test_ServerWrap_encrypt_decrypt);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_keyGUID",
+ test_ServerWrap_decrypt_wrong_keyGUID);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_empty_request",
+ test_ServerWrap_decrypt_empty_request);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_short_request",
+ test_ServerWrap_decrypt_short_request);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_magic",
+ test_ServerWrap_decrypt_wrong_magic);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_r2",
+ test_ServerWrap_decrypt_wrong_r2);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_payload_length",
+ test_ServerWrap_decrypt_wrong_payload_length);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_short_payload_length",
+ test_ServerWrap_decrypt_short_payload_length);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_zero_payload_length",
+ test_ServerWrap_decrypt_zero_payload_length);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_wrong_ciphertext_length",
+ test_ServerWrap_decrypt_wrong_ciphertext_length);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_short_ciphertext_length",
+ test_ServerWrap_decrypt_short_ciphertext_length);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_decrypt_zero_ciphertext_length",
+ test_ServerWrap_decrypt_zero_ciphertext_length);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt_remote_key",
+ test_ServerWrap_encrypt_decrypt_remote_key);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt_wrong_key",
+ test_ServerWrap_encrypt_decrypt_wrong_key);
+
+ torture_rpc_tcase_add_test(tcase, "server_wrap_encrypt_decrypt_wrong_sid",
+ test_ServerWrap_encrypt_decrypt_wrong_sid);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/bench.c b/source4/torture/rpc/bench.c
new file mode 100644
index 0000000..88825c5
--- /dev/null
+++ b/source4/torture/rpc/bench.c
@@ -0,0 +1,152 @@
+/*
+ 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 "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "torture/rpc/torture_rpc.h"
+
+/**************************/
+/* srvsvc_NetShare */
+/**************************/
+static bool test_NetShareEnumAll(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareEnumAll r;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr0 c0;
+ struct srvsvc_NetShareCtr1 c1;
+ struct srvsvc_NetShareCtr2 c2;
+ struct srvsvc_NetShareCtr501 c501;
+ struct srvsvc_NetShareCtr502 c502;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0, 1, 2, 501, 502};
+ int i;
+ bool ret = true;
+ uint32_t resume_handle;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(info_ctr);
+
+ r.in.server_unc = talloc_asprintf(mem_ctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = &resume_handle;
+ r.out.resume_handle = &resume_handle;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ resume_handle = 0;
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 501:
+ ZERO_STRUCT(c501);
+ info_ctr.ctr.ctr501 = &c501;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ }
+
+ status = dcerpc_srvsvc_NetShareEnumAll_r(b, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("NetShareEnumAll level %u failed - %s\n", info_ctr.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ printf("NetShareEnumAll level %u failed - %s\n", info_ctr.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 0000000..7c0d5e4
--- /dev/null
+++ b/source4/torture/rpc/bind.c
@@ -0,0 +1,245 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for rpc bind operations
+
+ Copyright (C) Guenther Deschner 2010
+
+ 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/rpc/torture_rpc.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "lib/cmdline/cmdline.h"
+
+static bool test_openpolicy(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle *handle;
+
+ torture_assert(tctx,
+ test_lsa_OpenPolicy2(b, tctx, &handle),
+ "failed to open policy");
+
+ torture_assert(tctx,
+ test_lsa_Close(b, tctx, handle),
+ "failed to close policy");
+
+ return true;
+}
+
+static bool test_bind(struct torture_context *tctx,
+ const void *private_data)
+{
+ struct dcerpc_binding *binding;
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ const uint32_t *flags = (const uint32_t *)private_data;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_binding(tctx, &binding),
+ "failed to parse binding string");
+
+ status = dcerpc_binding_set_flags(binding, *flags, DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(tctx, status, "set flags");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p, binding,
+ &ndr_table_lsarpc,
+ samba_cmdline_get_creds(),
+ tctx->ev,
+ tctx->lp_ctx),
+ "failed to connect pipe");
+
+ torture_assert(tctx,
+ test_openpolicy(tctx, p),
+ "failed to test openpolicy");
+
+ talloc_free(p);
+
+ return true;
+}
+
+/**
+ * Verifies a handle created in a connection is available on
+ * a second connection when the same association group is
+ * requested in the bind operation. The LSA interface can't be
+ * used because it runs in preforking mode in the selftests.
+ * Association groups should work when binding to interfaces
+ * running in the same process.
+ */
+static bool test_assoc_group_handles_external(struct torture_context *tctx,
+ const void *private_data)
+{
+ struct dcerpc_binding *binding1 = NULL;
+ struct dcerpc_binding *binding2 = NULL;
+ struct dcerpc_pipe *p1 = NULL;
+ struct dcerpc_pipe *p2 = NULL;
+ struct epm_Lookup r;
+ struct epm_LookupHandleFree f;
+ struct policy_handle handle;
+ uint32_t assoc_group_id;
+ uint32_t num_ents = 0;
+
+ ZERO_STRUCT(handle);
+
+ /* Open first pipe and open a policy handle */
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_binding(tctx, &binding1),
+ "failed to parse binding string");
+ dcerpc_binding_set_transport(binding1, NCACN_IP_TCP);
+ dcerpc_binding_set_string_option(binding1, "endpoint", "135");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p1, binding1,
+ &ndr_table_epmapper,
+ samba_cmdline_get_creds(),
+ tctx->ev,
+ tctx->lp_ctx),
+ "failed to connect first pipe");
+
+ r.in.inquiry_type = RPC_C_EP_ALL_ELTS;
+ r.in.object = NULL;
+ r.in.interface_id = NULL;
+ r.in.vers_option = RPC_C_VERS_ALL;
+
+ r.in.entry_handle = &handle;
+ r.in.max_ents = 1;
+
+ r.out.entry_handle = &handle;
+ r.out.num_ents = &num_ents;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_epm_Lookup_r(p1->binding_handle, tctx, &r),
+ "failed EPM Lookup");
+ torture_assert_int_equal(tctx,
+ r.out.result,
+ EPMAPPER_STATUS_OK,
+ "failed EPM Lookup");
+
+ /* Open second pipe, different association group. Handle not found */
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_binding(tctx, &binding2),
+ "failed to parse binding string");
+ dcerpc_binding_set_transport(binding2, NCACN_IP_TCP);
+ dcerpc_binding_set_string_option(binding2, "endpoint", "135");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p2, binding2,
+ &ndr_table_epmapper,
+ samba_cmdline_get_creds(),
+ tctx->ev,
+ tctx->lp_ctx),
+ "failed to connect second pipe");
+
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_epm_Lookup_r(p2->binding_handle, tctx, &r),
+ NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "Unexpected EPM Lookup success");
+
+ /* Open second pipe, same association group. Handle is found */
+ assoc_group_id = dcerpc_binding_get_assoc_group_id(p1->binding);
+ dcerpc_binding_set_assoc_group_id(binding2, assoc_group_id);
+
+ TALLOC_FREE(p2);
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p2, binding2,
+ &ndr_table_epmapper,
+ samba_cmdline_get_creds(),
+ tctx->ev,
+ tctx->lp_ctx),
+ "failed to connect second pipe");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_epm_Lookup_r(p2->binding_handle, tctx, &r),
+ "failed EPM Lookup");
+
+ torture_assert_int_equal(tctx,
+ r.out.result,
+ EPMAPPER_STATUS_OK,
+ "failed EPM Lookup");
+
+ /* Cleanup */
+ f.in.entry_handle = &handle;
+ f.out.entry_handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_epm_LookupHandleFree_r(p1->binding_handle, tctx, &f),
+ "failed EPM LookupHandleFree");
+
+ torture_assert_int_equal(tctx,
+ r.out.result,
+ EPMAPPER_STATUS_OK,
+ "failed EPM LookupHandleFree");
+
+ TALLOC_FREE(p1);
+ TALLOC_FREE(p2);
+ TALLOC_FREE(binding2);
+ TALLOC_FREE(binding1);
+
+ return true;
+}
+
+static void test_bind_op(struct torture_suite *suite,
+ const char *name,
+ uint32_t flags)
+{
+ uint32_t *flags_p = talloc(suite, uint32_t);
+
+ *flags_p = flags;
+
+ torture_suite_add_simple_tcase_const(suite, name, test_bind, flags_p);
+}
+
+
+struct torture_suite *torture_rpc_bind(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "bind");
+ struct {
+ const char *test_name;
+ uint32_t flags;
+ } tests[] = {
+ {
+ .test_name = "ntlm,sign",
+ .flags = DCERPC_AUTH_NTLM | DCERPC_SIGN
+ },{
+ .test_name = "ntlm,sign,seal",
+ .flags = DCERPC_AUTH_NTLM | DCERPC_SIGN | DCERPC_SEAL
+ },{
+ .test_name = "spnego,sign",
+ .flags = DCERPC_AUTH_SPNEGO | DCERPC_SIGN
+ },{
+ .test_name = "spnego,sign,seal",
+ .flags = DCERPC_AUTH_SPNEGO | DCERPC_SIGN | DCERPC_SEAL
+ }
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+ test_bind_op(suite, tests[i].test_name, tests[i].flags);
+ }
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+ test_bind_op(suite, talloc_asprintf(suite, "bigendian,%s", tests[i].test_name), tests[i].flags | DCERPC_PUSH_BIGENDIAN);
+ }
+
+ torture_suite_add_simple_tcase_const(suite,
+ "assoc_group_handles_external",
+ test_assoc_group_handles_external,
+ NULL);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/browser.c b/source4/torture/rpc/browser.c
new file mode 100644
index 0000000..2fbcd4e
--- /dev/null
+++ b/source4/torture/rpc/browser.c
@@ -0,0 +1,124 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for browser rpc operations
+
+ Copyright (C) Stefan Metzmacher 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 "librpc/gen_ndr/ndr_browser_c.h"
+#include "torture/rpc/torture_rpc.h"
+
+bool test_BrowserrQueryOtherDomains(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct BrowserrQueryOtherDomains r;
+ struct BrowserrSrvInfo info;
+ struct BrowserrSrvInfo100Ctr ctr100;
+ struct srvsvc_NetSrvInfo100 entries100[1];
+ struct BrowserrSrvInfo101Ctr ctr101;
+ struct srvsvc_NetSrvInfo101 entries101[1];
+ uint32_t total_entries;
+ NTSTATUS status;
+
+ torture_comment(tctx, "dcerpc_BrowserrQueryOtherDomains\n");
+
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(info);
+ ZERO_STRUCT(ctr100);
+ ZERO_STRUCT(entries100);
+ ZERO_STRUCT(ctr101);
+ ZERO_STRUCT(entries101);
+ total_entries = 0;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.info = &info;
+ r.out.info = &info;
+ r.out.total_entries = &total_entries;
+
+ info.level = 100;
+ info.info.info100 = &ctr100;
+
+ status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_ok(tctx, r.out.result, "BrowserrQueryOtherDomains failed");
+ torture_assert_int_equal(tctx, *r.out.total_entries, 0, "BrowserrQueryOtherDomains");
+
+ info.info.info100 = &ctr100;
+ ctr100.entries_read = ARRAY_SIZE(entries100);
+ ctr100.entries = entries100;
+
+ status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_ok(tctx, r.out.result, "BrowserrQueryOtherDomains failed");
+ torture_assert_int_equal(tctx, *r.out.total_entries, 0, "BrowserrQueryOtherDomains");
+
+ info.info.info100 = NULL;
+ status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_INVALID_PARAMETER, r.out.result,
+ "BrowserrQueryOtherDomains failed");
+
+ info.level = 101;
+ info.info.info101 = &ctr101;
+
+ status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result,
+ "BrowserrQueryOtherDomains");
+
+ info.info.info101 = &ctr101;
+ ctr101.entries_read = ARRAY_SIZE(entries101);
+ ctr101.entries = entries101;
+
+ status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result,
+ "BrowserrQueryOtherDomains");
+
+ info.info.info101 = NULL;
+ status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result,
+ "BrowserrQueryOtherDomains");
+
+ info.level = 102;
+ status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result,
+ "BrowserrQueryOtherDomains");
+
+ info.level = 0;
+ status = dcerpc_BrowserrQueryOtherDomains_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "BrowserrQueryOtherDomains failed");
+ torture_assert_werr_equal(tctx, WERR_INVALID_LEVEL, r.out.result,
+ "BrowserrQueryOtherDomains");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_browser(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "browser");
+ struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite, "browser", &ndr_table_browser);
+
+ torture_rpc_tcase_add_test(tcase, "BrowserrQueryOtherDomains", test_BrowserrQueryOtherDomains);
+
+ return suite;
+}
+
diff --git a/source4/torture/rpc/clusapi.c b/source4/torture/rpc/clusapi.c
new file mode 100644
index 0000000..99a272d
--- /dev/null
+++ b/source4/torture/rpc/clusapi.c
@@ -0,0 +1,4185 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for clusapi rpc operations
+
+ Copyright (C) Günther Deschner 2015
+
+ 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_clusapi_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+#include "libcli/registry/util_reg.h"
+
+struct torture_clusapi_context {
+ struct dcerpc_pipe *p;
+ const char *NodeName;
+ const char *ClusterName;
+ uint16_t lpwMajorVersion;
+ uint16_t lpwMinorVersion;
+ uint16_t lpwBuildNumber;
+};
+
+static bool test_OpenCluster_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Cluster)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenCluster r;
+ WERROR Status;
+
+ r.out.Status = &Status;
+ r.out.Cluster = Cluster;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenCluster_r(b, tctx, &r),
+ "OpenCluster failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenCluster failed");
+
+ return true;
+}
+
+static bool test_OpenClusterEx_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Cluster)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenClusterEx r;
+ uint32_t lpdwGrantedAccess;
+ WERROR Status;
+
+ r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.lpdwGrantedAccess = &lpdwGrantedAccess;
+ r.out.Status = &Status;
+ r.out.hCluster = Cluster;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenClusterEx_r(b, tctx, &r),
+ "OpenClusterEx failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenClusterEx failed");
+
+ return true;
+}
+
+static bool test_CloseCluster_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Cluster)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CloseCluster r;
+
+ r.in.Cluster = Cluster;
+ r.out.Cluster = Cluster;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CloseCluster_r(b, tctx, &r),
+ "CloseCluster failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CloseCluster failed");
+
+ torture_assert(tctx,
+ ndr_policy_handle_empty(Cluster),
+ "policy_handle non empty after CloseCluster");
+
+ return true;
+}
+
+static bool test_OpenCluster(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle Cluster;
+
+ if (!test_OpenCluster_int(tctx, t->p, &Cluster)) {
+ return false;
+ }
+
+ test_CloseCluster_int(tctx, t->p, &Cluster);
+
+ return true;
+}
+
+static bool test_OpenClusterEx(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle Cluster;
+
+ if (!test_OpenClusterEx_int(tctx, t->p, &Cluster)) {
+ return false;
+ }
+
+ test_CloseCluster_int(tctx, t->p, &Cluster);
+
+ return true;
+}
+
+static bool test_CloseCluster(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle Cluster;
+
+ if (!test_OpenCluster_int(tctx, t->p, &Cluster)) {
+ return false;
+ }
+
+ return test_CloseCluster_int(tctx, t->p, &Cluster);
+}
+
+static bool test_GetClusterName_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char **ClusterName)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetClusterName r;
+ const char *NodeName;
+
+ r.out.ClusterName = ClusterName;
+ r.out.NodeName = &NodeName;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetClusterName_r(b, tctx, &r),
+ "GetClusterName failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetClusterName failed");
+
+ return true;
+}
+
+static bool test_SetClusterName(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_SetClusterName r;
+ const char *NewClusterName;
+ WERROR rpc_status;
+
+ torture_assert(tctx,
+ test_GetClusterName_int(tctx, t->p, &NewClusterName),
+ "failed to query old ClusterName");
+
+ r.in.NewClusterName = NewClusterName;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_SetClusterName_r(b, tctx, &r),
+ "SetClusterName failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_RESOURCE_PROPERTIES_STORED,
+ "SetClusterName failed");
+
+ return true;
+}
+
+static bool test_GetClusterName(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ const char *ClusterName;
+
+ return test_GetClusterName_int(tctx, t->p, &ClusterName);
+}
+
+static bool test_GetClusterVersion(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_GetClusterVersion r;
+ uint16_t lpwMajorVersion;
+ uint16_t lpwMinorVersion;
+ uint16_t lpwBuildNumber;
+ const char *lpszVendorId;
+ const char *lpszCSDVersion;
+
+ r.out.lpwMajorVersion = &lpwMajorVersion;
+ r.out.lpwMinorVersion = &lpwMinorVersion;
+ r.out.lpwBuildNumber = &lpwBuildNumber;
+ r.out.lpszVendorId = &lpszVendorId;
+ r.out.lpszCSDVersion = &lpszCSDVersion;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetClusterVersion_r(b, tctx, &r),
+ "GetClusterVersion failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_CALL_NOT_IMPLEMENTED,
+ "GetClusterVersion failed");
+
+ return true;
+}
+
+static bool test_GetClusterVersion2(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_GetClusterVersion2 r;
+ uint16_t lpwMajorVersion;
+ uint16_t lpwMinorVersion;
+ uint16_t lpwBuildNumber;
+ const char *lpszVendorId;
+ const char *lpszCSDVersion;
+ struct CLUSTER_OPERATIONAL_VERSION_INFO *ppClusterOpVerInfo;
+ WERROR rpc_status;
+
+ r.out.lpwMajorVersion = &lpwMajorVersion;
+ r.out.lpwMinorVersion = &lpwMinorVersion;
+ r.out.lpwBuildNumber = &lpwBuildNumber;
+ r.out.lpszVendorId = &lpszVendorId;
+ r.out.lpszCSDVersion = &lpszCSDVersion;
+ r.out.ppClusterOpVerInfo = &ppClusterOpVerInfo;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetClusterVersion2_r(b, tctx, &r),
+ "GetClusterVersion2 failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetClusterVersion2 failed");
+
+ return true;
+}
+
+static bool test_CreateEnum(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_CreateEnum r;
+ uint32_t dwType[] = {
+ CLUSTER_ENUM_NODE,
+ CLUSTER_ENUM_RESTYPE,
+ CLUSTER_ENUM_RESOURCE,
+ CLUSTER_ENUM_GROUP,
+ CLUSTER_ENUM_NETWORK,
+ CLUSTER_ENUM_NETINTERFACE,
+ CLUSTER_ENUM_INTERNAL_NETWORK,
+ CLUSTER_ENUM_SHARED_VOLUME_RESOURCE
+ };
+ uint32_t dwType_invalid[] = {
+ 0x00000040,
+ 0x00000080,
+ 0x00000100 /* and many more ... */
+ };
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(dwType); i++) {
+
+ r.in.dwType = dwType[i];
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnum_r(b, tctx, &r),
+ "CreateEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateEnum failed");
+ }
+
+ for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) {
+
+ r.in.dwType = dwType_invalid[i];
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnum_r(b, tctx, &r),
+ "CreateEnum failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "CreateEnum failed");
+ }
+
+ return true;
+}
+
+static bool test_CreateEnumEx_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Cluster)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CreateEnumEx r;
+ uint32_t dwType[] = {
+ CLUSTER_ENUM_NODE,
+ CLUSTER_ENUM_RESTYPE,
+ CLUSTER_ENUM_RESOURCE,
+ CLUSTER_ENUM_GROUP,
+ CLUSTER_ENUM_NETWORK,
+ CLUSTER_ENUM_NETINTERFACE,
+ CLUSTER_ENUM_INTERNAL_NETWORK,
+ CLUSTER_ENUM_SHARED_VOLUME_RESOURCE
+ };
+ uint32_t dwType_invalid[] = {
+ 0x00000040,
+ 0x00000080,
+ 0x00000100 /* and many more ... */
+ };
+ struct ENUM_LIST *ReturnIdEnum;
+ struct ENUM_LIST *ReturnNameEnum;
+ WERROR rpc_status;
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(dwType); i++) {
+
+ r.in.hCluster = *Cluster;
+ r.in.dwType = dwType[i];
+ r.in.dwOptions = 0;
+ r.out.ReturnIdEnum = &ReturnIdEnum;
+ r.out.ReturnNameEnum = &ReturnNameEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnumEx_r(b, tctx, &r),
+ "CreateEnumEx failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateEnumEx failed");
+ }
+
+ for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) {
+
+ r.in.hCluster = *Cluster;
+ r.in.dwType = dwType_invalid[i];
+ r.in.dwOptions = 0;
+ r.out.ReturnIdEnum = &ReturnIdEnum;
+ r.out.ReturnNameEnum = &ReturnNameEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnumEx_r(b, tctx, &r),
+ "CreateEnumEx failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "CreateEnumEx failed");
+ }
+
+ return true;
+}
+
+static bool test_CreateEnumEx(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle Cluster;
+ bool ret;
+
+ if (!test_OpenCluster_int(tctx, t->p, &Cluster)) {
+ return false;
+ }
+
+ ret = test_CreateEnumEx_int(tctx, t->p, &Cluster);
+
+ test_CloseCluster_int(tctx, t->p, &Cluster);
+
+ return ret;
+}
+
+
+static bool test_GetQuorumResource(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_GetQuorumResource r;
+ const char *lpszResourceName;
+ const char *lpszDeviceName;
+ uint32_t pdwMaxQuorumLogSize;
+ WERROR rpc_status;
+
+ r.out.lpszResourceName = &lpszResourceName;
+ r.out.lpszDeviceName = &lpszDeviceName;
+ r.out.pdwMaxQuorumLogSize = &pdwMaxQuorumLogSize;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetQuorumResource_r(b, tctx, &r),
+ "GetQuorumResource failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetQuorumResource failed");
+
+ return true;
+}
+
+static bool test_SetQuorumResource(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_SetQuorumResource r;
+ const char *lpszDeviceName = "";
+ uint32_t dwMaxQuorumLogSize = 0;
+ WERROR rpc_status;
+ struct policy_handle hResource;
+
+ /* we need to figure out how this call works and what we provide as
+ devicename and resource handle - gd
+ */
+
+ torture_skip(tctx, "skipping SetQuorumResource test");
+
+ ZERO_STRUCT(hResource);
+
+ r.in.hResource = hResource;
+ r.in.lpszDeviceName = lpszDeviceName;
+ r.in.dwMaxQuorumLogSize = dwMaxQuorumLogSize;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_SetQuorumResource_r(b, tctx, &r),
+ "SetQuorumResource failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "SetQuorumResource failed");
+
+ return true;
+}
+
+static bool test_OpenResource_int_exp(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszResourceName,
+ struct policy_handle *hResource,
+ WERROR expected_Status,
+ WERROR expected_rpc_status)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenResource r;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszResourceName = lpszResourceName;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hResource = hResource;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenResource_r(b, tctx, &r),
+ "OpenResource failed");
+ torture_assert_werr_equal(tctx,
+ *r.out.Status, expected_Status,
+ "OpenResource failed");
+ torture_assert_werr_equal(tctx,
+ *r.out.rpc_status, expected_rpc_status,
+ "OpenResource failed");
+
+ return true;
+}
+
+bool test_OpenResource_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszResourceName,
+ struct policy_handle *hResource)
+{
+ return test_OpenResource_int_exp(tctx, p,
+ lpszResourceName,
+ hResource,
+ WERR_OK, WERR_OK);
+}
+
+static bool test_OpenResourceEx_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszResourceName,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenResourceEx r;
+ uint32_t lpdwGrantedAccess;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszResourceName = lpszResourceName;
+ r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.lpdwGrantedAccess = &lpdwGrantedAccess;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hResource = hResource;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenResourceEx_r(b, tctx, &r),
+ "OpenResourceEx failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenResourceEx failed");
+
+ return true;
+}
+
+bool test_CloseResource_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CloseResource r;
+
+ r.in.Resource = hResource;
+ r.out.Resource = hResource;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CloseResource_r(b, tctx, &r),
+ "CloseResource failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CloseResource failed");
+ torture_assert(tctx,
+ ndr_policy_handle_empty(hResource),
+ "policy_handle non empty after CloseResource");
+
+ return true;
+}
+
+static bool test_OpenResource(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ if (!test_OpenResource_int_exp(tctx, t->p, "", &hResource, WERR_RESOURCE_NOT_FOUND, WERR_OK)) {
+ return false;
+ }
+
+ torture_assert(tctx,
+ ndr_policy_handle_empty(&hResource),
+ "expected empty policy handle");
+
+ if (!test_OpenResource_int_exp(tctx, t->p, "jfUF38fjSNcfn", &hResource, WERR_RESOURCE_NOT_FOUND, WERR_OK)) {
+ return false;
+ }
+
+ torture_assert(tctx,
+ ndr_policy_handle_empty(&hResource),
+ "expected empty policy handle");
+
+ return true;
+}
+
+static bool test_OpenResourceEx(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+
+ if (!test_OpenResourceEx_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return true;
+}
+
+
+static bool test_CloseResource(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ return test_CloseResource_int(tctx, t->p, &hResource);
+}
+
+static bool test_OpenGroup_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszGroupName,
+ struct policy_handle *hGroup);
+static bool test_CloseGroup_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Group);
+
+static bool test_CreateResource_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CreateResource r;
+ const char *lpszResourceName = "wurst";
+ const char *lpszResourceType = "Generic Service";
+ WERROR Status;
+ WERROR rpc_status;
+ struct policy_handle hGroup;
+
+ torture_assert(tctx,
+ test_OpenGroup_int(tctx, p, "Cluster Group", &hGroup),
+ "failed to open group");
+
+ r.in.hGroup = hGroup;
+ r.in.lpszResourceName = lpszResourceName;
+ r.in.lpszResourceType = lpszResourceType;
+ r.in.dwFlags = CLUSTER_RESOURCE_DEFAULT_MONITOR;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hResource = hResource;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateResource_r(b, tctx, &r),
+ "CreateResource failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "CreateResource failed");
+
+ test_CloseGroup_int(tctx, p, &hGroup);
+
+ return true;
+}
+
+static bool test_DeleteResource_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_DeleteResource r;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_DeleteResource_r(b, tctx, &r),
+ "DeleteResource failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "DeleteResource failed");
+
+ return true;
+}
+
+static bool test_CreateResource(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+
+ if (!test_CreateResource_int(tctx, t->p, &hResource)) {
+ return false;
+ }
+
+ test_DeleteResource_int(tctx, t->p, &hResource);
+
+ return true;
+}
+
+static bool test_DeleteResource(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+
+ if (!test_CreateResource_int(tctx, t->p, &hResource)) {
+ return false;
+ }
+
+ return test_DeleteResource_int(tctx, t->p, &hResource);
+}
+
+static bool test_SetResourceName_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_SetResourceName r;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.in.lpszResourceName = "wurst";
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_SetResourceName_r(b, tctx, &r),
+ "SetResourceName failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "SetResourceName failed");
+
+ return true;
+}
+
+static bool test_SetResourceName(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_CreateResource_int(tctx, t->p, &hResource)) {
+ return false;
+ }
+
+ ret = test_SetResourceName_int(tctx, t->p, &hResource);
+
+ test_DeleteResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+static bool test_GetResourceState_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetResourceState r;
+ enum clusapi_ClusterResourceState State;
+ const char *NodeName;
+ const char *GroupName;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.State = &State;
+ r.out.NodeName = &NodeName;
+ r.out.GroupName = &GroupName;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetResourceState_r(b, tctx, &r),
+ "GetResourceState failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetResourceState failed");
+
+ return true;
+}
+
+static bool test_GetResourceState(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ ret = test_GetResourceState_int(tctx, t->p, &hResource);
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+static bool test_GetResourceId_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetResourceId r;
+ const char *pGuid;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.pGuid = &pGuid;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetResourceId_r(b, tctx, &r),
+ "GetResourceId failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetResourceId failed");
+
+ return true;
+}
+
+static bool test_GetResourceId(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ ret = test_GetResourceId_int(tctx, t->p, &hResource);
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+static bool test_GetResourceType_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetResourceType r;
+ const char *lpszResourceType;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.lpszResourceType = &lpszResourceType;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetResourceType_r(b, tctx, &r),
+ "GetResourceType failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetResourceType failed");
+
+ return true;
+}
+
+static bool test_GetResourceType(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ ret = test_GetResourceType_int(tctx, t->p, &hResource);
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+static bool test_FailResource_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_FailResource r;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_FailResource_r(b, tctx, &r),
+ "FailResource failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "FailResource failed");
+
+ return true;
+}
+
+static bool test_FailResource(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ ret = test_FailResource_int(tctx, t->p, &hResource);
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+bool test_OnlineResource_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OnlineResource r;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OnlineResource_r(b, tctx, &r),
+ "OnlineResource failed");
+ if (!W_ERROR_IS_OK(r.out.result) &&
+ !W_ERROR_EQUAL(r.out.result, WERR_IO_PENDING)) {
+ torture_result(tctx, TORTURE_FAIL,
+ "OnlineResource failed with %s",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_OnlineResource(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ ret = test_OnlineResource_int(tctx, t->p, &hResource);
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+bool test_OfflineResource_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OfflineResource r;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OfflineResource_r(b, tctx, &r),
+ "OfflineResource failed");
+ if (!W_ERROR_IS_OK(r.out.result) &&
+ !W_ERROR_EQUAL(r.out.result, WERR_IO_PENDING)) {
+ torture_result(tctx, TORTURE_FAIL,
+ "OfflineResource failed with %s",
+ win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_OfflineResource(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ ret = test_OfflineResource_int(tctx, t->p, &hResource);
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+static bool test_CreateResEnum_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CreateResEnum r;
+ uint32_t dwType = CLUSTER_ENUM_RESOURCE;
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.in.dwType = dwType;
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateResEnum_r(b, tctx, &r),
+ "CreateResEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateResEnum failed");
+
+ return true;
+}
+
+static bool test_CreateResEnum(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ ret = test_CreateResEnum_int(tctx, t->p, &hResource);
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+static bool test_GetResourceDependencyExpression_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetResourceDependencyExpression r;
+ const char *lpszDependencyExpression;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.lpszDependencyExpression = &lpszDependencyExpression;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetResourceDependencyExpression_r(b, tctx, &r),
+ "GetResourceDependencyExpression failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetResourceDependencyExpression failed");
+
+ return true;
+}
+
+static bool test_GetResourceDependencyExpression(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_OpenResource_int(tctx, t->p, "Cluster Name", &hResource)) {
+ return false;
+ }
+
+ ret = test_GetResourceDependencyExpression_int(tctx, t->p, &hResource);
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+static bool test_GetResourceNetworkName_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetResourceNetworkName r;
+ const char *lpszName;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.lpszName = &lpszName;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetResourceNetworkName_r(b, tctx, &r),
+ "GetResourceNetworkName failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetResourceNetworkName failed");
+
+ return true;
+}
+
+static bool test_GetResourceNetworkName(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hResource;
+ bool ret = true;
+
+ if (!test_OpenResource_int(tctx, t->p, "Network Name", &hResource)) {
+ return false;
+ }
+
+ ret = test_GetResourceNetworkName_int(tctx, t->p, &hResource);
+
+ test_CloseResource_int(tctx, t->p, &hResource);
+
+ return ret;
+}
+
+static bool test_ResourceTypeControl_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Cluster,
+ const char *resource_type,
+ enum clusapi_ResourceTypeControlCode dwControlCode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_ResourceTypeControl r;
+ uint32_t lpBytesReturned;
+ uint32_t lpcbRequired;
+ WERROR rpc_status;
+
+ r.in.hCluster = *Cluster;
+ r.in.lpszResourceTypeName = resource_type;
+ r.in.dwControlCode = 0;
+ r.in.lpInBuffer = NULL;
+ r.in.nInBufferSize = 0;
+ r.in.nOutBufferSize = 0;
+ r.out.lpOutBuffer = NULL;
+ r.out.lpBytesReturned = &lpBytesReturned;
+ r.out.lpcbRequired = &lpcbRequired;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_ResourceTypeControl_r(b, tctx, &r),
+ "ResourceTypeControl failed");
+
+ if (strequal(r.in.lpszResourceTypeName, "MSMQ") ||
+ strequal(r.in.lpszResourceTypeName, "MSMQTriggers")) {
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_CLUSTER_RESTYPE_NOT_SUPPORTED,
+ "ResourceTypeControl failed");
+ return true;
+ }
+
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_FUNCTION,
+ "ResourceTypeControl failed");
+
+ r.in.dwControlCode = dwControlCode;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_ResourceTypeControl_r(b, tctx, &r),
+ "ResourceTypeControl failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, *r.out.lpcbRequired);
+ r.in.nOutBufferSize = *r.out.lpcbRequired;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_ResourceTypeControl_r(b, tctx, &r),
+ "ResourceTypeControl failed");
+ }
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "ResourceTypeControl failed");
+
+ /* now try what happens when we query with a buffer large enough to hold
+ * the entire packet */
+
+ r.in.nOutBufferSize = 0x4000;
+ r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, r.in.nOutBufferSize);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_ResourceTypeControl_r(b, tctx, &r),
+ "ResourceTypeControl failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "ResourceTypeControl failed");
+ torture_assert(tctx, *r.out.lpBytesReturned < r.in.nOutBufferSize,
+ "lpBytesReturned expected to be smaller than input size nOutBufferSize");
+
+ return true;
+}
+
+static bool test_ResourceTypeControl(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *resourcetype_name)
+{
+ struct policy_handle Cluster;
+ bool ret;
+ uint32_t control_codes[] = {
+ CLUSCTL_RESOURCE_TYPE_GET_CLASS_INFO,
+ CLUSCTL_RESOURCE_TYPE_GET_CHARACTERISTICS,
+ CLUSCTL_RESOURCE_TYPE_GET_COMMON_PROPERTIES,
+ CLUSCTL_RESOURCE_TYPE_GET_RO_COMMON_PROPERTIES,
+ CLUSCTL_RESOURCE_TYPE_GET_PRIVATE_PROPERTIES
+ };
+ int i;
+
+ if (!test_OpenCluster_int(tctx, p, &Cluster)) {
+ return false;
+ }
+
+ for (i=0; i < ARRAY_SIZE(control_codes); i++) {
+ ret = test_ResourceTypeControl_int(tctx, p, &Cluster,
+ resourcetype_name,
+ control_codes[i]);
+ if (!ret) {
+ goto done;
+ }
+ }
+
+ done:
+ test_CloseCluster_int(tctx, p, &Cluster);
+
+ return ret;
+}
+
+
+
+static bool test_one_resourcetype(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *resourcetype_name)
+{
+ torture_assert(tctx,
+ test_ResourceTypeControl(tctx, p, resourcetype_name),
+ "failed to query ResourceTypeControl");
+
+ return true;
+}
+
+static bool test_one_resource(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *resource_name)
+{
+ struct policy_handle hResource;
+
+ torture_assert(tctx,
+ test_OpenResource_int(tctx, p, resource_name, &hResource),
+ "failed to open resource");
+ test_CloseResource_int(tctx, p, &hResource);
+
+ torture_assert(tctx,
+ test_OpenResourceEx_int(tctx, p, resource_name, &hResource),
+ "failed to openex resource");
+
+ torture_assert(tctx,
+ test_GetResourceType_int(tctx, p, &hResource),
+ "failed to query resource type");
+ torture_assert(tctx,
+ test_GetResourceId_int(tctx, p, &hResource),
+ "failed to query resource id");
+ torture_assert(tctx,
+ test_GetResourceState_int(tctx, p, &hResource),
+ "failed to query resource state");
+ torture_assert(tctx,
+ test_CreateResEnum_int(tctx, p, &hResource),
+ "failed to query resource enum");
+ torture_assert(tctx,
+ test_GetResourceDependencyExpression_int(tctx, p, &hResource),
+ "failed to query resource dependency expression");
+ torture_assert(tctx,
+ test_GetResourceNetworkName_int(tctx, p, &hResource),
+ "failed to query resource network name");
+
+ test_CloseResource_int(tctx, p, &hResource);
+
+ return true;
+}
+
+static bool test_all_resources(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_CreateEnum r;
+ uint32_t dwType = CLUSTER_ENUM_RESOURCE;
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ int i;
+
+ r.in.dwType = dwType;
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnum_r(b, tctx, &r),
+ "CreateEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateEnum failed");
+
+ for (i=0; i < ReturnEnum->EntryCount; i++) {
+
+ struct ENUM_ENTRY e = ReturnEnum->Entry[i];
+
+ torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_RESOURCE, "type mismatch");
+
+ torture_assert(tctx,
+ test_one_resource(tctx, t->p, e.Name),
+ "failed to test one resource");
+ }
+
+ return true;
+}
+
+static bool test_all_resourcetypes(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_CreateEnum r;
+ uint32_t dwType = CLUSTER_ENUM_RESTYPE;
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ int i;
+
+ r.in.dwType = dwType;
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnum_r(b, tctx, &r),
+ "CreateEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateEnum failed");
+
+ for (i=0; i < ReturnEnum->EntryCount; i++) {
+
+ struct ENUM_ENTRY e = ReturnEnum->Entry[i];
+
+ torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_RESTYPE, "type mismatch");
+
+ torture_assert(tctx,
+ test_one_resourcetype(tctx, t->p, e.Name),
+ "failed to test one resourcetype");
+ }
+
+ return true;
+}
+
+
+static bool test_OpenNode_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszNodeName,
+ struct policy_handle *hNode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenNode r;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszNodeName = lpszNodeName;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hNode= hNode;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenNode_r(b, tctx, &r),
+ "OpenNode failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenNode failed");
+
+ return true;
+}
+
+static bool test_OpenNodeEx_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszNodeName,
+ struct policy_handle *hNode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenNodeEx r;
+ uint32_t lpdwGrantedAccess;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszNodeName = lpszNodeName;
+ r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.lpdwGrantedAccess = &lpdwGrantedAccess;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hNode= hNode;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenNodeEx_r(b, tctx, &r),
+ "OpenNodeEx failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenNodeEx failed");
+
+ return true;
+}
+
+
+static bool test_CloseNode_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Node)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CloseNode r;
+
+ r.in.Node = Node;
+ r.out.Node = Node;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CloseNode_r(b, tctx, &r),
+ "CloseNode failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CloseNode failed");
+ torture_assert(tctx,
+ ndr_policy_handle_empty(Node),
+ "policy_handle non empty after CloseNode");
+
+ return true;
+}
+
+static bool test_OpenNode(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNode;
+
+ if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) {
+ return false;
+ }
+
+ test_CloseNode_int(tctx, t->p, &hNode);
+
+ return true;
+}
+
+static bool test_OpenNodeEx(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNode;
+
+ if (!test_OpenNodeEx_int(tctx, t->p, t->NodeName, &hNode)) {
+ return false;
+ }
+
+ test_CloseNode_int(tctx, t->p, &hNode);
+
+ return true;
+}
+
+static bool test_CloseNode(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNode;
+
+ if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) {
+ return false;
+ }
+
+ return test_CloseNode_int(tctx, t->p, &hNode);
+}
+
+static bool test_GetNodeState_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetNodeState r;
+ enum clusapi_ClusterNodeState State;
+ WERROR rpc_status;
+
+ r.in.hNode = *hNode;
+ r.out.State = &State;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetNodeState_r(b, tctx, &r),
+ "GetNodeState failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetNodeState failed");
+
+ return true;
+}
+
+static bool test_GetNodeState(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNode;
+ bool ret = true;
+
+ if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) {
+ return false;
+ }
+
+ ret = test_GetNodeState_int(tctx, t->p, &hNode);
+
+ test_CloseNode_int(tctx, t->p, &hNode);
+
+ return ret;
+}
+
+static bool test_GetNodeId_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetNodeId r;
+ const char *pGuid;
+ WERROR rpc_status;
+
+ r.in.hNode = *hNode;
+ r.out.pGuid = &pGuid;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetNodeId_r(b, tctx, &r),
+ "GetNodeId failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetNodeId failed");
+
+ return true;
+}
+
+static bool test_GetNodeId(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNode;
+ bool ret = true;
+
+ if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) {
+ return false;
+ }
+
+ ret = test_GetNodeId_int(tctx, t->p, &hNode);
+
+ test_CloseNode_int(tctx, t->p, &hNode);
+
+ return ret;
+}
+
+static bool test_NodeControl_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNode,
+ enum clusapi_NodeControlCode dwControlCode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_NodeControl r;
+ uint32_t lpBytesReturned;
+ uint32_t lpcbRequired;
+ WERROR rpc_status;
+
+ r.in.hNode = *hNode;
+ r.in.dwControlCode = 0;
+ r.in.lpInBuffer = NULL;
+ r.in.nInBufferSize = 0;
+ r.in.nOutBufferSize = 0;
+ r.out.lpOutBuffer = NULL;
+ r.out.lpBytesReturned = &lpBytesReturned;
+ r.out.lpcbRequired = &lpcbRequired;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_NodeControl_r(b, tctx, &r),
+ "NodeControl failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_FUNCTION,
+ "NodeControl failed");
+
+ r.in.dwControlCode = dwControlCode;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_NodeControl_r(b, tctx, &r),
+ "NodeControl failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, *r.out.lpcbRequired);
+ r.in.nOutBufferSize = *r.out.lpcbRequired;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_NodeControl_r(b, tctx, &r),
+ "NodeControl failed");
+ }
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "NodeControl failed");
+
+ /* now try what happens when we query with a buffer large enough to hold
+ * the entire packet */
+
+ r.in.nOutBufferSize = 0x4000;
+ r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, r.in.nOutBufferSize);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_NodeControl_r(b, tctx, &r),
+ "NodeControl failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "NodeControl failed");
+ torture_assert(tctx, *r.out.lpBytesReturned < r.in.nOutBufferSize,
+ "lpBytesReturned expected to be smaller than input size nOutBufferSize");
+
+ if (dwControlCode == CLUSCTL_NODE_GET_ID) {
+ const char *str;
+ DATA_BLOB blob = data_blob_const(r.out.lpOutBuffer, *r.out.lpBytesReturned);
+
+ torture_assert(tctx, *r.out.lpBytesReturned >= 4, "must be at least 4 bytes long");
+ torture_assert(tctx, (*r.out.lpBytesReturned % 2) == 0, "must be a multiple of 2");
+
+ torture_assert(tctx,
+ pull_reg_sz(tctx, &blob, &str),
+ "failed to pull unicode string");
+
+ torture_comment(tctx, "got this node id: '%s'", str);
+ }
+
+ return true;
+}
+
+static bool test_NodeControl(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNode;
+ bool ret = true;
+
+ if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) {
+ return false;
+ }
+
+ ret = test_NodeControl_int(tctx, t->p, &hNode, CLUSCTL_NODE_GET_RO_COMMON_PROPERTIES);
+ if (!ret) {
+ return false;
+ }
+
+ ret = test_NodeControl_int(tctx, t->p, &hNode, CLUSCTL_NODE_GET_ID);
+ if (!ret) {
+ return false;
+ }
+
+ test_CloseNode_int(tctx, t->p, &hNode);
+
+ return ret;
+}
+
+static bool test_PauseNode_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_PauseNode r;
+ WERROR rpc_status;
+
+ r.in.hNode = *hNode;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_PauseNode_r(b, tctx, &r),
+ "PauseNode failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "PauseNode failed");
+
+ return true;
+}
+
+static bool test_PauseNode(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNode;
+ bool ret = true;
+
+ if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) {
+ return false;
+ }
+
+ ret = test_PauseNode_int(tctx, t->p, &hNode);
+
+ test_CloseNode_int(tctx, t->p, &hNode);
+
+ return ret;
+}
+
+static bool test_ResumeNode_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_ResumeNode r;
+ WERROR rpc_status;
+
+ r.in.hNode = *hNode;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_ResumeNode_r(b, tctx, &r),
+ "ResumeNode failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_CLUSTER_NODE_NOT_PAUSED,
+ "ResumeNode gave unexpected result");
+
+ return true;
+}
+
+static bool test_ResumeNode(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNode;
+ bool ret = true;
+
+ if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) {
+ return false;
+ }
+
+ ret = test_ResumeNode_int(tctx, t->p, &hNode);
+
+ test_CloseNode_int(tctx, t->p, &hNode);
+
+ return ret;
+}
+
+static bool test_EvictNode_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_EvictNode r;
+ WERROR rpc_status;
+
+ r.in.hNode = *hNode;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_EvictNode_r(b, tctx, &r),
+ "EvictNode failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "EvictNode failed");
+
+ return true;
+}
+
+static bool test_EvictNode(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNode;
+ bool ret = true;
+
+ if (!test_OpenNode_int(tctx, t->p, t->NodeName, &hNode)) {
+ return false;
+ }
+
+ ret = test_EvictNode_int(tctx, t->p, &hNode);
+
+ test_CloseNode_int(tctx, t->p, &hNode);
+
+ return ret;
+}
+
+static bool test_one_node(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *node_name)
+{
+ struct policy_handle hNode;
+
+ torture_assert(tctx,
+ test_OpenNode_int(tctx, p, node_name, &hNode),
+ "failed to open node");
+ test_CloseNode_int(tctx, p, &hNode);
+
+ torture_assert(tctx,
+ test_OpenNodeEx_int(tctx, p, node_name, &hNode),
+ "failed to openex node");
+
+ torture_assert(tctx,
+ test_GetNodeId_int(tctx, p, &hNode),
+ "failed to query node id");
+ torture_assert(tctx,
+ test_GetNodeState_int(tctx, p, &hNode),
+ "failed to query node id");
+
+ test_CloseNode_int(tctx, p, &hNode);
+
+ return true;
+}
+
+static bool test_all_nodes(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_CreateEnum r;
+ uint32_t dwType = CLUSTER_ENUM_NODE;
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ int i;
+
+ r.in.dwType = dwType;
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnum_r(b, tctx, &r),
+ "CreateEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateEnum failed");
+
+ for (i=0; i < ReturnEnum->EntryCount; i++) {
+
+ struct ENUM_ENTRY e = ReturnEnum->Entry[i];
+
+ torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_NODE, "type mismatch");
+
+ torture_assert(tctx,
+ test_one_node(tctx, t->p, e.Name),
+ "failed to test one node");
+ }
+
+ return true;
+}
+
+static bool test_OpenGroup_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszGroupName,
+ struct policy_handle *hGroup)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenGroup r;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszGroupName = lpszGroupName;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hGroup= hGroup;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenGroup_r(b, tctx, &r),
+ "OpenGroup failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenGroup failed");
+
+ return true;
+}
+
+static bool test_OpenGroupEx_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszGroupName,
+ struct policy_handle *hGroup)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenGroupEx r;
+ uint32_t lpdwGrantedAccess;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszGroupName = lpszGroupName;
+ r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.lpdwGrantedAccess = &lpdwGrantedAccess;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hGroup= hGroup;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenGroupEx_r(b, tctx, &r),
+ "OpenGroupEx failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenGroupEx failed");
+
+ return true;
+}
+
+static bool test_CloseGroup_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Group)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CloseGroup r;
+
+ r.in.Group = Group;
+ r.out.Group = Group;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CloseGroup_r(b, tctx, &r),
+ "CloseGroup failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CloseGroup failed");
+ torture_assert(tctx,
+ ndr_policy_handle_empty(Group),
+ "policy_handle non empty after CloseGroup");
+
+ return true;
+}
+
+static bool test_OpenGroup(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroup;
+
+ if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) {
+ return false;
+ }
+
+ test_CloseGroup_int(tctx, t->p, &hGroup);
+
+ return true;
+}
+
+static bool test_OpenGroupEx(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroup;
+
+ if (!test_OpenGroupEx_int(tctx, t->p, "Cluster Group", &hGroup)) {
+ return false;
+ }
+
+ test_CloseGroup_int(tctx, t->p, &hGroup);
+
+ return true;
+}
+
+static bool test_CloseGroup(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroup;
+
+ if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) {
+ return false;
+ }
+
+ return test_CloseGroup_int(tctx, t->p, &hGroup);
+}
+
+static bool test_GetGroupState_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hGroup)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetGroupState r;
+ enum clusapi_ClusterGroupState State;
+ const char *NodeName;
+ WERROR rpc_status;
+
+ r.in.hGroup = *hGroup;
+ r.out.State = &State;
+ r.out.NodeName = &NodeName;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetGroupState_r(b, tctx, &r),
+ "GetGroupState failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetGroupState failed");
+
+ return true;
+}
+
+static bool test_GetGroupState(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroup;
+ bool ret = true;
+
+ if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) {
+ return false;
+ }
+
+ ret = test_GetGroupState_int(tctx, t->p, &hGroup);
+
+ test_CloseGroup_int(tctx, t->p, &hGroup);
+
+ return ret;
+}
+
+static bool test_GetGroupId_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hGroup)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetGroupId r;
+ const char *pGuid;
+ WERROR rpc_status;
+
+ r.in.hGroup = *hGroup;
+ r.out.pGuid = &pGuid;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetGroupId_r(b, tctx, &r),
+ "GetGroupId failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetGroupId failed");
+
+ return true;
+}
+
+static bool test_GetGroupId(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroup;
+ bool ret = true;
+
+ if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) {
+ return false;
+ }
+
+ ret = test_GetGroupId_int(tctx, t->p, &hGroup);
+
+ test_CloseGroup_int(tctx, t->p, &hGroup);
+
+ return ret;
+}
+
+static bool test_GroupControl_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hGroup,
+ enum clusapi_GroupControlCode dwControlCode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GroupControl r;
+ uint32_t lpBytesReturned;
+ uint32_t lpcbRequired;
+ WERROR rpc_status;
+
+ r.in.hGroup = *hGroup;
+ r.in.dwControlCode = 0;
+ r.in.lpInBuffer = NULL;
+ r.in.nInBufferSize = 0;
+ r.in.nOutBufferSize = 0;
+ r.out.lpOutBuffer = NULL;
+ r.out.lpBytesReturned = &lpBytesReturned;
+ r.out.lpcbRequired = &lpcbRequired;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GroupControl_r(b, tctx, &r),
+ "GroupControl failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_FUNCTION,
+ "GroupControl failed");
+
+ r.in.dwControlCode = dwControlCode;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GroupControl_r(b, tctx, &r),
+ "GroupControl failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, *r.out.lpcbRequired);
+ r.in.nOutBufferSize = *r.out.lpcbRequired;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GroupControl_r(b, tctx, &r),
+ "GroupControl failed");
+ }
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GroupControl failed");
+
+ /* now try what happens when we query with a buffer large enough to hold
+ * the entire packet */
+
+ r.in.nOutBufferSize = 0x400;
+ r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, r.in.nOutBufferSize);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GroupControl_r(b, tctx, &r),
+ "GroupControl failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GroupControl failed");
+ torture_assert(tctx, *r.out.lpBytesReturned < r.in.nOutBufferSize,
+ "lpBytesReturned expected to be smaller than input size nOutBufferSize");
+
+ return true;
+}
+
+static bool test_CreateGroupResourceEnum_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hGroup)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CreateGroupResourceEnum r;
+ uint32_t dwType[] = {
+ CLUSTER_GROUP_ENUM_CONTAINS,
+ CLUSTER_GROUP_ENUM_NODES
+ };
+ uint32_t dwType_invalid[] = {
+ 0x00000040,
+ 0x00000080,
+ 0x00000100 /* and many more ... */
+ };
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ int i;
+
+ r.in.hGroup = *hGroup;
+
+ for (i=0; i < ARRAY_SIZE(dwType); i++) {
+
+ r.in.hGroup = *hGroup;
+ r.in.dwType = dwType[i];
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateGroupResourceEnum_r(b, tctx, &r),
+ "CreateGroupResourceEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateGroupResourceEnum failed");
+ }
+
+ for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) {
+
+ r.in.dwType = dwType_invalid[i];
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateGroupResourceEnum_r(b, tctx, &r),
+ "CreateGroupResourceEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateGroupResourceEnum failed");
+ }
+
+ return true;
+}
+
+
+static bool test_GroupControl(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroup;
+ bool ret = true;
+
+ if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) {
+ return false;
+ }
+
+ ret = test_GroupControl_int(tctx, t->p, &hGroup, CLUSCTL_GROUP_GET_CHARACTERISTICS);
+ if (!ret) {
+ return false;
+ }
+
+ ret = test_GroupControl_int(tctx, t->p, &hGroup, CLUSCTL_GROUP_GET_RO_COMMON_PROPERTIES);
+ if (!ret) {
+ return false;
+ }
+
+ ret = test_GroupControl_int(tctx, t->p, &hGroup, CLUSCTL_GROUP_GET_FLAGS);
+ if (!ret) {
+ return false;
+ }
+
+ test_CloseGroup_int(tctx, t->p, &hGroup);
+
+ return ret;
+}
+
+static bool test_OnlineGroup_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hGroup)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OnlineGroup r;
+ WERROR rpc_status;
+
+ r.in.hGroup = *hGroup;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OnlineGroup_r(b, tctx, &r),
+ "OnlineGroup failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "OnlineGroup failed");
+
+ return true;
+}
+
+static bool test_OnlineGroup(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroup;
+ bool ret = true;
+
+ if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) {
+ return false;
+ }
+
+ ret = test_OnlineGroup_int(tctx, t->p, &hGroup);
+
+ test_CloseGroup_int(tctx, t->p, &hGroup);
+
+ return ret;
+}
+
+static bool test_OfflineGroup_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hGroup)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OfflineGroup r;
+ WERROR rpc_status;
+
+ r.in.hGroup = *hGroup;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OfflineGroup_r(b, tctx, &r),
+ "OfflineGroup failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "OfflineGroup failed");
+
+ return true;
+}
+
+static bool test_OfflineGroup(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroup;
+ bool ret = true;
+
+ if (!test_OpenGroup_int(tctx, t->p, "Cluster Group", &hGroup)) {
+ return false;
+ }
+
+ ret = test_OfflineGroup_int(tctx, t->p, &hGroup);
+
+ test_CloseGroup_int(tctx, t->p, &hGroup);
+
+ return ret;
+}
+
+static bool test_one_group(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *group_name)
+{
+ struct policy_handle hGroup;
+
+ torture_assert(tctx,
+ test_OpenGroup_int(tctx, p, group_name, &hGroup),
+ "failed to open group");
+ test_CloseGroup_int(tctx, p, &hGroup);
+
+ torture_assert(tctx,
+ test_OpenGroupEx_int(tctx, p, group_name, &hGroup),
+ "failed to openex group");
+
+ torture_assert(tctx,
+ test_GetGroupId_int(tctx, p, &hGroup),
+ "failed to query group id");
+ torture_assert(tctx,
+ test_GetGroupState_int(tctx, p, &hGroup),
+ "failed to query group id");
+
+ torture_assert(tctx,
+ test_GroupControl_int(tctx, p, &hGroup, CLUSCTL_GROUP_GET_FLAGS),
+ "failed to query group control");
+
+ torture_assert(tctx,
+ test_CreateGroupResourceEnum_int(tctx, p, &hGroup),
+ "failed to query resource enum");
+
+ test_CloseGroup_int(tctx, p, &hGroup);
+
+ return true;
+}
+
+static bool test_all_groups(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_CreateEnum r;
+ uint32_t dwType = CLUSTER_ENUM_GROUP;
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ int i;
+
+ r.in.dwType = dwType;
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnum_r(b, tctx, &r),
+ "CreateEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateEnum failed");
+
+ for (i=0; i < ReturnEnum->EntryCount; i++) {
+
+ struct ENUM_ENTRY e = ReturnEnum->Entry[i];
+
+ torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_GROUP, "type mismatch");
+
+ torture_assert(tctx,
+ test_one_group(tctx, t->p, e.Name),
+ "failed to test one group");
+ }
+
+ return true;
+}
+
+static bool test_BackupClusterDatabase(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_BackupClusterDatabase r;
+ WERROR rpc_status;
+
+ r.in.lpszPathName = "c:\\cluster_backup";
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_BackupClusterDatabase_r(b, tctx, &r),
+ "BackupClusterDatabase failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_CALL_NOT_IMPLEMENTED,
+ "BackupClusterDatabase failed");
+
+ return true;
+}
+
+static bool test_SetServiceAccountPassword(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_SetServiceAccountPassword r;
+ uint32_t SizeReturned;
+ uint32_t ExpectedBufferSize;
+
+ r.in.lpszNewPassword = "P@ssw0rd!";
+ r.in.dwFlags = IDL_CLUSTER_SET_PASSWORD_IGNORE_DOWN_NODES;
+ r.in.ReturnStatusBufferSize = 1024;
+ r.out.ReturnStatusBufferPtr = NULL;
+ r.out.SizeReturned = &SizeReturned;
+ r.out.ExpectedBufferSize = &ExpectedBufferSize;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_SetServiceAccountPassword_r(b, tctx, &r),
+ "SetServiceAccountPassword failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_CALL_NOT_IMPLEMENTED,
+ "SetServiceAccountPassword failed");
+
+ return true;
+}
+
+static bool test_ClusterControl_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Cluster,
+ enum clusapi_ClusterControlCode dwControlCode)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_ClusterControl r;
+ uint32_t lpBytesReturned;
+ uint32_t lpcbRequired;
+ WERROR rpc_status;
+
+ r.in.hCluster = *Cluster;
+ r.in.dwControlCode = 0;
+ r.in.lpInBuffer = NULL;
+ r.in.nInBufferSize = 0;
+ r.in.nOutBufferSize = 0;
+ r.out.lpOutBuffer = NULL;
+ r.out.lpBytesReturned = &lpBytesReturned;
+ r.out.lpcbRequired = &lpcbRequired;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_ClusterControl_r(b, tctx, &r),
+ "ClusterControl failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_FUNCTION,
+ "ClusterControl failed");
+
+ r.in.dwControlCode = dwControlCode;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_ClusterControl_r(b, tctx, &r),
+ "ClusterControl failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, *r.out.lpcbRequired);
+ r.in.nOutBufferSize = *r.out.lpcbRequired;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_ClusterControl_r(b, tctx, &r),
+ "ClusterControl failed");
+ }
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "ClusterControl failed");
+
+ /* now try what happens when we query with a buffer large enough to hold
+ * the entire packet */
+
+ r.in.nOutBufferSize = 0xffff;
+ r.out.lpOutBuffer = talloc_zero_array(tctx, uint8_t, r.in.nOutBufferSize);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_ClusterControl_r(b, tctx, &r),
+ "ClusterControl failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "ClusterControl failed");
+ torture_assert(tctx, *r.out.lpBytesReturned < r.in.nOutBufferSize,
+ "lpBytesReturned expected to be smaller than input size nOutBufferSize");
+
+ return true;
+}
+
+static bool test_ClusterControl(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle Cluster;
+ bool ret;
+ uint32_t control_codes[] = {
+ CLUSCTL_CLUSTER_GET_COMMON_PROPERTIES,
+ CLUSCTL_CLUSTER_GET_RO_COMMON_PROPERTIES,
+ CLUSCTL_CLUSTER_GET_FQDN,
+ CLUSCTL_CLUSTER_GET_PRIVATE_PROPERTIES,
+ CLUSCTL_CLUSTER_CHECK_VOTER_DOWN
+ };
+ int i;
+
+ if (!test_OpenCluster_int(tctx, t->p, &Cluster)) {
+ return false;
+ }
+
+ for (i=0; i < ARRAY_SIZE(control_codes); i++) {
+ ret = test_ClusterControl_int(tctx, t->p, &Cluster,
+ control_codes[i]);
+ if (!ret) {
+ goto done;
+ }
+ }
+
+ done:
+ test_CloseCluster_int(tctx, t->p, &Cluster);
+
+ return ret;
+}
+
+static bool test_CreateResTypeEnum(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_CreateResTypeEnum r;
+ uint32_t dwType[] = {
+ CLUSTER_RESOURCE_TYPE_ENUM_NODES,
+ CLUSTER_RESOURCE_TYPE_ENUM_RESOURCES
+ };
+ uint32_t dwType_invalid[] = {
+ 0x00000040,
+ 0x00000080,
+ 0x00000100 /* and many more ... */
+ };
+ const char *valid_names[] = {
+ "Physical Disk",
+ "Storage Pool"
+ };
+ const char *invalid_names[] = {
+ "INVALID_TYPE_XXXX"
+ };
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ int i, s;
+
+ for (s = 0; s < ARRAY_SIZE(valid_names); s++) {
+
+ r.in.lpszTypeName = valid_names[s];
+
+ for (i=0; i < ARRAY_SIZE(dwType); i++) {
+
+ r.in.dwType = dwType[i];
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateResTypeEnum_r(b, tctx, &r),
+ "CreateResTypeEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateResTypeEnum failed");
+ }
+
+ for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) {
+
+ r.in.dwType = dwType_invalid[i];
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateResTypeEnum_r(b, tctx, &r),
+ "CreateResTypeEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateResTypeEnum failed");
+ }
+ }
+
+ for (s = 0; s < ARRAY_SIZE(invalid_names); s++) {
+
+ r.in.lpszTypeName = invalid_names[s];
+
+ for (i=0; i < ARRAY_SIZE(dwType); i++) {
+
+ r.in.dwType = dwType[i];
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateResTypeEnum_r(b, tctx, &r),
+ "CreateResTypeEnum failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_CLUSTER_RESOURCE_TYPE_NOT_FOUND,
+ "CreateResTypeEnum failed");
+ }
+
+ for (i=0; i < ARRAY_SIZE(dwType_invalid); i++) {
+
+ r.in.dwType = dwType_invalid[i];
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateResTypeEnum_r(b, tctx, &r),
+ "CreateResTypeEnum failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_CLUSTER_RESOURCE_TYPE_NOT_FOUND,
+ "CreateResTypeEnum failed");
+ }
+ }
+
+
+ return true;
+}
+
+static bool test_CreateGroupEnum_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Cluster,
+ const char **multi_sz,
+ const char **multi_sz_ro)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CreateGroupEnum r;
+ struct GROUP_ENUM_LIST *pResultList;
+ WERROR rpc_status;
+ DATA_BLOB blob = data_blob_null;
+ DATA_BLOB blob_ro = data_blob_null;
+
+ r.in.hCluster = *Cluster;
+ r.in.pProperties = blob.data;
+ r.in.cbProperties = blob.length;
+ r.in.pRoProperties = blob_ro.data;
+ r.in.cbRoProperties = blob_ro.length;
+ r.out.ppResultList = &pResultList;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateGroupEnum_r(b, tctx, &r),
+ "CreateGroupEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateGroupEnum failed");
+
+ if (!push_reg_multi_sz(tctx, &blob, multi_sz)) {
+ return false;
+ }
+
+ if (!push_reg_multi_sz(tctx, &blob_ro, multi_sz_ro)) {
+ return false;
+ }
+
+ r.in.pProperties = blob.data;
+ r.in.cbProperties = blob.length;
+
+ r.in.pRoProperties = blob_ro.data;
+ r.in.cbRoProperties = blob_ro.length;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateGroupEnum_r(b, tctx, &r),
+ "CreateGroupEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateGroupEnum failed");
+
+#if 0
+ {
+ int i;
+ enum ndr_err_code ndr_err;
+
+ for (i=0; i < pResultList->EntryCount; i++) {
+ struct clusapi_PROPERTY_LIST list;
+ torture_comment(tctx, "entry #%d\n", i);
+
+ blob = data_blob_const(pResultList->Entry[i].Properties,
+ pResultList->Entry[i].cbProperties);
+
+ ndr_err = ndr_pull_struct_blob(&blob, tctx, &list,
+ (ndr_pull_flags_fn_t)ndr_pull_clusapi_PROPERTY_LIST);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NDR_PRINT_DEBUG(clusapi_PROPERTY_LIST, &list);
+ }
+
+ blob_ro = data_blob_const(pResultList->Entry[i].RoProperties,
+ pResultList->Entry[i].cbRoProperties);
+
+ ndr_err = ndr_pull_struct_blob(&blob_ro, tctx, &list,
+ (ndr_pull_flags_fn_t)ndr_pull_clusapi_PROPERTY_LIST);
+ if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NDR_PRINT_DEBUG(clusapi_PROPERTY_LIST, &list);
+ }
+ }
+ }
+#endif
+
+ return true;
+}
+
+static bool test_CreateGroupEnum(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle Cluster;
+ bool ret;
+ const char *multi_sz[] = {
+ "Priority", NULL,
+ };
+ const char *multi_sz_ro[] = {
+ "GroupType", NULL,
+ };
+
+ if (!test_OpenCluster_int(tctx, t->p, &Cluster)) {
+ return false;
+ }
+
+ ret = test_CreateGroupEnum_int(tctx, t->p, &Cluster,
+ multi_sz, multi_sz_ro);
+ if (!ret) {
+ goto done;
+ }
+
+ done:
+ test_CloseCluster_int(tctx, t->p, &Cluster);
+
+ return ret;
+}
+
+static bool test_OpenNetwork_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszNetworkName,
+ struct policy_handle *hNetwork)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenNetwork r;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszNetworkName = lpszNetworkName;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hNetwork = hNetwork ;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenNetwork_r(b, tctx, &r),
+ "OpenNetwork failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenNetwork failed");
+
+ return true;
+}
+
+static bool test_OpenNetworkEx_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszNetworkName,
+ struct policy_handle *hNetwork)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenNetworkEx r;
+ uint32_t lpdwGrantedAccess;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszNetworkName = lpszNetworkName;
+ r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.lpdwGrantedAccess = &lpdwGrantedAccess;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hNetwork = hNetwork ;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenNetworkEx_r(b, tctx, &r),
+ "OpenNetworkEx failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenNetworkEx failed");
+
+ return true;
+}
+
+static bool test_CloseNetwork_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *Network)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CloseNetwork r;
+
+ r.in.Network = Network;
+ r.out.Network = Network;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CloseNetwork_r(b, tctx, &r),
+ "CloseNetwork failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CloseNetwork failed");
+ torture_assert(tctx,
+ ndr_policy_handle_empty(Network),
+ "policy_handle non empty after CloseNetwork");
+
+ return true;
+}
+
+static bool test_OpenNetwork(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetwork;
+
+ if (!test_OpenNetwork_int(tctx, t->p, "Cluster Network 1", &hNetwork)) {
+ return false;
+ }
+
+ test_CloseNetwork_int(tctx, t->p, &hNetwork);
+
+ return true;
+}
+
+static bool test_OpenNetworkEx(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetwork;
+
+ if (!test_OpenNetworkEx_int(tctx, t->p, "Cluster Network 1", &hNetwork)) {
+ return false;
+ }
+
+ test_CloseNetwork_int(tctx, t->p, &hNetwork);
+
+ return true;
+}
+
+static bool test_CloseNetwork(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetwork;
+
+ if (!test_OpenNetwork_int(tctx, t->p, "Cluster Network 1", &hNetwork)) {
+ return false;
+ }
+
+ return test_CloseNetwork_int(tctx, t->p, &hNetwork);
+}
+
+static bool test_GetNetworkState_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNetwork)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetNetworkState r;
+ enum clusapi_ClusterNetworkState State;
+ WERROR rpc_status;
+
+ r.in.hNetwork = *hNetwork;
+ r.out.State = &State;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetNetworkState_r(b, tctx, &r),
+ "GetNetworkState failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetNetworkState failed");
+
+ return true;
+}
+
+static bool test_GetNetworkState(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetwork;
+ bool ret = true;
+
+ if (!test_OpenNetwork_int(tctx, t->p, "Cluster Network 1", &hNetwork)) {
+ return false;
+ }
+
+ ret = test_GetNetworkState_int(tctx, t->p, &hNetwork);
+
+ test_CloseNetwork_int(tctx, t->p, &hNetwork);
+
+ return ret;
+}
+
+static bool test_GetNetworkId_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNetwork)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetNetworkId r;
+ const char *pGuid;
+ WERROR rpc_status;
+
+ r.in.hNetwork = *hNetwork;
+ r.out.pGuid = &pGuid;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetNetworkId_r(b, tctx, &r),
+ "GetNetworkId failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetNetworkId failed");
+
+ return true;
+}
+
+static bool test_GetNetworkId(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetwork;
+ bool ret = true;
+
+ if (!test_OpenNetwork_int(tctx, t->p, "Cluster Network 1", &hNetwork)) {
+ return false;
+ }
+
+ ret = test_GetNetworkId_int(tctx, t->p, &hNetwork);
+
+ test_CloseNetwork_int(tctx, t->p, &hNetwork);
+
+ return ret;
+}
+
+static bool test_one_network(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *network_name)
+{
+ struct policy_handle hNetwork;
+
+ torture_assert(tctx,
+ test_OpenNetwork_int(tctx, p, network_name, &hNetwork),
+ "failed to open network");
+ test_CloseNetwork_int(tctx, p, &hNetwork);
+
+ torture_assert(tctx,
+ test_OpenNetworkEx_int(tctx, p, network_name, &hNetwork),
+ "failed to openex network");
+
+ torture_assert(tctx,
+ test_GetNetworkId_int(tctx, p, &hNetwork),
+ "failed to query network id");
+ torture_assert(tctx,
+ test_GetNetworkState_int(tctx, p, &hNetwork),
+ "failed to query network id");
+
+ test_CloseNetwork_int(tctx, p, &hNetwork);
+
+ return true;
+}
+
+static bool test_all_networks(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_CreateEnum r;
+ uint32_t dwType = CLUSTER_ENUM_NETWORK;
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ int i;
+
+ r.in.dwType = dwType;
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnum_r(b, tctx, &r),
+ "CreateEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateEnum failed");
+
+ for (i=0; i < ReturnEnum->EntryCount; i++) {
+
+ struct ENUM_ENTRY e = ReturnEnum->Entry[i];
+
+ torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_NETWORK, "type mismatch");
+
+ torture_assert(tctx,
+ test_one_network(tctx, t->p, e.Name),
+ "failed to test one network");
+ }
+
+ return true;
+}
+
+static bool test_OpenNetInterface_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszNetInterfaceName,
+ struct policy_handle *hNetInterface)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenNetInterface r;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszNetInterfaceName = lpszNetInterfaceName;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hNetInterface = hNetInterface;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenNetInterface_r(b, tctx, &r),
+ "OpenNetInterface failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenNetInterface failed");
+
+ return true;
+}
+
+static bool test_OpenNetInterfaceEx_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszNetInterfaceName,
+ struct policy_handle *hNetInterface)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenNetInterfaceEx r;
+ uint32_t lpdwGrantedAccess;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszNetInterfaceName = lpszNetInterfaceName;
+ r.in.dwDesiredAccess = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.lpdwGrantedAccess = &lpdwGrantedAccess;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hNetInterface = hNetInterface;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenNetInterfaceEx_r(b, tctx, &r),
+ "OpenNetInterfaceEx failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenNetInterfaceEx failed");
+
+ return true;
+}
+
+static bool test_CloseNetInterface_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *NetInterface)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CloseNetInterface r;
+
+ r.in.NetInterface = NetInterface;
+ r.out.NetInterface = NetInterface;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CloseNetInterface_r(b, tctx, &r),
+ "CloseNetInterface failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CloseNetInterface failed");
+ torture_assert(tctx,
+ ndr_policy_handle_empty(NetInterface),
+ "policy_handle non empty after CloseNetInterface");
+
+ return true;
+}
+
+static bool test_OpenNetInterface(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetInterface;
+
+ if (!test_OpenNetInterface_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) {
+ return false;
+ }
+
+ test_CloseNetInterface_int(tctx, t->p, &hNetInterface);
+
+ return true;
+}
+
+static bool test_OpenNetInterfaceEx(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetInterface;
+
+ if (!test_OpenNetInterfaceEx_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) {
+ return false;
+ }
+
+ test_CloseNetInterface_int(tctx, t->p, &hNetInterface);
+
+ return true;
+}
+
+static bool test_CloseNetInterface(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetInterface;
+
+ if (!test_OpenNetInterface_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) {
+ return false;
+ }
+
+ return test_CloseNetInterface_int(tctx, t->p, &hNetInterface);
+}
+
+static bool test_GetNetInterfaceState_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNetInterface)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetNetInterfaceState r;
+ enum clusapi_ClusterNetInterfaceState State;
+ WERROR rpc_status;
+
+ r.in.hNetInterface = *hNetInterface;
+ r.out.State = &State;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetNetInterfaceState_r(b, tctx, &r),
+ "GetNetInterfaceState failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetNetInterfaceState failed");
+
+ return true;
+}
+
+static bool test_GetNetInterfaceState(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetInterface;
+ bool ret = true;
+
+ if (!test_OpenNetInterface_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) {
+ return false;
+ }
+
+ ret = test_GetNetInterfaceState_int(tctx, t->p, &hNetInterface);
+
+ test_CloseNetInterface_int(tctx, t->p, &hNetInterface);
+
+ return ret;
+}
+
+static bool test_GetNetInterfaceId_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hNetInterface)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetNetInterfaceId r;
+ const char *pGuid;
+ WERROR rpc_status;
+
+ r.in.hNetInterface = *hNetInterface;
+ r.out.pGuid = &pGuid;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetNetInterfaceId_r(b, tctx, &r),
+ "GetNetInterfaceId failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetNetInterfaceId failed");
+
+ return true;
+}
+
+static bool test_GetNetInterfaceId(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hNetInterface;
+ bool ret = true;
+
+ if (!test_OpenNetInterface_int(tctx, t->p, "node1 - Ethernet", &hNetInterface)) {
+ return false;
+ }
+
+ ret = test_GetNetInterfaceId_int(tctx, t->p, &hNetInterface);
+
+ test_CloseNetInterface_int(tctx, t->p, &hNetInterface);
+
+ return ret;
+}
+
+static bool test_one_netinterface(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *netinterface_name)
+{
+ struct policy_handle hNetInterface;
+
+ torture_assert(tctx,
+ test_OpenNetInterface_int(tctx, p, netinterface_name, &hNetInterface),
+ "failed to open netinterface");
+ test_CloseNetInterface_int(tctx, p, &hNetInterface);
+
+ torture_assert(tctx,
+ test_OpenNetInterfaceEx_int(tctx, p, netinterface_name, &hNetInterface),
+ "failed to openex netinterface");
+
+ torture_assert(tctx,
+ test_GetNetInterfaceId_int(tctx, p, &hNetInterface),
+ "failed to query netinterface id");
+ torture_assert(tctx,
+ test_GetNetInterfaceState_int(tctx, p, &hNetInterface),
+ "failed to query netinterface id");
+
+ test_CloseNetInterface_int(tctx, p, &hNetInterface);
+
+ return true;
+}
+
+static bool test_all_netinterfaces(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_CreateEnum r;
+ uint32_t dwType = CLUSTER_ENUM_NETINTERFACE;
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ int i;
+
+ r.in.dwType = dwType;
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnum_r(b, tctx, &r),
+ "CreateEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateEnum failed");
+
+ for (i=0; i < ReturnEnum->EntryCount; i++) {
+
+ struct ENUM_ENTRY e = ReturnEnum->Entry[i];
+
+ torture_assert_int_equal(tctx, e.Type, CLUSTER_ENUM_NETINTERFACE, "type mismatch");
+
+ torture_assert(tctx,
+ test_one_netinterface(tctx, t->p, e.Name),
+ "failed to test one netinterface");
+ }
+
+ return true;
+}
+
+static bool test_CloseKey_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *pKey)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CloseKey r;
+
+ r.in.pKey = pKey;
+ r.out.pKey = pKey;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CloseKey_r(b, tctx, &r),
+ "CloseKey failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CloseKey failed");
+ torture_assert(tctx,
+ ndr_policy_handle_empty(pKey),
+ "policy_handle non empty after CloseKey");
+
+ return true;
+}
+
+static bool test_GetRootKey_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *phKey)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetRootKey r;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.samDesired = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.Status = &Status;
+ r.out.rpc_status = &rpc_status;
+ r.out.phKey = phKey;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetRootKey_r(b, tctx, &r),
+ "GetRootKey failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "GetRootKey failed");
+
+ return true;
+}
+
+static bool test_EnumKey_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hKey)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_EnumKey r;
+ const char *KeyName;
+ NTTIME lpftLastWriteTime;
+ WERROR rpc_status;
+
+ r.in.hKey = *hKey;
+ r.in.dwIndex = 0;
+ r.out.KeyName = &KeyName;
+ r.out.lpftLastWriteTime = &lpftLastWriteTime;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_EnumKey_r(b, tctx, &r),
+ "EnumKey failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "EnumKey failed");
+
+ return true;
+}
+
+static bool test_OpenKey_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hKey,
+ const char *lpSubKey,
+ struct policy_handle *phKey)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenKey r;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.hKey = *hKey;
+ r.in.lpSubKey = lpSubKey;
+ r.in.samDesired = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.Status = &Status;
+ r.out.rpc_status = &rpc_status;
+ r.out.phKey = phKey;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenKey_r(b, tctx, &r),
+ "OpenKey failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenKey failed");
+
+ return true;
+}
+
+static bool test_EnumValue_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hKey)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_EnumValue r;
+ const char *lpValueName;
+ uint32_t lpType;
+ uint32_t TotalSize;
+ WERROR rpc_status;
+ int i = 0;
+
+ do {
+ uint32_t lpcbData = 2048;
+
+ r.in.hKey = *hKey;
+ r.in.dwIndex = i++;
+ r.in.lpcbData = &lpcbData;
+ r.out.lpValueName = &lpValueName;
+ r.out.lpType = &lpType;
+ r.out.lpData = talloc_array(tctx, uint8_t, lpcbData);
+ r.out.TotalSize = &TotalSize;
+ r.out.rpc_status = &rpc_status;
+ r.out.lpcbData = &lpcbData;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_EnumValue_r(b, tctx, &r),
+ "EnumValue failed");
+
+ } while (W_ERROR_IS_OK(r.out.result));
+
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_NO_MORE_ITEMS,
+ "EnumValue failed");
+
+ return true;
+}
+
+static bool test_QueryInfoKey_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hKey)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_QueryInfoKey r;
+ uint32_t lpcSubKeys;
+ uint32_t lpcbMaxSubKeyLen;
+ uint32_t lpcValues;
+ uint32_t lpcbMaxValueNameLen;
+ uint32_t lpcbMaxValueLen;
+ uint32_t lpcbSecurityDescriptor;
+ NTTIME lpftLastWriteTime;
+ WERROR rpc_status;
+
+ r.in.hKey = *hKey;
+ r.out.lpcSubKeys = &lpcSubKeys;
+ r.out.lpcbMaxSubKeyLen = &lpcbMaxSubKeyLen;
+ r.out.lpcValues = &lpcValues;
+ r.out.lpcbMaxValueNameLen = &lpcbMaxValueNameLen;
+ r.out.lpcbMaxValueLen = &lpcbMaxValueLen;
+ r.out.lpcbSecurityDescriptor = &lpcbSecurityDescriptor;
+ r.out.lpftLastWriteTime = &lpftLastWriteTime;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_QueryInfoKey_r(b, tctx, &r),
+ "QueryInfoKey failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "QueryInfoKey failed");
+
+ return true;
+}
+
+static bool test_GetKeySecurity_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hKey)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetKeySecurity r;
+ uint32_t SecurityInformation = SECINFO_DACL | SECINFO_OWNER | SECINFO_GROUP;
+ struct RPC_SECURITY_DESCRIPTOR pRpcSecurityDescriptor;
+ WERROR rpc_status;
+
+ ZERO_STRUCT(pRpcSecurityDescriptor);
+
+ r.in.hKey = *hKey;
+ r.in.SecurityInformation = SecurityInformation;
+ r.in.pRpcSecurityDescriptor = &pRpcSecurityDescriptor;
+ r.out.rpc_status = &rpc_status;
+ r.out.pRpcSecurityDescriptor = &pRpcSecurityDescriptor;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetKeySecurity_r(b, tctx, &r),
+ "GetKeySecurity failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ pRpcSecurityDescriptor.lpSecurityDescriptor = talloc_array(tctx,
+ uint8_t, pRpcSecurityDescriptor.cbInSecurityDescriptor);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetKeySecurity_r(b, tctx, &r),
+ "GetKeySecurity failed");
+ }
+
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetKeySecurity failed");
+
+ return true;
+}
+
+static bool test_GetRootKey(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hKey;
+
+ if (!test_GetRootKey_int(tctx, t->p, &hKey)) {
+ return false;
+ }
+
+ test_CloseKey_int(tctx, t->p, &hKey);
+
+ return true;
+}
+
+static bool test_CloseKey(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hKey;
+
+ if (!test_GetRootKey_int(tctx, t->p, &hKey)) {
+ return false;
+ }
+
+ return test_CloseKey_int(tctx, t->p, &hKey);
+}
+
+static bool test_EnumKey(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hKey;
+ bool ret = true;
+
+ if (!test_GetRootKey_int(tctx, t->p, &hKey)) {
+ return false;
+ }
+
+ ret = test_EnumKey_int(tctx, t->p, &hKey);
+
+ test_CloseKey_int(tctx, t->p, &hKey);
+
+ return ret;
+}
+
+static bool test_QueryValue_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hKey,
+ const char *ValueName)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_QueryValue r;
+ uint32_t lpValueType;
+ uint32_t lpcbRequired;
+ WERROR rpc_status;
+
+ r.in.hKey = *hKey;
+ r.in.lpValueName = ValueName;
+ r.in.cbData = 0;
+ r.out.lpValueType = &lpValueType;
+ r.out.lpData = NULL;
+ r.out.lpcbRequired = &lpcbRequired;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_QueryValue_r(b, tctx, &r),
+ "QueryValue failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+
+ r.in.cbData = lpcbRequired;
+ r.out.lpData = talloc_zero_array(tctx, uint8_t, r.in.cbData);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_QueryValue_r(b, tctx, &r),
+ "QueryValue failed");
+ }
+
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "QueryValue failed");
+
+ if (lpValueType == REG_SZ) {
+ const char *s;
+ DATA_BLOB blob = data_blob_const(r.out.lpData, lpcbRequired);
+ pull_reg_sz(tctx, &blob, &s);
+ torture_comment(tctx, "got: %s\n", s);
+ }
+
+ return true;
+}
+
+static bool test_QueryValue(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hKey;
+ bool ret = true;
+
+ if (!test_GetRootKey_int(tctx, t->p, &hKey)) {
+ return false;
+ }
+
+ ret = test_QueryValue_int(tctx, t->p, &hKey, "ClusterInstanceID");
+
+ test_CloseKey_int(tctx, t->p, &hKey);
+
+ return ret;
+}
+
+
+static bool test_one_key(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hKey,
+ const char *KeyName)
+{
+ struct policy_handle phKey;
+
+ torture_assert(tctx,
+ test_OpenKey_int(tctx, p, hKey, KeyName, &phKey),
+ "failed to open key");
+
+ torture_assert(tctx,
+ test_QueryInfoKey_int(tctx, p, &phKey),
+ "failed to enum values");
+ torture_assert(tctx,
+ test_GetKeySecurity_int(tctx, p, &phKey),
+ "failed to get key security");
+
+ torture_assert(tctx,
+ test_EnumValue_int(tctx, p, &phKey),
+ "failed to enum values");
+
+ torture_assert(tctx,
+ test_CloseKey_int(tctx, p, &phKey),
+ "failed to close key");
+
+ return true;
+}
+
+static bool test_all_keys(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct policy_handle hKey;
+ struct clusapi_EnumKey r;
+ const char *KeyName;
+ NTTIME lpftLastWriteTime;
+ WERROR rpc_status;
+ int i = 0;
+
+ if (!test_GetRootKey_int(tctx, t->p, &hKey)) {
+ return false;
+ }
+
+ do {
+ r.in.hKey = hKey;
+ r.in.dwIndex = i++;
+ r.out.KeyName = &KeyName;
+ r.out.lpftLastWriteTime = &lpftLastWriteTime;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_EnumKey_r(b, tctx, &r),
+ "EnumKey failed");
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+ torture_assert(tctx,
+ test_one_key(tctx, t->p, &hKey, KeyName),
+ "failed to test one key");
+ }
+
+ } while (W_ERROR_IS_OK(r.out.result));
+
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_NO_MORE_ITEMS,
+ "EnumKey failed");
+
+ test_CloseKey_int(tctx, t->p, &hKey);
+
+ return true;
+}
+
+static bool test_OpenGroupSet_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *lpszGroupSetName,
+ struct policy_handle *hGroupSet)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_OpenGroupSet r;
+ WERROR Status;
+ WERROR rpc_status;
+
+ r.in.lpszGroupSetName = lpszGroupSetName;
+ r.out.rpc_status = &rpc_status;
+ r.out.Status = &Status;
+ r.out.hGroupSet = hGroupSet;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_OpenGroupSet_r(b, tctx, &r),
+ "OpenGroupSet failed");
+ torture_assert_werr_ok(tctx,
+ *r.out.Status,
+ "OpenGroupSet failed");
+
+ return true;
+}
+
+static bool test_CloseGroupSet_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *GroupSet)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_CloseGroupSet r;
+
+ r.in.GroupSet = GroupSet;
+ r.out.GroupSet = GroupSet;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CloseGroupSet_r(b, tctx, &r),
+ "CloseGroupSet failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CloseGroupSet failed");
+ torture_assert(tctx,
+ ndr_policy_handle_empty(GroupSet),
+ "policy_handle non empty after CloseGroupSet");
+
+ return true;
+}
+
+static bool test_OpenGroupSet(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroupSet;
+
+ if (t->lpwMajorVersion < 0x000a) {
+ torture_skip(tctx, "GroupSet fn not available on old clusters");
+ return true;
+ }
+
+ if (!test_OpenGroupSet_int(tctx, t->p, "Cluster Group", &hGroupSet)) {
+ return false;
+ }
+
+ test_CloseGroupSet_int(tctx, t->p, &hGroupSet);
+
+ return true;
+}
+
+static bool test_CloseGroupSet(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct policy_handle hGroupSet;
+
+ if (t->lpwMajorVersion < 0x000a) {
+ torture_skip(tctx, "GroupSet fn not available on old clusters");
+ return true;
+ }
+
+ if (!test_OpenGroupSet_int(tctx, t->p, "Cluster Group", &hGroupSet)) {
+ return false;
+ }
+
+ return test_CloseGroupSet_int(tctx, t->p, &hGroupSet);
+}
+
+static bool test_one_groupset(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *groupset_name)
+{
+ struct policy_handle hGroupSet;
+
+ torture_assert(tctx,
+ test_OpenGroupSet_int(tctx, p, groupset_name, &hGroupSet),
+ "failed to open groupset");
+
+ test_CloseGroupSet_int(tctx, p, &hGroupSet);
+
+ return true;
+}
+
+static bool test_all_groupsets(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_clusapi_context *t =
+ talloc_get_type_abort(data, struct torture_clusapi_context);
+ struct dcerpc_binding_handle *b = t->p->binding_handle;
+ struct clusapi_CreateGroupSetEnum r;
+ struct ENUM_LIST *ReturnEnum;
+ struct policy_handle Cluster;
+ WERROR rpc_status;
+ int i;
+
+ if (!test_OpenCluster_int(tctx, t->p, &Cluster)) {
+ return false;
+ }
+
+ r.in.hCluster = Cluster;
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateGroupSetEnum_r(b, tctx, &r),
+ "CreateGroupSetEnum failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "CreateGroupSetEnum failed");
+
+ test_CloseCluster_int(tctx, t->p, &Cluster);
+
+ for (i=0; i < ReturnEnum->EntryCount; i++) {
+
+ struct ENUM_ENTRY e = ReturnEnum->Entry[i];
+
+ torture_assert(tctx,
+ test_one_groupset(tctx, t->p, e.Name),
+ "failed to test one groupset");
+ }
+
+ return true;
+}
+
+static bool torture_rpc_clusapi_setup_common(struct torture_context *tctx,
+ struct torture_clusapi_context *t)
+{
+ struct dcerpc_binding_handle *b;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &t->p, &ndr_table_clusapi),
+ "Error connecting to server");
+
+ b = t->p->binding_handle;
+
+ {
+ struct clusapi_GetClusterName r;
+
+ r.out.ClusterName = &t->ClusterName;
+ r.out.NodeName = &t->NodeName;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetClusterName_r(b, tctx, &r),
+ "GetClusterName failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetClusterName failed");
+ }
+ {
+ struct clusapi_GetClusterVersion2 r;
+ const char *lpszVendorId;
+ const char *lpszCSDVersion;
+ struct CLUSTER_OPERATIONAL_VERSION_INFO *ppClusterOpVerInfo;
+ WERROR rpc_status;
+
+ r.out.lpwMajorVersion = &t->lpwMajorVersion;
+ r.out.lpwMinorVersion = &t->lpwMinorVersion;
+ r.out.lpwBuildNumber = &t->lpwBuildNumber;
+ r.out.lpszVendorId = &lpszVendorId;
+ r.out.lpszCSDVersion = &lpszCSDVersion;
+ r.out.ppClusterOpVerInfo = &ppClusterOpVerInfo;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetClusterVersion2_r(b, tctx, &r),
+ "GetClusterVersion2 failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetClusterVersion2 failed");
+ }
+
+ return true;
+}
+
+static bool torture_rpc_clusapi_setup(struct torture_context *tctx,
+ void **data)
+{
+ struct torture_clusapi_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_clusapi_context);
+
+ return torture_rpc_clusapi_setup_common(tctx, t);
+}
+
+static bool torture_rpc_clusapi_teardown(struct torture_context *tctx,
+ void *data)
+{
+ talloc_free(data);
+
+ return true;
+}
+
+void torture_tcase_cluster(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "OpenCluster",
+ test_OpenCluster);
+ torture_tcase_add_simple_test(tcase, "OpenClusterEx",
+ test_OpenClusterEx);
+ torture_tcase_add_simple_test(tcase, "CloseCluster",
+ test_CloseCluster);
+ torture_tcase_add_simple_test(tcase, "SetClusterName",
+ test_SetClusterName);
+ torture_tcase_add_simple_test(tcase, "GetClusterName",
+ test_GetClusterName);
+ torture_tcase_add_simple_test(tcase, "GetClusterVersion",
+ test_GetClusterVersion);
+ torture_tcase_add_simple_test(tcase, "CreateEnum",
+ test_CreateEnum);
+ torture_tcase_add_simple_test(tcase, "CreateEnumEx",
+ test_CreateEnumEx);
+ torture_tcase_add_simple_test(tcase, "GetClusterVersion2",
+ test_GetClusterVersion2);
+ torture_tcase_add_simple_test(tcase, "BackupClusterDatabase",
+ test_BackupClusterDatabase);
+ torture_tcase_add_simple_test(tcase, "SetServiceAccountPassword",
+ test_SetServiceAccountPassword);
+ torture_tcase_add_simple_test(tcase, "ClusterControl",
+ test_ClusterControl);
+ torture_tcase_add_simple_test(tcase, "CreateResTypeEnum",
+ test_CreateResTypeEnum);
+ torture_tcase_add_simple_test(tcase, "CreateGroupEnum",
+ test_CreateGroupEnum);
+
+}
+
+void torture_tcase_resource(struct torture_tcase *tcase)
+{
+ struct torture_test *test;
+
+ torture_tcase_add_simple_test(tcase, "GetQuorumResource",
+ test_GetQuorumResource);
+ torture_tcase_add_simple_test(tcase, "SetQuorumResource",
+ test_SetQuorumResource);
+ torture_tcase_add_simple_test(tcase, "OpenResource",
+ test_OpenResource);
+ torture_tcase_add_simple_test(tcase, "OpenResourceEx",
+ test_OpenResourceEx);
+ torture_tcase_add_simple_test(tcase, "CloseResource",
+ test_CloseResource);
+ torture_tcase_add_simple_test(tcase, "CreateResource",
+ test_CreateResource);
+ torture_tcase_add_simple_test(tcase, "DeleteResource",
+ test_DeleteResource);
+ torture_tcase_add_simple_test(tcase, "SetResourceName",
+ test_SetResourceName);
+ torture_tcase_add_simple_test(tcase, "GetResourceState",
+ test_GetResourceState);
+ torture_tcase_add_simple_test(tcase, "GetResourceId",
+ test_GetResourceId);
+ torture_tcase_add_simple_test(tcase, "GetResourceType",
+ test_GetResourceType);
+ torture_tcase_add_simple_test(tcase, "CreateResEnum",
+ test_CreateResEnum);
+ test = torture_tcase_add_simple_test(tcase, "FailResource",
+ test_FailResource);
+ test->dangerous = true;
+ torture_tcase_add_simple_test(tcase, "OnlineResource",
+ test_OnlineResource);
+ test = torture_tcase_add_simple_test(tcase, "OfflineResource",
+ test_OfflineResource);
+ test->dangerous = true;
+ torture_tcase_add_simple_test(tcase, "GetResourceDependencyExpression",
+ test_GetResourceDependencyExpression);
+ torture_tcase_add_simple_test(tcase, "GetResourceNetworkName",
+ test_GetResourceNetworkName);
+ torture_tcase_add_simple_test(tcase, "all_resources",
+ test_all_resources);
+}
+
+void torture_tcase_resourcetype(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "all_resourcetypes",
+ test_all_resourcetypes);
+}
+
+void torture_tcase_node(struct torture_tcase *tcase)
+{
+ struct torture_test *test;
+
+ torture_tcase_add_simple_test(tcase, "OpenNode",
+ test_OpenNode);
+ torture_tcase_add_simple_test(tcase, "OpenNodeEx",
+ test_OpenNodeEx);
+ torture_tcase_add_simple_test(tcase, "CloseNode",
+ test_CloseNode);
+ torture_tcase_add_simple_test(tcase, "GetNodeState",
+ test_GetNodeState);
+ torture_tcase_add_simple_test(tcase, "GetNodeId",
+ test_GetNodeId);
+ torture_tcase_add_simple_test(tcase, "NodeControl",
+ test_NodeControl);
+ test = torture_tcase_add_simple_test(tcase, "PauseNode",
+ test_PauseNode);
+ test->dangerous = true;
+ torture_tcase_add_simple_test(tcase, "ResumeNode",
+ test_ResumeNode);
+ test = torture_tcase_add_simple_test(tcase, "EvictNode",
+ test_EvictNode);
+ test->dangerous = true;
+ torture_tcase_add_simple_test(tcase, "all_nodes",
+ test_all_nodes);
+}
+
+void torture_tcase_group(struct torture_tcase *tcase)
+{
+ struct torture_test *test;
+
+ torture_tcase_add_simple_test(tcase, "OpenGroup",
+ test_OpenGroup);
+ torture_tcase_add_simple_test(tcase, "OpenGroupEx",
+ test_OpenGroupEx);
+ torture_tcase_add_simple_test(tcase, "CloseGroup",
+ test_CloseGroup);
+ torture_tcase_add_simple_test(tcase, "GetGroupState",
+ test_GetGroupState);
+ torture_tcase_add_simple_test(tcase, "GetGroupId",
+ test_GetGroupId);
+ torture_tcase_add_simple_test(tcase, "GroupControl",
+ test_GroupControl);
+ torture_tcase_add_simple_test(tcase, "OnlineGroup",
+ test_OnlineGroup);
+ test = torture_tcase_add_simple_test(tcase, "OfflineGroup",
+ test_OfflineGroup);
+ test->dangerous = true;
+ torture_tcase_add_simple_test(tcase, "all_groups",
+ test_all_groups);
+}
+
+void torture_tcase_network(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "OpenNetwork",
+ test_OpenNetwork);
+ torture_tcase_add_simple_test(tcase, "OpenNetworkEx",
+ test_OpenNetworkEx);
+ torture_tcase_add_simple_test(tcase, "CloseNetwork",
+ test_CloseNetwork);
+ torture_tcase_add_simple_test(tcase, "GetNetworkState",
+ test_GetNetworkState);
+ torture_tcase_add_simple_test(tcase, "GetNetworkId",
+ test_GetNetworkId);
+ torture_tcase_add_simple_test(tcase, "all_networks",
+ test_all_networks);
+}
+
+void torture_tcase_netinterface(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "OpenNetInterface",
+ test_OpenNetInterface);
+ torture_tcase_add_simple_test(tcase, "OpenNetInterfaceEx",
+ test_OpenNetInterfaceEx);
+ torture_tcase_add_simple_test(tcase, "CloseNetInterface",
+ test_CloseNetInterface);
+ torture_tcase_add_simple_test(tcase, "GetNetInterfaceState",
+ test_GetNetInterfaceState);
+ torture_tcase_add_simple_test(tcase, "GetNetInterfaceId",
+ test_GetNetInterfaceId);
+ torture_tcase_add_simple_test(tcase, "all_netinterfaces",
+ test_all_netinterfaces);
+}
+
+void torture_tcase_registry(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "GetRootKey",
+ test_GetRootKey);
+ torture_tcase_add_simple_test(tcase, "CloseKey",
+ test_CloseKey);
+ torture_tcase_add_simple_test(tcase, "EnumKey",
+ test_EnumKey);
+ torture_tcase_add_simple_test(tcase, "QueryValue",
+ test_QueryValue);
+ torture_tcase_add_simple_test(tcase, "all_keys",
+ test_all_keys);
+}
+
+void torture_tcase_groupset(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "OpenGroupSet",
+ test_OpenGroupSet);
+ torture_tcase_add_simple_test(tcase, "CloseGroupSet",
+ test_CloseGroupSet);
+ torture_tcase_add_simple_test(tcase, "all_groupsets",
+ test_all_groupsets);
+}
+
+struct torture_suite *torture_rpc_clusapi(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "clusapi");
+
+ tcase = torture_suite_add_tcase(suite, "cluster");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_clusapi_setup,
+ torture_rpc_clusapi_teardown);
+
+ torture_tcase_cluster(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "resource");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_clusapi_setup,
+ torture_rpc_clusapi_teardown);
+
+ torture_tcase_resource(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "resourcetype");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_clusapi_setup,
+ torture_rpc_clusapi_teardown);
+
+ torture_tcase_resourcetype(tcase);
+
+
+ tcase = torture_suite_add_tcase(suite, "node");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_clusapi_setup,
+ torture_rpc_clusapi_teardown);
+
+ torture_tcase_node(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "group");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_clusapi_setup,
+ torture_rpc_clusapi_teardown);
+
+ torture_tcase_group(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "network");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_clusapi_setup,
+ torture_rpc_clusapi_teardown);
+
+ torture_tcase_network(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "netinterface");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_clusapi_setup,
+ torture_rpc_clusapi_teardown);
+
+ torture_tcase_netinterface(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "registry");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_clusapi_setup,
+ torture_rpc_clusapi_teardown);
+
+ torture_tcase_registry(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "groupset");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_clusapi_setup,
+ torture_rpc_clusapi_teardown);
+
+ torture_tcase_groupset(tcase);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/countcalls.c b/source4/torture/rpc/countcalls.c
new file mode 100644
index 0000000..52be979
--- /dev/null
+++ b/source4/torture/rpc/countcalls.c
@@ -0,0 +1,131 @@
+/*
+ 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 "librpc/ndr/libndr.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.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_IS_RPC(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_null;
+
+ printf("\nScanning pipe '%s'\n", iface->name);
+
+ for (i=0;i<500;i++) {
+ uint32_t out_flags = 0;
+
+ status = dcerpc_binding_handle_raw_call(p->binding_handle,
+ NULL, i,
+ 0, /* in_flags */
+ stub_in.data,
+ stub_in.length,
+ mem_ctx,
+ &stub_out.data,
+ &stub_out.length,
+ &out_flags);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ 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 = lpcfg_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 0000000..14af288
--- /dev/null
+++ b/source4/torture/rpc/dfs.c
@@ -0,0 +1,651 @@
+/*
+ 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/rpc/torture_rpc.h"
+#include "librpc/gen_ndr/ndr_dfs_c.h"
+#include "libnet/libnet.h"
+#include "torture/util.h"
+#include "libcli/libcli.h"
+#include "lib/cmdline/cmdline.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(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 = samba_cmdline_get_creds();
+
+ 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, tctx, &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(struct torture_context *tctx,
+ const char *host,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct libnet_context* libnetctx;
+ struct libnet_DelShare r;
+
+ torture_comment(tctx, "Deleting share %s\n", sharename);
+
+ if (!(libnetctx = libnet_context_init(tctx->ev, tctx->lp_ctx))) {
+ return false;
+ }
+
+ libnetctx->cred = samba_cmdline_get_creds();
+
+ r.in.share_name = sharename;
+ r.in.server_name = host;
+
+ status = libnet_DelShare(libnetctx, tctx, &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 torture_context *tctx,
+ struct smbcli_state *cli,
+ const char *dir)
+{
+ torture_comment(tctx, "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_opts(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ enum dfs_ManagerVersion *version_p)
+{
+ struct dfs_GetManagerVersion r;
+ enum dfs_ManagerVersion version;
+
+ r.out.version = &version;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_GetManagerVersion_r(b, tctx, &r),
+ "GetManagerVersion failed");
+
+ if (version_p) {
+ *version_p = version;
+ }
+
+ return true;
+}
+
+
+static bool test_GetManagerVersion(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_GetManagerVersion_opts(tctx, b, NULL);
+}
+
+static bool test_ManagerInitialize(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ enum dfs_ManagerVersion version;
+ struct dfs_ManagerInitialize r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *host = torture_setting_string(tctx, "host", NULL);
+
+ torture_comment(tctx, "Testing ManagerInitialize\n");
+
+ torture_assert(tctx,
+ test_GetManagerVersion_opts(tctx, b, &version),
+ "GetManagerVersion failed");
+
+ r.in.servername = host;
+ r.in.flags = 0;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_ManagerInitialize_r(b, tctx, &r),
+ "ManagerInitialize failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_warning(tctx, "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 torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint16_t level,
+ const char *root)
+{
+ struct dfs_GetInfo r;
+ union dfs_Info info;
+
+ torture_comment(tctx, "Testing GetInfo level %u on '%s'\n", level, root);
+
+ r.in.dfs_entry_path = root;
+ r.in.servername = NULL;
+ r.in.sharename = NULL;
+ r.in.level = level;
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_GetInfo_r(b, tctx, &r),
+ "GetInfo failed");
+
+ if (!W_ERROR_IS_OK(r.out.result) &&
+ !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) {
+ torture_warning(tctx, "dfs_GetInfo failed - %s\n", win_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_GetInfo(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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(tctx, b, levels[i], root)) {
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+static bool test_EnumLevelEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint16_t level,
+ const char *dfs_name)
+{
+ 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;
+
+ torture_comment(tctx, "Testing EnumEx level %u on '%s'\n", level, dfs_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_EnumEx_r(b, tctx, &rex),
+ "EnumEx failed");
+ torture_assert_werr_ok(tctx, rex.out.result,
+ "EnumEx failed");
+
+ if (level == 1 && rex.out.total) {
+ int i;
+ for (i=0;i<*rex.out.total;i++) {
+ const char *root = rex.out.info->e.info1->s[i].path;
+ if (!test_GetInfo(tctx, b, 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 = rex.out.info->e.info300->s[i].dom_root;
+ for (k=0;k<ARRAY_SIZE(levels);k++) {
+ if (!test_EnumLevelEx(tctx, b,
+ levels[k], root))
+ {
+ ret = false;
+ }
+ }
+ if (!test_GetInfo(tctx, b, root)) {
+ ret = false;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_EnumLevel(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint16_t level)
+{
+ 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;
+
+ torture_comment(tctx, "Testing Enum level %u\n", level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_Enum_r(b, tctx, &r),
+ "Enum failed");
+
+ if (!W_ERROR_IS_OK(r.out.result) &&
+ !W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, r.out.result)) {
+ torture_warning(tctx, "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(tctx, b, root)) {
+ ret = false;
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_Enum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 200, 300};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (!test_EnumLevel(tctx, b, levels[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_EnumEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 200, 300};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *host = torture_setting_string(tctx, "host", NULL);
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ if (!test_EnumLevelEx(tctx, b, levels[i], host)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_RemoveStdRoot(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *host,
+ const char *sharename)
+{
+ struct dfs_RemoveStdRoot r;
+
+ torture_comment(tctx, "Testing RemoveStdRoot\n");
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+ r.in.flags = 0;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_RemoveStdRoot_r(b, tctx, &r),
+ "RemoveStdRoot failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "dfs_RemoveStdRoot failed");
+
+ return true;
+}
+
+static bool test_AddStdRoot(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *host,
+ const char *sharename)
+{
+ struct dfs_AddStdRoot r;
+
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_AddStdRoot_r(b, tctx, &r),
+ "AddStdRoot failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "AddStdRoot failed");
+
+ return true;
+}
+
+static bool test_AddStdRootForced(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *host,
+ const char *sharename)
+{
+ struct dfs_AddStdRootForced r;
+ enum dfs_ManagerVersion version;
+
+ torture_comment(tctx, "Testing AddStdRootForced\n");
+
+ torture_assert(tctx,
+ test_GetManagerVersion_opts(tctx, b, &version),
+ "GetManagerVersion failed");
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_AddStdRootForced_r(b, tctx, &r),
+ "AddStdRootForced failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_warning(tctx, "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(tctx, b, host, sharename);
+}
+
+static void test_cleanup_stdroot(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *host,
+ const char *sharename,
+ const char *dir)
+{
+ struct smbcli_state *cli;
+
+ torture_comment(tctx, "Cleaning up StdRoot\n");
+
+ test_RemoveStdRoot(tctx, b, host, sharename);
+ test_NetShareDel(tctx, host, sharename);
+ if (torture_open_connection_share(tctx, &cli, tctx, host, "C$", tctx->ev)) {
+ test_DeleteDir(tctx, cli, dir);
+ torture_close_connection(cli);
+ }
+}
+
+static bool test_StdRoot(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ 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;
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing StdRoot\n");
+
+ test_cleanup_stdroot(tctx, b, host, sharename, dir);
+
+ torture_assert(tctx,
+ test_CreateDir(tctx, &cli, tctx, host, "C$", dir),
+ "failed to connect C$ share and to create directory");
+ torture_assert(tctx,
+ test_NetShareAdd(tctx, host, sharename, path),
+ "failed to create new share");
+
+ ret &= test_AddStdRoot(tctx, b, host, sharename);
+ ret &= test_RemoveStdRoot(tctx, b, host, sharename);
+ ret &= test_AddStdRootForced(tctx, b, host, sharename);
+ ret &= test_NetShareDel(tctx, host, sharename);
+ ret &= test_DeleteDir(tctx, cli, dir);
+
+ torture_close_connection(cli);
+
+ return ret;
+}
+
+static bool test_GetDcAddress(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *host)
+{
+ struct dfs_GetDcAddress r;
+ uint8_t is_root = 0;
+ uint32_t ttl = 0;
+ const char *ptr;
+
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_GetDcAddress_r(b, tctx, &r),
+ "GetDcAddress failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "dfs_GetDcAddress failed");
+
+ return true;
+}
+
+static bool test_SetDcAddress(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *host)
+{
+ struct dfs_SetDcAddress r;
+
+ torture_comment(tctx, "Testing SetDcAddress\n");
+
+ r.in.servername = host;
+ r.in.server_fullname = host;
+ r.in.flags = 0;
+ r.in.ttl = 1000;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_SetDcAddress_r(b, tctx, &r),
+ "SetDcAddress failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "dfs_SetDcAddress failed");
+
+ return true;
+}
+
+static bool test_DcAddress(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_GetDcAddress(tctx, b, host)) {
+ return false;
+ }
+
+ if (!test_SetDcAddress(tctx, b, host)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_FlushFtTable(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *host,
+ const char *sharename)
+{
+ struct dfs_FlushFtTable r;
+ enum dfs_ManagerVersion version;
+
+ torture_comment(tctx, "Testing FlushFtTable\n");
+
+ torture_assert(tctx,
+ test_GetManagerVersion_opts(tctx, b, &version),
+ "GetManagerVersion failed");
+
+ r.in.servername = host;
+ r.in.rootshare = sharename;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_dfs_FlushFtTable_r(b, tctx, &r),
+ "FlushFtTable failed");
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_warning(tctx, "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 torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ const char *sharename = SMBTORTURE_DFS_SHARENAME;
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_FlushFtTable(tctx, b, host, sharename);
+}
+
+struct torture_suite *torture_rpc_dfs(TALLOC_CTX *mem_ctx)
+{
+ struct torture_rpc_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "dfs");
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "netdfs",
+ &ndr_table_netdfs);
+
+ torture_rpc_tcase_add_test(tcase, "GetManagerVersion", test_GetManagerVersion);
+ torture_rpc_tcase_add_test(tcase, "ManagerInitialize", test_ManagerInitialize);
+ torture_rpc_tcase_add_test(tcase, "Enum", test_Enum);
+ torture_rpc_tcase_add_test(tcase, "EnumEx", test_EnumEx);
+ torture_rpc_tcase_add_test(tcase, "StdRoot", test_StdRoot);
+ torture_rpc_tcase_add_test(tcase, "FtRoot", test_FtRoot);
+ torture_rpc_tcase_add_test(tcase, "DcAddress", test_DcAddress);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/drsuapi.c b/source4/torture/rpc/drsuapi.c
new file mode 100644
index 0000000..d3e18ca
--- /dev/null
+++ b/source4/torture/rpc/drsuapi.c
@@ -0,0 +1,1049 @@
+/*
+ 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 "lib/cmdline/cmdline.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include "libcli/security/dom_sid.h"
+#include "param/param.h"
+
+#define TEST_MACHINE_NAME "torturetest"
+
+static bool test_DsBind(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *bind_handle,
+ struct drsuapi_DsBindInfo28 *srv_info28)
+{
+ NTSTATUS status;
+ struct drsuapi_DsBind r;
+ struct GUID bind_guid;
+ struct drsuapi_DsBindInfo28 *bind_info28;
+ struct drsuapi_DsBindInfoCtr bind_info_ctr;
+
+ ZERO_STRUCT(bind_info_ctr);
+ bind_info_ctr.length = 28;
+
+ bind_info28 = &bind_info_ctr.info.info28;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
+ bind_info28->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &bind_guid);
+
+ r.in.bind_guid = &bind_guid;
+ r.in.bind_info = &bind_info_ctr;
+ r.out.bind_handle = bind_handle;
+
+ torture_comment(tctx, "Testing DsBind\n");
+
+ status = dcerpc_drsuapi_DsBind_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsBind");
+
+ if (srv_info28 != NULL) {
+ *srv_info28 = r.out.bind_info->info.info28;
+ }
+
+ return true;
+}
+
+static bool test_DsGetDomainControllerInfo(struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ struct drsuapi_DsGetDomainControllerInfo r;
+ union drsuapi_DsGetDCInfoCtr ctr;
+ union drsuapi_DsGetDCInfoRequest req;
+ int32_t level_out = 0;
+ 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 = &req;
+
+ r.in.req->req1.domain_name = names[j].name;
+ r.in.req->req1.level = level;
+
+ r.out.ctr = &ctr;
+ r.out.level_out = &level_out;
+
+ torture_comment(tctx,
+ "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
+ r.in.req->req1.level, r.in.req->req1.domain_name);
+
+ status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(p->binding_handle, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
+ torture_assert_werr_equal(tctx,
+ 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(tctx,
+ r.in.req->req1.level, *r.out.level_out,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo in/out level differs");
+
+ 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(tctx, 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.out.ctr = &ctr;
+ r.out.level_out = &level_out;
+
+ r.in.req->req1.domain_name = "__UNKNOWN_DOMAIN__"; /* This is clearly ignored for this level */
+ r.in.req->req1.level = -1;
+
+ torture_comment(tctx, "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
+ r.in.req->req1.level, r.in.req->req1.domain_name);
+
+ status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(p->binding_handle, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "DsGetDomainControllerInfo with dns domain failed");
+
+ {
+ const char *dc_account = talloc_asprintf(tctx, "%s\\%s$",
+ torture_join_dom_netbios_name(priv->join),
+ priv->dcinfo.netbios_name);
+ torture_comment(tctx, "%s: Enum active LDAP sessions searching for %s\n", __func__, dc_account);
+ 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(tctx, found,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo level: Failed to find the domain controller in last logon records");
+ }
+
+
+ return true;
+}
+
+static bool test_DsWriteAccountSpn(struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ struct drsuapi_DsWriteAccountSpn r;
+ union drsuapi_DsWriteAccountSpnRequest req;
+ struct drsuapi_DsNameString names[2];
+ union drsuapi_DsWriteAccountSpnResult res;
+ uint32_t level_out;
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+ r.in.req = &req;
+
+ torture_comment(tctx, "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(tctx, "smbtortureSPN/%s",priv->dcinfo.netbios_name);
+ names[1].str = talloc_asprintf(tctx, "smbtortureSPN/%s",priv->dcinfo.dns_name);
+
+ r.out.res = &res;
+ r.out.level_out = &level_out;
+
+ status = dcerpc_drsuapi_DsWriteAccountSpn_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsWriteAccountSpn");
+
+ r.in.req->req1.operation = DRSUAPI_DS_SPN_OPERATION_DELETE;
+ r.in.req->req1.unknown1 = 0;
+
+ status = dcerpc_drsuapi_DsWriteAccountSpn_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsWriteAccountSpn");
+
+ return true;
+}
+
+static bool test_DsReplicaGetInfo(struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ struct drsuapi_DsReplicaGetInfo r;
+ union drsuapi_DsReplicaGetInfoRequest req;
+ union drsuapi_DsReplicaInfo info;
+ enum drsuapi_DsReplicaInfoType info_type;
+ 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_REPSTO,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_CLIENT_CONTEXTS,
+ "__IGNORED__"
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_UPTODATE_VECTOR_V1,
+ NULL
+ },{
+ DRSUAPI_DS_REPLICA_GET_INFO2,
+ DRSUAPI_DS_REPLICA_INFO_SERVER_OUTGOING_CALLS,
+ NULL
+ }
+ };
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "skipping DsReplicaGetInfo test against Samba4\n");
+ return true;
+ }
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.req = &req;
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ const char *object_dn;
+
+ torture_comment(tctx, "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.source_dsa_guid);
+ 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->req2.source_dsa_guid);
+ r.in.req->req2.flags = 0;
+ r.in.req->req2.attribute_name = NULL;
+ r.in.req->req2.value_dn_str = NULL;
+ r.in.req->req2.enumeration_context = 0;
+ break;
+ }
+
+ r.out.info = &info;
+ r.out.info_type = &info_type;
+
+ status = dcerpc_drsuapi_DsReplicaGetInfo_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaGetInfo");
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) {
+ torture_comment(tctx,
+ "DsReplicaGetInfo level %d and/or infotype %d not supported by server\n",
+ array[i].level, array[i].infotype);
+ } else {
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaGetInfo");
+ }
+ }
+
+ return true;
+}
+
+static bool test_DsReplicaSync(struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ int i;
+ struct drsuapi_DsReplicaSync r;
+ union drsuapi_DsReplicaSyncRequest sync_req;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct dom_sid null_sid;
+ struct {
+ int32_t level;
+ } array[] = {
+ {
+ 1
+ }
+ };
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ torture_comment(tctx, "DsReplicaSync disabled - enable dangerous tests to use\n");
+ return true;
+ }
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "skipping DsReplicaSync test against Samba4\n");
+ return true;
+ }
+
+ ZERO_STRUCT(null_sid);
+
+ r.in.bind_handle = &priv->bind_handle;
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ torture_comment(tctx, "Testing DsReplicaSync level %d\n",
+ array[i].level);
+
+ r.in.level = array[i].level;
+ switch(r.in.level) {
+ case 1:
+ nc.guid = GUID_zero();
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn?priv->domain_obj_dn:"";
+
+ sync_req.req1.naming_context = &nc;
+ sync_req.req1.source_dsa_guid = priv->dcinfo.ntds_guid;
+ sync_req.req1.source_dsa_dns = NULL;
+ sync_req.req1.options = 16;
+
+ r.in.req = &sync_req;
+ break;
+ }
+
+ status = dcerpc_drsuapi_DsReplicaSync_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaSync");
+ }
+
+ return true;
+}
+
+static bool test_DsReplicaUpdateRefs(struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ struct drsuapi_DsReplicaUpdateRefs r;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct GUID dest_dsa_guid;
+ const char *dest_dsa_guid_str;
+ struct dom_sid null_sid;
+
+ ZERO_STRUCT(null_sid);
+ dest_dsa_guid = GUID_random();
+ dest_dsa_guid_str = GUID_string(tctx, &dest_dsa_guid);
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1; /* Only version 1 is defined presently */
+
+ /* setup NC */
+ nc.guid = priv->domain_obj_dn ? GUID_zero():priv->domain_guid;
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn ? priv->domain_obj_dn : "";
+
+ /* default setup for request */
+ r.in.req.req1.naming_context = &nc;
+ r.in.req.req1.dest_dsa_dns_name = talloc_asprintf(tctx, "%s._msdn.%s",
+ dest_dsa_guid_str,
+ priv->domain_dns_name);
+ r.in.req.req1.dest_dsa_guid = dest_dsa_guid;
+
+ /* 1. deleting replica dest should fail */
+ torture_comment(tctx, "delete: %s\n", r.in.req.req1.dest_dsa_dns_name);
+ r.in.req.req1.options = DRSUAPI_DRS_DEL_REF;
+ status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call_werr(tctx, p,
+ status, WERR_DS_DRA_REF_NOT_FOUND, &r,
+ "dcerpc_drsuapi_DsReplicaUpdateRefs");
+
+ /* 2. hopefully adding random replica dest should succeed */
+ torture_comment(tctx, "add : %s\n", r.in.req.req1.dest_dsa_dns_name);
+ r.in.req.req1.options = DRSUAPI_DRS_ADD_REF;
+ status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call_werr(tctx, p,
+ status, WERR_OK, &r,
+ "dcerpc_drsuapi_DsReplicaUpdateRefs");
+
+ /* 3. try adding same replica dest - should fail */
+ torture_comment(tctx, "add : %s\n", r.in.req.req1.dest_dsa_dns_name);
+ r.in.req.req1.options = DRSUAPI_DRS_ADD_REF;
+ status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call_werr(tctx, p,
+ status, WERR_DS_DRA_REF_ALREADY_EXISTS, &r,
+ "dcerpc_drsuapi_DsReplicaUpdateRefs");
+
+ /* 4. try resetting same replica dest - should succeed */
+ torture_comment(tctx, "reset : %s\n", r.in.req.req1.dest_dsa_dns_name);
+ r.in.req.req1.options = DRSUAPI_DRS_DEL_REF | DRSUAPI_DRS_ADD_REF;
+ status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call_werr(tctx, p,
+ status, WERR_OK, &r,
+ "dcerpc_drsuapi_DsReplicaUpdateRefs");
+
+ /* 5. delete random replicate added at step 2. */
+ torture_comment(tctx, "delete : %s\n", r.in.req.req1.dest_dsa_dns_name);
+ r.in.req.req1.options = DRSUAPI_DRS_DEL_REF;
+ status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call_werr(tctx, p,
+ status, WERR_OK, &r,
+ "dcerpc_drsuapi_DsReplicaUpdateRefs");
+
+ /* 6. try replace on non-existing replica dest - should succeed */
+ torture_comment(tctx, "replace: %s\n", r.in.req.req1.dest_dsa_dns_name);
+ r.in.req.req1.options = DRSUAPI_DRS_DEL_REF | DRSUAPI_DRS_ADD_REF;
+ status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call_werr(tctx, p,
+ status, WERR_OK, &r,
+ "dcerpc_drsuapi_DsReplicaUpdateRefs");
+
+ /* 7. delete random replicate added at step 6. */
+ torture_comment(tctx, "delete : %s\n", r.in.req.req1.dest_dsa_dns_name);
+ r.in.req.req1.options = DRSUAPI_DRS_DEL_REF;
+ status = dcerpc_drsuapi_DsReplicaUpdateRefs_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call_werr(tctx, p,
+ status, WERR_OK, &r,
+ "dcerpc_drsuapi_DsReplicaUpdateRefs");
+
+ return true;
+}
+
+static bool test_DsGetNCChanges(struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ int i;
+ struct drsuapi_DsGetNCChanges r;
+ union drsuapi_DsGetNCChangesRequest req;
+ union drsuapi_DsGetNCChangesCtr ctr;
+ struct drsuapi_DsReplicaObjectIdentifier nc;
+ struct dom_sid null_sid;
+ uint32_t level_out;
+ struct {
+ uint32_t level;
+ } array[] = {
+ {
+ 5
+ },
+ {
+ 8
+ }
+ };
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "skipping DsGetNCChanges test against Samba4\n");
+ return true;
+ }
+
+ ZERO_STRUCT(null_sid);
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ torture_comment(tctx,
+ "Testing DsGetNCChanges level %d\n",
+ array[i].level);
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = array[i].level;
+ r.out.level_out = &level_out;
+ r.out.ctr = &ctr;
+
+ switch (r.in.level) {
+ case 5:
+ nc.guid = GUID_zero();
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn ? priv->domain_obj_dn : "";
+
+ r.in.req = &req;
+ r.in.req->req5.destination_dsa_guid = GUID_random();
+ r.in.req->req5.source_dsa_invocation_id = GUID_zero();
+ 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 (lpcfg_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "compression", false)) {
+ r.in.req->req5.replica_flags |= DRSUAPI_DRS_USE_COMPRESSION;
+ }
+ 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 = GUID_zero();
+ nc.sid = null_sid;
+ nc.dn = priv->domain_obj_dn ? priv->domain_obj_dn : "";
+
+ r.in.req = &req;
+ r.in.req->req8.destination_dsa_guid = GUID_random();
+ r.in.req->req8.source_dsa_invocation_id = GUID_zero();
+ 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 (lpcfg_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "compression", false)) {
+ r.in.req->req8.replica_flags |= DRSUAPI_DRS_USE_COMPRESSION;
+ }
+ if (lpcfg_parm_bool(tctx->lp_ctx, NULL, "drsuapi", "neighbour_writeable", true)) {
+ r.in.req->req8.replica_flags |= DRSUAPI_DRS_WRIT_REP;
+ }
+ r.in.req->req8.replica_flags |= DRSUAPI_DRS_INIT_SYNC
+ | DRSUAPI_DRS_PER_SYNC
+ | DRSUAPI_DRS_GET_ANC
+ | DRSUAPI_DRS_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_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsGetNCChanges");
+ }
+
+ return true;
+}
+
+bool test_QuerySitesByCost(struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ struct drsuapi_QuerySitesByCost r;
+ union drsuapi_QuerySitesByCostRequest req;
+
+ const char *my_site = "Default-First-Site-Name";
+ const char *remote_site1 = "smbtorture-nonexisting-site1";
+ const char *remote_site2 = "smbtorture-nonexisting-site2";
+
+ req.req1.site_from = talloc_strdup(tctx, my_site);
+ req.req1.num_req = 2;
+ req.req1.site_to = talloc_zero_array(tctx, const char *, 2);
+ req.req1.site_to[0] = talloc_strdup(tctx, remote_site1);
+ req.req1.site_to[1] = talloc_strdup(tctx, remote_site2);
+ req.req1.flags = 0;
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+ r.in.req = &req;
+
+ status = dcerpc_drsuapi_QuerySitesByCost_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_QuerySitesByCost");
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+ torture_assert_werr_equal(tctx,
+ r.out.ctr->ctr1.info[0].error_code, WERR_DS_OBJ_NOT_FOUND,
+ "dcerpc_drsuapi_QuerySitesByCost");
+ torture_assert_werr_equal(tctx,
+ r.out.ctr->ctr1.info[1].error_code, WERR_DS_OBJ_NOT_FOUND,
+ "dcerpc_drsuapi_QuerySitesByCost expected error_code WERR_DS_OBJ_NOT_FOUND");
+
+ torture_assert_int_equal(tctx,
+ r.out.ctr->ctr1.info[0].site_cost, -1,
+ "dcerpc_drsuapi_QuerySitesByCost");
+ torture_assert_int_equal(tctx,
+ r.out.ctr->ctr1.info[1].site_cost, -1,
+ "dcerpc_drsuapi_QuerySitesByCost exptected site cost");
+ }
+
+ return true;
+
+
+}
+
+bool test_DsUnbind(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ struct drsuapi_DsUnbind r;
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.out.bind_handle = &priv->bind_handle;
+
+ torture_comment(tctx, "Testing DsUnbind\n");
+
+ status = dcerpc_drsuapi_DsUnbind_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsUnbind");
+
+ return true;
+}
+
+
+/**
+ * Helper func to collect DC information for testing purposes.
+ * This function is almost identical to test_DsGetDomainControllerInfo
+ */
+bool torture_rpc_drsuapi_get_dcinfo(struct torture_context *torture,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ int32_t level_out = 0;
+ struct drsuapi_DsGetDomainControllerInfo r;
+ union drsuapi_DsGetDCInfoCtr ctr;
+ int j, k;
+ const char *names[] = {
+ torture_join_dom_netbios_name(priv->join),
+ torture_join_dom_dns_name(priv->join)};
+
+ for (j=0; j < ARRAY_SIZE(names); j++) {
+ union drsuapi_DsGetDCInfoRequest req;
+ struct dcerpc_binding_handle *b = priv->drs_pipe->binding_handle;
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+ r.in.req = &req;
+
+ r.in.req->req1.domain_name = names[j];
+ r.in.req->req1.level = 2;
+
+ r.out.ctr = &ctr;
+ r.out.level_out = &level_out;
+
+ status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(b, torture, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ /* If this was an error, we can't read the result structure */
+ continue;
+ }
+
+ 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) {
+ priv->dcinfo = r.out.ctr->ctr2.array[k];
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Common test case setup function to be used
+ * in DRS suit of test when appropriate
+ */
+bool torture_drsuapi_tcase_setup_common(struct torture_context *tctx, struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ int rnd = rand() % 1000;
+ char *name = talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, rnd);
+
+ torture_assert(tctx, priv, "Invalid argument");
+
+ priv->admin_credentials = samba_cmdline_get_creds();
+
+ torture_comment(tctx, "Create DRSUAPI pipe\n");
+ status = torture_rpc_connection(tctx,
+ &priv->drs_pipe,
+ &ndr_table_drsuapi);
+ torture_assert(tctx, NT_STATUS_IS_OK(status), "Unable to connect to DRSUAPI pipe");
+
+ torture_comment(tctx, "About to join domain with name %s\n", name);
+ priv->join = torture_join_domain(tctx, name, ACB_SVRTRUST,
+ &priv->dc_credentials);
+ torture_assert(tctx, priv->join, "Failed to join as BDC");
+
+ if (!test_DsBind(priv->drs_pipe, tctx,
+ &priv->bind_handle,
+ &priv->srv_bind_info))
+ {
+ /* clean up */
+ torture_drsuapi_tcase_teardown_common(tctx, priv);
+ torture_fail(tctx, "Failed execute test_DsBind()");
+ }
+
+ /* try collect some information for testing */
+ torture_rpc_drsuapi_get_dcinfo(tctx, priv);
+
+ return true;
+}
+
+/**
+ * Common test case teardown function to be used
+ * in DRS suit of test when appropriate
+ */
+bool torture_drsuapi_tcase_teardown_common(struct torture_context *tctx, struct DsPrivate *priv)
+{
+ if (priv->join) {
+ torture_leave_domain(tctx, priv->join);
+ }
+
+ return true;
+}
+
+/**
+ * Test case setup for DRSUAPI test case
+ */
+static bool torture_drsuapi_tcase_setup(struct torture_context *tctx, void **data)
+{
+ struct DsPrivate *priv;
+
+ *data = priv = talloc_zero(tctx, struct DsPrivate);
+
+ return torture_drsuapi_tcase_setup_common(tctx, priv);
+}
+
+/**
+ * Test case tear-down for DRSUAPI test case
+ */
+static bool torture_drsuapi_tcase_teardown(struct torture_context *tctx, void *data)
+{
+ bool ret;
+ struct DsPrivate *priv = talloc_get_type(data, struct DsPrivate);
+
+ ret = torture_drsuapi_tcase_teardown_common(tctx, priv);
+
+ talloc_free(priv);
+ return ret;
+}
+
+static bool __test_DsBind_assoc_group(struct torture_context *tctx,
+ const char *testname,
+ struct DsPrivate *priv,
+ struct cli_credentials *creds)
+{
+ NTSTATUS status;
+ const char *err_msg;
+ struct drsuapi_DsCrackNames r;
+ union drsuapi_DsNameRequest req;
+ uint32_t level_out;
+ union drsuapi_DsNameCtr ctr;
+ struct drsuapi_DsNameString names[1];
+ const char *dom_sid = NULL;
+ struct dcerpc_pipe *p1 = NULL;
+ struct dcerpc_pipe *p2 = NULL;
+ TALLOC_CTX *mem_ctx = priv;
+ struct dcerpc_binding *binding = NULL;
+ struct policy_handle ds_bind_handle = { .handle_type = 0, };
+
+ torture_comment(tctx, "%s: starting...\n", testname);
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_binding(tctx, &binding),
+ "torture_rpc_binding");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx,
+ &p1,
+ binding,
+ &ndr_table_drsuapi,
+ creds,
+ tctx->ev,
+ tctx->lp_ctx),
+ "connect p1");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx,
+ &p2,
+ p1->binding,
+ &ndr_table_drsuapi,
+ creds,
+ tctx->ev,
+ tctx->lp_ctx),
+ "connect p2");
+
+ torture_assert(tctx, test_DsBind(p1, tctx, &ds_bind_handle, NULL), "DsBind");
+
+ ZERO_STRUCT(r);
+ r.in.bind_handle = &ds_bind_handle;
+ r.in.level = 1;
+ r.in.req = &req;
+ r.in.req->req1.codepage = 1252; /* german */
+ r.in.req->req1.language = 0x00000407; /* german */
+ r.in.req->req1.count = 1;
+ r.in.req->req1.names = names;
+ r.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
+
+ r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
+ r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+
+ r.out.level_out = &level_out;
+ r.out.ctr = &ctr;
+
+ dom_sid = dom_sid_string(mem_ctx, torture_join_sid(priv->join));
+
+ names[0].str = dom_sid;
+
+ torture_comment(tctx, "Testing DsCrackNames on p1 with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+ status = dcerpc_drsuapi_DsCrackNames_r(p1->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ torture_comment(tctx, "Testing DsCrackNames on p2 with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+ status = dcerpc_drsuapi_DsCrackNames_r(p2->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ TALLOC_FREE(p1);
+
+ torture_comment(tctx, "Testing DsCrackNames on p2 (with p1 closed) with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+ status = dcerpc_drsuapi_DsCrackNames_r(p2->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ torture_comment(tctx, "%s: ... finished\n", testname);
+ return true;
+}
+
+static bool test_DsBindAssocGroupAdmin(struct torture_context *tctx,
+ struct DsPrivate *priv,
+ struct cli_credentials *creds)
+{
+ return __test_DsBind_assoc_group(tctx, __func__, priv,
+ priv->admin_credentials);
+}
+
+static bool test_DsBindAssocGroupDC(struct torture_context *tctx,
+ struct DsPrivate *priv,
+ struct cli_credentials *creds)
+{
+ return __test_DsBind_assoc_group(tctx, __func__, priv,
+ priv->dc_credentials);
+}
+
+static bool test_DsBindAssocGroupWS(struct torture_context *tctx,
+ struct DsPrivate *priv,
+ struct cli_credentials *creds)
+{
+ struct test_join *wks_join = NULL;
+ struct cli_credentials *wks_credentials = NULL;
+ int rnd = rand() % 1000;
+ char *wks_name = talloc_asprintf(tctx, "WKS%s%d", TEST_MACHINE_NAME, rnd);
+ bool ret;
+
+ torture_comment(tctx, "%s: About to join workstation with name %s\n",
+ __func__, wks_name);
+ wks_join = torture_join_domain(tctx, wks_name, ACB_WSTRUST,
+ &wks_credentials);
+ torture_assert(tctx, wks_join, "Failed to join as WORKSTATION");
+ ret = __test_DsBind_assoc_group(tctx, __func__, priv,
+ wks_credentials);
+ torture_leave_domain(tctx, wks_join);
+ return ret;
+}
+
+/**
+ * DRSUAPI test case implementation
+ */
+void torture_rpc_drsuapi_tcase(struct torture_suite *suite)
+{
+ typedef bool (*run_func) (struct torture_context *test, void *tcase_data);
+
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "drsuapi");
+
+ torture_tcase_set_fixture(tcase, torture_drsuapi_tcase_setup,
+ torture_drsuapi_tcase_teardown);
+
+#if 0
+ test = torture_tcase_add_simple_test(tcase, "QuerySitesByCost", (run_func)test_QuerySitesByCost);
+#endif
+
+ torture_tcase_add_simple_test(tcase, "DsGetDomainControllerInfo", (run_func)test_DsGetDomainControllerInfo);
+
+ torture_tcase_add_simple_test(tcase, "DsCrackNames", (run_func)test_DsCrackNames);
+
+ torture_tcase_add_simple_test(tcase, "DsWriteAccountSpn", (run_func)test_DsWriteAccountSpn);
+
+ torture_tcase_add_simple_test(tcase, "DsReplicaGetInfo", (run_func)test_DsReplicaGetInfo);
+
+ torture_tcase_add_simple_test(tcase, "DsReplicaSync", (run_func)test_DsReplicaSync);
+
+ torture_tcase_add_simple_test(tcase, "DsReplicaUpdateRefs", (run_func)test_DsReplicaUpdateRefs);
+
+ torture_tcase_add_simple_test(tcase, "DsGetNCChanges", (run_func)test_DsGetNCChanges);
+
+ torture_tcase_add_simple_test(tcase, "DsBindAssocGroupAdmin", (run_func)test_DsBindAssocGroupAdmin);
+ torture_tcase_add_simple_test(tcase, "DsBindAssocGroupDC", (run_func)test_DsBindAssocGroupDC);
+ torture_tcase_add_simple_test(tcase, "DsBindAssocGroupWS", (run_func)test_DsBindAssocGroupWS);
+}
diff --git a/source4/torture/rpc/drsuapi.h b/source4/torture/rpc/drsuapi.h
new file mode 100644
index 0000000..3cc4be4
--- /dev/null
+++ b/source4/torture/rpc/drsuapi.h
@@ -0,0 +1,94 @@
+/*
+ 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"
+
+/**
+ * Data structure common for most of DRSUAPI tests
+ */
+struct DsPrivate {
+ struct cli_credentials *admin_credentials;
+ struct dcerpc_pipe *drs_pipe;
+ struct policy_handle bind_handle;
+ struct drsuapi_DsBindInfo28 srv_bind_info;
+
+ 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;
+ struct cli_credentials *dc_credentials;
+};
+
+/**
+ * Data structure of DRSUAPI W2K8 tests
+ * W2K8 Clients use different versions of structs
+ */
+struct DsPrivate_w2k8 {
+ struct dcerpc_pipe *drs_pipe;
+ struct policy_handle bind_handle;
+ struct GUID bind_guid;
+ struct drsuapi_DsBindInfoCtr srv_bind_info;
+
+ const char *domain_obj_dn;
+ const char *domain_guid_str;
+ const char *domain_dns_name;
+ struct GUID domain_guid;
+ struct drsuapi_DsGetDCInfo3 dcinfo;
+ struct test_join *join;
+};
+
+
+/**
+ * Custom torture macro to check dcerpc_drsuapi_ call
+ * return values printing more friendly messages
+ * \param _tctx torture context
+ * \param _p DCERPC pipe handle
+ * \param _ntstatus NTSTATUS for dcerpc_drsuapi_ call
+ * \param _werr_expected Expected windows error to be returned
+ * \param _pr in/out DCEPRC request structure - pointer
+ * \param _msg error message prefix
+ */
+#define torture_drsuapi_assert_call_werr(_tctx, _p, _ntstat, _werr_expected, _pr, _msg) \
+ do { \
+ NTSTATUS __nt = _ntstat; \
+ if (!NT_STATUS_IS_OK(__nt)) { \
+ const char *errstr = nt_errstr(__nt); \
+ torture_fail(tctx, talloc_asprintf(_tctx, "%s failed - %s", _msg, errstr)); \
+ } \
+ torture_assert_werr_equal(_tctx, (_pr)->out.result, _werr_expected, _msg); \
+ } while(0)
+
+/**
+ * Custom torture macro to check dcerpc_drsuapi_ call
+ * return values printing more friendly messages
+ * \param _tctx torture context
+ * \param _p DCERPC pipe handle
+ * \param _ntstatus NTSTATUS for dcerpc_drsuapi_ call
+ * \param _pr in/out DCEPRC request structure
+ * \param _msg error message prefix
+ */
+#define torture_drsuapi_assert_call(_tctx, _p, _ntstat, _pr, _msg) \
+ torture_drsuapi_assert_call_werr(_tctx, _p, _ntstat, WERR_OK, _pr, _msg)
+
diff --git a/source4/torture/rpc/drsuapi_cracknames.c b/source4/torture/rpc/drsuapi_cracknames.c
new file mode 100644
index 0000000..511309f
--- /dev/null
+++ b/source4/torture/rpc/drsuapi_cracknames.c
@@ -0,0 +1,1087 @@
+/*
+ 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 "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include <ldb.h>
+#include "libcli/security/security.h"
+
+#undef strcasecmp
+
+struct DsCrackNamesPrivate {
+ struct DsPrivate base;
+
+ /* following names are used in Crack Names Matrix test */
+ const char *fqdn_name;
+ const char *user_principal_name;
+ const char *service_principal_name;
+};
+
+static bool test_DsCrackNamesMatrix(struct torture_context *tctx,
+ struct DsPrivate *priv, const char *dn,
+ const char *user_principal_name, const char *service_principal_name)
+{
+ NTSTATUS status;
+ const char *err_msg;
+ struct drsuapi_DsCrackNames r;
+ union drsuapi_DsNameRequest req;
+ uint32_t level_out;
+ union drsuapi_DsNameCtr ctr;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ TALLOC_CTX *mem_ctx = priv;
+
+ enum drsuapi_DsNameFormat formats[] = {
+ DRSUAPI_DS_NAME_FORMAT_UNKNOWN,
+ 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 = &req;
+ r.in.req->req1.codepage = 1252; /* german */
+ r.in.req->req1.language = 0x00000407; /* german */
+ r.in.req->req1.count = 1;
+ r.in.req->req1.names = names;
+ r.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
+
+ r.out.level_out = &level_out;
+ r.out.ctr = &ctr;
+
+ 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;
+ torture_comment(tctx, "Testing DsCrackNames (matrix prep) with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+ status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx,
+ "testing DsCrackNames (matrix prep) 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);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "testing DsCrackNames (matrix prep) 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));
+ torture_fail(tctx, err_msg);
+ }
+
+ switch (formats[i]) {
+ case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
+ if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "Unexpected error (%d): This name lookup should fail",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+ torture_comment(tctx, __location__ ": (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) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "Unexpected error (%d): This name lookup should fail",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+ torture_comment(tctx, __location__ ": (expected) error\n");
+ break;
+ case DRSUAPI_DS_NAME_FORMAT_UNKNOWN: /* should fail as we ask server to convert to Unknown format */
+ case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN:
+ if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "Unexpected error (%d): This name lookup should fail",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+ torture_comment(tctx, __location__ ": (expected) error\n");
+ break;
+ default:
+ if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "DsCrackNames error: %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+ break;
+ }
+
+ 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_UNKNOWN:
+ 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]);
+ break;
+ }
+ }
+
+ 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_r(p->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx,
+ "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);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "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));
+ torture_fail(tctx, err_msg);
+ }
+
+ 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++) {
+ torture_comment(tctx, "Converting %s (format %d)"
+ " to %d gave %s\n",
+ n_from[i] == NULL ? "NULL" : n_from[i],
+ formats[i], formats[j],
+ n_matrix[i][j] == NULL ?
+ "NULL" : n_matrix[i][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) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: should be %s",
+ formats[i], formats[j], n_from[j]);
+ torture_fail(tctx, err_msg);
+ } else if (n_matrix[i][j] != NULL && n_from[j] == NULL) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: should be %s",
+ formats[i], formats[j], n_matrix[i][j]);
+ torture_fail(tctx, err_msg);
+ } else if (strcmp(n_matrix[i][j], n_from[j]) != 0) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "dcerpc_drsuapi_DsCrackNames mismatch - from %d to %d: %s should be %s",
+ formats[i], formats[j], n_matrix[i][j], n_from[j]);
+ torture_fail(tctx, err_msg);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool test_DsCrackNames(struct torture_context *tctx,
+ struct DsPrivate *priv)
+{
+ NTSTATUS status;
+ const char *err_msg;
+ struct drsuapi_DsCrackNames r;
+ union drsuapi_DsNameRequest req;
+ uint32_t level_out;
+ union drsuapi_DsNameCtr ctr;
+ struct drsuapi_DsNameString names[1];
+ const char *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);
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ TALLOC_CTX *mem_ctx = priv;
+
+ ZERO_STRUCT(r);
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+ r.in.req = &req;
+ r.in.req->req1.codepage = 1252; /* german */
+ r.in.req->req1.language = 0x00000407; /* german */
+ r.in.req->req1.count = 1;
+ r.in.req->req1.names = names;
+ r.in.req->req1.format_flags = DRSUAPI_DS_NAME_FLAG_NO_FLAGS;
+
+ r.in.req->req1.format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
+ r.in.req->req1.format_desired = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
+
+ r.out.level_out = &level_out;
+ r.out.ctr = &ctr;
+
+ dom_sid = dom_sid_string(mem_ctx, torture_join_sid(priv->join));
+
+ names[0].str = dom_sid;
+
+ torture_comment(tctx, "Testing DsCrackNames with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ 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;
+
+ torture_comment(tctx, "Testing DsCrackNames with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ 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;
+
+ torture_comment(tctx, "Testing DsCrackNames with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ 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) {
+ err_msg = talloc_asprintf(mem_ctx, "local Round trip on canonical name failed: %s != %s!",
+ realm_canonical,
+ talloc_asprintf(mem_ctx, "%s/", dns_domain));
+ torture_fail(tctx, err_msg);
+ };
+
+ 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) {
+ err_msg = talloc_asprintf(mem_ctx, "local Round trip on canonical ex name failed: %s != %s!",
+ realm_canonical_ex,
+ talloc_asprintf(mem_ctx, "%s\n", dns_domain));
+ torture_fail(tctx, err_msg);
+ };
+
+ 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;
+
+ torture_comment(tctx, "Testing DsCrackNames with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ 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);
+
+ torture_comment(tctx, "Testing DsCrackNames with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ 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;
+
+ torture_comment(tctx, "Testing DsCrackNames with name '%s'"
+ " offered format: %d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+
+ status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != DRSUAPI_DS_NAME_STATUS_OK) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed on name - %d",
+ r.out.ctr->ctr1->array[0].status);
+ torture_fail(tctx, err_msg);
+ }
+
+ if (strcmp(priv->domain_dns_name, r.out.ctr->ctr1->array[0].dns_domain_name) != 0) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "DsCrackNames failed to return same DNS name - expected %s got %s",
+ priv->domain_dns_name, r.out.ctr->ctr1->array[0].dns_domain_name);
+ torture_fail(tctx, err_msg);
+ }
+
+ 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 = "DISPLAY 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 service 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 service 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 service 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 service 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 = DRSUAPI_DS_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 = "BUILTIN 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;
+
+ torture_comment(tctx, "Testing DsCrackNames with name '%s'"
+ " offered format: %d desired format:%d\n",
+ crack[i].str,
+ crack[i].format_offered,
+ crack[i].format_desired);
+
+ 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' offered format:%d desired format:%d\n",
+ crack[i].comment, names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+ } else {
+ comment = talloc_asprintf(mem_ctx, "'%s' offered format:%d desired format:%d\n",
+ names[0].str,
+ r.in.req->req1.format_offered,
+ r.in.req->req1.format_desired);
+ }
+ if (crack[i].skip) {
+ torture_comment(tctx, "skipping: %s", comment);
+ continue;
+ }
+ status = dcerpc_drsuapi_DsCrackNames_r(p->binding_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ err_msg = talloc_asprintf(mem_ctx, "dcerpc_drsuapi_DsCrackNames failed - %s", errstr);
+ torture_fail(tctx, err_msg);
+ } else if (!W_ERROR_IS_OK(r.out.result)) {
+ err_msg = talloc_asprintf(mem_ctx, "DsCrackNames failed - %s", win_errstr(r.out.result));
+ torture_fail(tctx, err_msg);
+ } else if (r.out.ctr->ctr1->array[0].status != crack[i].status) {
+ if (crack[i].alternate_status) {
+ if (r.out.ctr->ctr1->array[0].status != crack[i].alternate_status) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "DsCrackNames unexpected status %d, wanted %d or %d on: %s",
+ r.out.ctr->ctr1->array[0].status,
+ crack[i].status,
+ crack[i].alternate_status,
+ comment);
+ torture_fail(tctx, err_msg);
+ }
+ } else {
+ err_msg = talloc_asprintf(mem_ctx,
+ "DsCrackNames unexpected status %d, wanted %d on: %s\n",
+ r.out.ctr->ctr1->array[0].status,
+ crack[i].status,
+ comment);
+ torture_fail(tctx, err_msg);
+ }
+ } else if (crack[i].expected_str &&
+ (!r.out.ctr->ctr1->count ||
+ !r.out.ctr->ctr1->array[0].result_name))
+ {
+ if (!r.out.ctr->ctr1->count) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "DsCrackNames failed - got 0 entries, expected %s on %s",
+ crack[i].expected_str, comment);
+ torture_fail(tctx, err_msg);
+ } else {
+ err_msg = talloc_asprintf(mem_ctx,
+ "DsCrackNames failed - got NULL pointer, expected %s on %s",
+ crack[i].expected_str, comment);
+ torture_fail(tctx, err_msg);
+ }
+ } 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) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "DsCrackNames failed - got %s, expected %s on %s",
+ r.out.ctr->ctr1->array[0].result_name,
+ crack[i].expected_str, comment);
+ torture_fail(tctx, err_msg);
+ } else {
+ torture_comment(tctx,
+ "(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)) {
+ err_msg = talloc_asprintf(mem_ctx,
+ "DsCrackNames failed - got DNS name %s, expected %s on %s",
+ r.out.ctr->ctr1->array[0].result_name,
+ crack[i].expected_str, comment);
+ torture_fail(tctx, err_msg);
+ }
+
+ torture_comment(tctx, "Testing DsCrackNames got %s\n", r.out.ctr->ctr1->array[0].result_name);
+ }
+ }
+
+ return test_DsCrackNamesMatrix(tctx, priv, FQDN_1779_name,
+ user_principal_name, service_principal_name);
+}
+
+/**
+ * Test case setup for CrackNames
+ */
+static bool torture_drsuapi_cracknames_setup(struct torture_context *tctx, void **data)
+{
+ struct DsCrackNamesPrivate *priv;
+
+ *data = priv = talloc_zero(tctx, struct DsCrackNamesPrivate);
+
+ return torture_drsuapi_tcase_setup_common(tctx, &priv->base);
+}
+
+/**
+ * Test case tear-down for CrackNames
+ */
+static bool torture_drsuapi_cracknames_teardown(struct torture_context *tctx, void *data)
+{
+ struct DsCrackNamesPrivate *priv = talloc_get_type(data, struct DsCrackNamesPrivate);
+
+ return torture_drsuapi_tcase_teardown_common(tctx, &priv->base);
+}
+
+/**
+ * CRACKNAMES test suite implementation
+ */
+void torture_rpc_drsuapi_cracknames_tcase(struct torture_suite *suite)
+{
+ typedef bool (*run_func) (struct torture_context *test, void *tcase_data);
+
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "cracknames");
+
+ torture_tcase_set_fixture(tcase,
+ torture_drsuapi_cracknames_setup,
+ torture_drsuapi_cracknames_teardown);
+
+ torture_tcase_add_simple_test(tcase, "cracknames-test", (run_func)test_DsCrackNames);
+}
diff --git a/source4/torture/rpc/drsuapi_w2k8.c b/source4/torture/rpc/drsuapi_w2k8.c
new file mode 100644
index 0000000..9ff37ed
--- /dev/null
+++ b/source4/torture/rpc/drsuapi_w2k8.c
@@ -0,0 +1,334 @@
+/*
+ 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 "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+
+#define TEST_MACHINE_NAME "torturetest"
+
+/*
+ * DsBind as sent from W2K8 Client.
+ * This should work regardless of functional level, and accept
+ * any info <=48
+ */
+bool test_DsBind_w2k8(struct torture_context *tctx,
+ struct DsPrivate_w2k8 *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ struct drsuapi_DsBind r;
+ struct drsuapi_DsBindInfo48 *bind_info48;
+ struct drsuapi_DsBindInfoCtr bind_info_ctr;
+
+ /* We send info48 */
+ ZERO_STRUCT(bind_info_ctr);
+ bind_info_ctr.length = 48;
+
+ bind_info48 = &bind_info_ctr.info.info48;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_BASE;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ASYNC_REPLICATION;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_REMOVEAPI;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_MOVEREQ_V2;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHG_COMPRESS;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V1;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_RESTORE_USN_OPTIMIZATION;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_KCC_EXECUTE;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRY_V2;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_LINKED_VALUE_REPLICATION;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V2;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_INSTANCE_TYPE_NOT_REQ_ON_MOD;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_CRYPTO_BIND;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_REPL_INFO;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_STRONG_ENCRYPTION;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_DCINFO_V01;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_TRANSITIVE_MEMBERSHIP;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADD_SID_HISTORY;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_POST_BETA3;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GET_MEMBERSHIPS2;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V6;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_NONDOMAIN_NCS;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V8;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V5;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V6;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_ADDENTRYREPLY_V3;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_GETCHGREPLY_V7;
+ bind_info48->supported_extensions |= DRSUAPI_SUPPORTED_EXTENSION_VERIFY_OBJECT;
+
+ /*
+ * We wish for DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2,
+ * needed for DsGetDomainControllerInfo level 3
+ */
+ bind_info48->supported_extensions_ext |= DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2;
+
+ GUID_from_string(DRSUAPI_DS_BIND_GUID, &priv->bind_guid);
+
+ r.in.bind_guid = &priv->bind_guid;
+ r.in.bind_info = &bind_info_ctr;
+ r.out.bind_handle = &priv->bind_handle;
+
+ torture_comment(tctx, "Testing DsBind W2K8\n");
+
+ status = dcerpc_drsuapi_DsBind_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsBind");
+
+ torture_assert_not_null(tctx, r.out.bind_info,
+ "DsBind with info48 results in NULL");
+
+ /* cache server supported extensions, i.e. bind_info */
+ priv->srv_bind_info = *r.out.bind_info;
+
+ /*
+ * We do not check for length here, because it should be valid to return
+ * any valid info
+ */
+
+ return true;
+}
+
+static bool test_DsGetDomainControllerInfo_w2k8(struct torture_context *tctx,
+ struct DsPrivate_w2k8 *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ struct drsuapi_DsGetDomainControllerInfo r;
+ union drsuapi_DsGetDCInfoCtr ctr;
+ int32_t level_out = 0;
+ uint32_t supported_extensions_ext = 0;
+ bool found = false;
+ int 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
+ },
+ };
+
+ /* Levels 1 and 2 are tested in standard drsuapi tests */
+ int level = 3;
+
+ /* Do Bind first. */
+ if (!test_DsBind_w2k8(tctx, priv)) {
+ return false;
+ }
+
+ /*
+ * We used DsBind_w2k8, so DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2
+ * should mean support for level 3
+ */
+
+ /*
+ * We are looking for an extension found in info32 and later
+ */
+ switch (priv->srv_bind_info.length) {
+ case 32:
+ supported_extensions_ext = priv->srv_bind_info.info.info32.supported_extensions_ext;
+ break;
+ case 48:
+ supported_extensions_ext = priv->srv_bind_info.info.info48.supported_extensions_ext;
+ break;
+ default:
+ supported_extensions_ext = 0;
+ break;
+ }
+
+ supported_extensions_ext &= DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2;
+ torture_assert(tctx, (supported_extensions_ext > 0),
+ "Server does not support DRSUAPI_SUPPORTED_EXTENSION_LH_BETA2");
+
+ for (j=0; j < ARRAY_SIZE(names); j++) {
+ union drsuapi_DsGetDCInfoRequest req;
+ r.in.bind_handle = &priv->bind_handle;
+ r.in.level = 1;
+ r.in.req = &req;
+
+ r.in.req->req1.domain_name = names[j].name;
+ r.in.req->req1.level = level;
+
+ r.out.ctr = &ctr;
+ r.out.level_out = &level_out;
+
+ torture_comment(tctx,
+ "Testing DsGetDomainControllerInfo level %d on domainname '%s'\n",
+ r.in.req->req1.level, r.in.req->req1.domain_name);
+
+ status = dcerpc_drsuapi_DsGetDomainControllerInfo_r(p->binding_handle, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo with dns domain failed");
+ torture_assert_werr_equal(tctx, 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(tctx, r.in.req->req1.level, *r.out.level_out,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo in/out level differs");
+
+ for (k=0; k < r.out.ctr->ctr3.count; k++) {
+ if (strcasecmp_m(r.out.ctr->ctr3.array[k].netbios_name,
+ torture_join_netbios_name(priv->join)) == 0) {
+ found = true;
+ priv->dcinfo = r.out.ctr->ctr3.array[k];
+ break;
+ }
+ }
+ break;
+
+ torture_assert(tctx, found,
+ "dcerpc_drsuapi_DsGetDomainControllerInfo: Failed to find the domain controller we just created during the join");
+ }
+
+ return true;
+}
+
+
+bool test_DsUnbind_w2k8(struct torture_context *tctx,
+ struct DsPrivate_w2k8 *priv)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = priv->drs_pipe;
+ struct drsuapi_DsUnbind r;
+
+ r.in.bind_handle = &priv->bind_handle;
+ r.out.bind_handle = &priv->bind_handle;
+
+ torture_comment(tctx, "Testing DsUnbind W2K8\n");
+
+ status = dcerpc_drsuapi_DsUnbind_r(p->binding_handle, tctx, &r);
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsUnbind");
+
+ return true;
+}
+
+/**
+ * Common test case setup function to be used
+ * in DRS suit of test when appropriate
+ */
+bool torture_drsuapi_w2k8_tcase_setup_common(struct torture_context *tctx,
+ struct DsPrivate_w2k8 *priv)
+{
+ NTSTATUS status;
+ int rnd = rand() % 1000;
+ char *name = talloc_asprintf(tctx, "%s%d", TEST_MACHINE_NAME, rnd);
+ struct cli_credentials *machine_credentials;
+
+ torture_assert(tctx, priv, "Invalid argument");
+
+ torture_comment(tctx, "Create DRSUAPI pipe\n");
+ status = torture_rpc_connection(tctx,
+ &priv->drs_pipe,
+ &ndr_table_drsuapi);
+ torture_assert(tctx, NT_STATUS_IS_OK(status), "Unable to connect to DRSUAPI pipe");
+
+ torture_comment(tctx, "About to join domain with name %s\n", name);
+ priv->join = torture_join_domain(tctx, name, ACB_SVRTRUST,
+ &machine_credentials);
+ torture_assert(tctx, priv->join, "Failed to join as BDC");
+
+ /*
+ * After that every test should use DsBind and DsGetDomainControllerInfo
+ */
+ if (!test_DsBind_w2k8(tctx, priv)) {
+ /* clean up */
+ torture_drsuapi_w2k8_tcase_teardown_common(tctx, priv);
+ torture_fail(tctx, "Failed execute test_DsBind_w2k8()");
+ }
+
+
+ return true;
+}
+
+/**
+ * Common test case teardown function to be used
+ * in DRS suit of test when appropriate
+ */
+bool torture_drsuapi_w2k8_tcase_teardown_common(struct torture_context *tctx,
+ struct DsPrivate_w2k8 *priv)
+{
+ if (priv->join) {
+ torture_leave_domain(tctx, priv->join);
+ }
+
+ return true;
+}
+
+/**
+ * Test case setup for DRSUAPI test case
+ */
+static bool torture_drsuapi_w2k8_tcase_setup(struct torture_context *tctx, void **data)
+{
+ struct DsPrivate_w2k8 *priv;
+
+ *data = priv = talloc_zero(tctx, struct DsPrivate_w2k8);
+
+ return torture_drsuapi_w2k8_tcase_setup_common(tctx, priv);
+}
+
+/**
+ * Test case tear-down for DRSUAPI test case
+ */
+static bool torture_drsuapi_w2k8_tcase_teardown(struct torture_context *tctx, void *data)
+{
+ bool ret;
+ struct DsPrivate_w2k8 *priv = talloc_get_type(data, struct DsPrivate_w2k8);
+
+ ret = torture_drsuapi_w2k8_tcase_teardown_common(tctx, priv);
+
+ talloc_free(priv);
+ return ret;
+}
+
+/**
+ * DRSUAPI test case implementation
+ */
+void torture_rpc_drsuapi_w2k8_tcase(struct torture_suite *suite)
+{
+ typedef bool (*run_func) (struct torture_context *test, void *tcase_data);
+
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "drsuapi_w2k8");
+
+ torture_tcase_set_fixture(tcase, torture_drsuapi_w2k8_tcase_setup,
+ torture_drsuapi_w2k8_tcase_teardown);
+
+ torture_tcase_add_simple_test(tcase, "DsBind_W2K8", (run_func)test_DsBind_w2k8);
+ torture_tcase_add_simple_test(tcase, "DsGetDomainControllerInfo_W2K8", (run_func)test_DsGetDomainControllerInfo_w2k8);
+}
diff --git a/source4/torture/rpc/dsgetinfo.c b/source4/torture/rpc/dsgetinfo.c
new file mode 100644
index 0000000..b47d6ee
--- /dev/null
+++ b/source4/torture/rpc/dsgetinfo.c
@@ -0,0 +1,452 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ DsGetReplInfo test. Based on code from dssync.c
+
+ Copyright (C) Erick Nogueira do Nascimento 2009
+
+ 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/cmdline.h"
+#include "librpc/gen_ndr/ndr_drsuapi_c.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "libcli/cldap/cldap.h"
+#include "torture/torture.h"
+#include "../libcli/drsuapi/drsuapi.h"
+#include "auth/gensec/gensec.h"
+#include "param/param.h"
+#include "dsdb/samdb/samdb.h"
+#include "torture/rpc/torture_rpc.h"
+#include "torture/drs/proto.h"
+#include "lib/util/util_paths.h"
+
+
+struct DsGetinfoBindInfo {
+ struct dcerpc_pipe *drs_pipe;
+ struct dcerpc_binding_handle *drs_handle;
+ 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 DsGetinfoTest {
+ 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 DsGetinfoBindInfo drsuapi;
+ } admin;
+};
+
+
+
+/*
+ return the default DN for a ldap server given a connected RPC pipe to the
+ server
+ */
+static const char *torture_get_ldap_base_dn(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ const char *hostname = dcerpc_binding_get_string_option(p->binding, "host");
+ struct ldb_context *ldb;
+ const char *ldap_url = talloc_asprintf(p, "ldap://%s", hostname);
+ const char *attrs[] = { "defaultNamingContext", NULL };
+ const char *dnstr;
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+ int ret;
+ struct ldb_result *res;
+
+ ldb = ldb_init(tmp_ctx, tctx->ev);
+ if (ldb == NULL) {
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ if (ldb_set_opaque(ldb, "loadparm", tctx->lp_ctx)) {
+ talloc_free(ldb);
+ return NULL;
+ }
+
+ ldb_set_modules_dir(ldb,
+ modules_path(ldb, "ldb"));
+
+ ret = ldb_connect(ldb, ldap_url, 0, NULL);
+ if (ret != LDB_SUCCESS) {
+ torture_comment(tctx, "Failed to make LDB connection to target");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ ret = dsdb_search_dn(ldb, tmp_ctx, &res, ldb_dn_new(tmp_ctx, ldb, ""),
+ attrs, 0);
+ if (ret != LDB_SUCCESS) {
+ torture_comment(tctx, "Failed to get defaultNamingContext");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ dnstr = ldb_msg_find_attr_as_string(res->msgs[0], "defaultNamingContext", NULL);
+ dnstr = talloc_strdup(tctx, dnstr);
+ talloc_free(tmp_ctx);
+ return dnstr;
+}
+
+
+static struct DsGetinfoTest *test_create_context(struct torture_context *tctx)
+{
+ NTSTATUS status;
+ struct DsGetinfoTest *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 DsGetinfoTest);
+ 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;
+ }
+ status = dcerpc_binding_set_flags(ctx->drsuapi_binding, DCERPC_SIGN | DCERPC_SEAL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_binding_set_flags - %s\n", nt_errstr(status));
+ return NULL;
+ }
+
+ /* ctx->admin ...*/
+ ctx->admin.credentials = samba_cmdline_get_creds();
+
+ 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;
+
+ return ctx;
+}
+
+static bool _test_DsBind(struct torture_context *tctx,
+ struct DsGetinfoTest *ctx, struct cli_credentials *credentials, struct DsGetinfoBindInfo *b)
+{
+ NTSTATUS status;
+ bool ret = true;
+
+ status = dcerpc_pipe_connect_b(ctx,
+ &b->drs_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;
+ }
+ b->drs_handle = b->drs_pipe->binding_handle;
+
+ status = dcerpc_drsuapi_DsBind_r(b->drs_handle, ctx, &b->req);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ 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 28: {
+ b->peer_bind_info28 = b->req.out.bind_info->info.info28;
+ break;
+ }
+ case 32: {
+ struct drsuapi_DsBindInfo32 *info32;
+ info32 = &b->req.out.bind_info->info.info32;
+ b->peer_bind_info28.supported_extensions= info32->supported_extensions;
+ b->peer_bind_info28.site_guid = info32->site_guid;
+ b->peer_bind_info28.pid = info32->pid;
+ b->peer_bind_info28.repl_epoch = info32->repl_epoch;
+ 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 52: {
+ struct drsuapi_DsBindInfo52 *info52;
+ info52 = &b->req.out.bind_info->info.info52;
+ b->peer_bind_info28.supported_extensions= info52->supported_extensions;
+ b->peer_bind_info28.site_guid = info52->site_guid;
+ b->peer_bind_info28.pid = info52->pid;
+ b->peer_bind_info28.repl_epoch = info52->repl_epoch;
+ break;
+ }
+ default:
+ printf("DsBind - warning: unknown BindInfo length: %u\n",
+ b->req.out.bind_info->length);
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_getinfo(struct torture_context *tctx,
+ struct DsGetinfoTest *ctx)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = ctx->admin.drsuapi.drs_pipe;
+ struct dcerpc_binding_handle *b = ctx->admin.drsuapi.drs_handle;
+ struct drsuapi_DsReplicaGetInfo r;
+ union drsuapi_DsReplicaGetInfoRequest req;
+ union drsuapi_DsReplicaInfo info;
+ enum drsuapi_DsReplicaInfoType info_type;
+ int i;
+ bool no_invalid_levels = true;
+ struct {
+ int32_t level;
+ int32_t infotype;
+ const char *obj_dn;
+ const char *attribute_name;
+ uint32_t flags;
+ } array[] = {
+ {
+ .level = DRSUAPI_DS_REPLICA_GET_INFO,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_NEIGHBORS
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_CURSORS
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_KCC_DSA_CONNECT_FAILURES
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_KCC_DSA_LINK_FAILURES
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_PENDING_OPS
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_CURSORS2
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_CURSORS3
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2,
+ .obj_dn = "CN=Domain Admins,CN=Users,",
+ .flags = 0
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_OBJ_METADATA2,
+ .obj_dn = "CN=Domain Admins,CN=Users,",
+ .flags = DRSUAPI_DS_LINKED_ATTRIBUTE_FLAG_ACTIVE
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_ATTRIBUTE_VALUE_METADATA2
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_REPSTO
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_CLIENT_CONTEXTS
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_UPTODATE_VECTOR_V1
+ },{
+ .level = DRSUAPI_DS_REPLICA_GET_INFO2,
+ .infotype = DRSUAPI_DS_REPLICA_INFO_SERVER_OUTGOING_CALLS
+ }
+ };
+
+ ctx->domain_dn = torture_get_ldap_base_dn(tctx, p);
+ torture_assert(tctx, ctx->domain_dn != NULL, "Cannot get domain_dn");
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "skipping DsReplicaGetInfo test against Samba4\n");
+ return true;
+ }
+
+ r.in.bind_handle = &ctx->admin.drsuapi.bind_handle;
+ r.in.req = &req;
+
+ for (i=0; i < ARRAY_SIZE(array); i++) {
+ const char *object_dn;
+
+ torture_comment(tctx, "Testing DsReplicaGetInfo level %d infotype %d\n",
+ array[i].level, array[i].infotype);
+
+ if (array[i].obj_dn) {
+ object_dn = array[i].obj_dn;
+ if (object_dn[strlen(object_dn)-1] == ',') {
+ /* add the domain DN on the end */
+ object_dn = talloc_asprintf(tctx, "%s%s", object_dn, ctx->domain_dn);
+ }
+ } else {
+ object_dn = ctx->domain_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.source_dsa_guid);
+ 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->req2.source_dsa_guid);
+ r.in.req->req2.flags = 0;
+ r.in.req->req2.attribute_name = NULL;
+ r.in.req->req2.value_dn_str = NULL;
+ r.in.req->req2.enumeration_context = 0;
+ break;
+ }
+
+ /* Construct a different request for some of the infoTypes */
+ if (array[i].attribute_name != NULL) {
+ r.in.req->req2.attribute_name = array[i].attribute_name;
+ }
+ if (array[i].flags != 0) {
+ r.in.req->req2.flags |= array[i].flags;
+ }
+
+ r.out.info = &info;
+ r.out.info_type = &info_type;
+
+ status = dcerpc_drsuapi_DsReplicaGetInfo_r(b, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) {
+ torture_comment(tctx,
+ "DsReplicaGetInfo level %d and/or infotype %d not supported by server\n",
+ array[i].level, array[i].infotype);
+ continue;
+ }
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx,
+ "DsReplicaGetInfo level %d and/or infotype %d failed\n",
+ array[i].level, array[i].infotype));
+ if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL)) {
+ /* this is a not yet supported level */
+ torture_comment(tctx,
+ "DsReplicaGetInfo level %d and/or infotype %d not yet supported by server\n",
+ array[i].level, array[i].infotype);
+ no_invalid_levels = false;
+ continue;
+ }
+
+ torture_drsuapi_assert_call(tctx, p, status, &r, "dcerpc_drsuapi_DsReplicaGetInfo");
+ }
+
+ return no_invalid_levels;
+}
+
+/**
+ * DSGETINFO test case setup
+ */
+static bool torture_dsgetinfo_tcase_setup(struct torture_context *tctx, void **data)
+{
+ bool bret;
+ struct DsGetinfoTest *ctx;
+
+ *data = ctx = test_create_context(tctx);
+ torture_assert(tctx, ctx, "test_create_context() failed");
+
+ bret = _test_DsBind(tctx, ctx, ctx->admin.credentials, &ctx->admin.drsuapi);
+ torture_assert(tctx, bret, "_test_DsBind() failed");
+
+ return true;
+}
+
+/**
+ * DSGETINFO test case cleanup
+ */
+static bool torture_dsgetinfo_tcase_teardown(struct torture_context *tctx, void *data)
+{
+ struct DsGetinfoTest *ctx;
+ struct drsuapi_DsUnbind r;
+ struct policy_handle bind_handle;
+
+ ctx = talloc_get_type(data, struct DsGetinfoTest);
+
+ ZERO_STRUCT(r);
+ r.out.bind_handle = &bind_handle;
+
+ /* Unbing admin handle */
+ r.in.bind_handle = &ctx->admin.drsuapi.bind_handle;
+ if (ctx->admin.drsuapi.drs_handle) {
+ dcerpc_drsuapi_DsUnbind_r(ctx->admin.drsuapi.drs_handle,
+ ctx, &r);
+ }
+
+ talloc_free(ctx);
+
+ return true;
+}
+
+/**
+ * DSGETINFO test case implementation
+ */
+void torture_drs_rpc_dsgetinfo_tcase(struct torture_suite *suite)
+{
+ typedef bool (*run_func) (struct torture_context *test, void *tcase_data);
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "dsgetinfo");
+
+ torture_tcase_set_fixture(tcase,
+ torture_dsgetinfo_tcase_setup,
+ torture_dsgetinfo_tcase_teardown);
+
+ torture_tcase_add_simple_test(tcase, "DsGetReplicaInfo", (run_func)test_getinfo);
+}
+
diff --git a/source4/torture/rpc/dssetup.c b/source4/torture/rpc/dssetup.c
new file mode 100644
index 0000000..9a61199
--- /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 "librpc/gen_ndr/ndr_dssetup_c.h"
+#include "torture/rpc/torture_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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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/echo.c b/source4/torture/rpc/echo.c
new file mode 100644
index 0000000..93fd408
--- /dev/null
+++ b/source4/torture/rpc/echo.c
@@ -0,0 +1,474 @@
+/*
+ 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/rpc/torture_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; \
+ torture_assert_ntstatus_ok(tctx, dcerpc_echo_AddOne_r(b, tctx, &r), \
+ 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;
+ uint32_t n;
+ struct echo_AddOne r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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;
+ uint8_t *data_in, *data_out;
+ int len;
+ struct echo_EchoData r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_echo_EchoData_r(b, tctx, &r),
+ 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;
+ int len;
+ struct echo_SourceData r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_echo_SourceData_r(b, tctx, &r),
+ 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;
+ uint8_t *data_in;
+ int len;
+ struct echo_SinkData r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_echo_SinkData_r(b, tctx, &r),
+ 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)
+{
+ struct echo_TestCall r;
+ const char *s = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.s1 = "input string";
+ r.out.s2 = &s;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestCall_r(b, tctx, &r),
+ "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)
+{
+ struct echo_TestCall2 r;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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);
+ torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestCall2_r(b, tctx, &r),
+ "TestCall2 failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "TestCall2 failed");
+ }
+ return true;
+}
+
+static void test_sleep_done(struct tevent_req *subreq)
+{
+ bool *done1 = (bool *)tevent_req_callback_data_void(subreq);
+ *done1 = true;
+}
+
+/*
+ test the TestSleep interface
+*/
+static bool test_sleep(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+#define ASYNC_COUNT 3
+ struct tevent_req *req[ASYNC_COUNT];
+ struct echo_TestSleep r[ASYNC_COUNT];
+ bool done1[ASYNC_COUNT];
+ bool done2[ASYNC_COUNT];
+ struct timeval snd[ASYNC_COUNT];
+ struct timeval rcv[ASYNC_COUNT];
+ struct timeval diff[ASYNC_COUNT];
+ int total_done = 0;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ enum dcerpc_transport_t transport;
+ uint32_t assoc_group_id;
+ struct dcerpc_pipe *p2 = NULL;
+ NTSTATUS status;
+
+ 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");
+
+ transport = dcerpc_binding_get_transport(p->binding);
+ assoc_group_id = dcerpc_binding_get_assoc_group_id(p->binding);
+
+ torture_comment(tctx, "connect echo connection 2 with "
+ "DCERPC_CONCURRENT_MULTIPLEX\n");
+ status = torture_rpc_connection_transport(tctx, &p2,
+ &ndr_table_rpcecho,
+ transport,
+ assoc_group_id,
+ DCERPC_CONCURRENT_MULTIPLEX);
+ torture_assert_ntstatus_ok(tctx, status, "opening echo connection 2");
+ b = p2->binding_handle;
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ done1[i] = false;
+ done2[i] = false;
+ snd[i] = timeval_current();
+ rcv[i] = timeval_zero();
+ r[i].in.seconds = ASYNC_COUNT-i;
+ req[i] = dcerpc_echo_TestSleep_r_send(tctx, tctx->ev, b, &r[i]);
+ torture_assert(tctx, req[i], "Failed to send async sleep request\n");
+ tevent_req_set_callback(req[i], test_sleep_done, &done1[i]);
+ }
+
+ while (total_done < ASYNC_COUNT) {
+ torture_assert(tctx, tevent_loop_once(tctx->ev) == 0,
+ "Event context loop failed");
+ for (i=0;i<ASYNC_COUNT;i++) {
+ if (done2[i] == false && done1[i] == true) {
+ int rounded_tdiff;
+ total_done++;
+ done2[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));
+ torture_comment(tctx, "rounded_tdiff=%d\n", rounded_tdiff);
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_echo_TestSleep_r_recv(req[i], tctx),
+ 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, (unsigned int)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, (unsigned int)diff[i].tv_sec, (unsigned int)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, (unsigned int)diff[i].tv_sec, (unsigned int)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, (unsigned int)diff[i].tv_sec, (unsigned int)diff[i].tv_usec);
+ } else {
+ torture_fail(tctx, talloc_asprintf(tctx,
+ "(Failed) - Not async - Slept for %u seconds (but reply takes %u.%06u seconds)\n",
+ r[i].out.result, (unsigned int)diff[i].tv_sec, (unsigned int)diff[i].tv_usec));
+ }
+ }
+ }
+ }
+ torture_comment(tctx, "\n");
+ return true;
+}
+
+/*
+ test enum handling
+*/
+static bool test_enum(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct echo_TestEnum r;
+ enum echo_Enum1 v = ECHO_ENUM1;
+ struct echo_Enum2 e2;
+ union echo_Enum3 e3;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestEnum_r(b, tctx, &r),
+ "TestEnum failed");
+ return true;
+}
+
+/*
+ test surrounding conformant array handling
+*/
+static bool test_surrounding(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct echo_TestSurrounding r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestSurrounding_r(b, tctx, &r),
+ "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)
+{
+ struct echo_TestDoublePointer r;
+ uint16_t value = 12;
+ uint16_t *pvalue = &value;
+ uint16_t **ppvalue = &pvalue;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(r);
+ r.in.data = &ppvalue;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_echo_TestDoublePointer_r(b, tctx, &r),
+ "TestDoublePointer failed");
+
+ torture_assert_int_equal(tctx, value, r.out.result,
+ "TestDoublePointer did not return original value");
+ return true;
+}
+
+
+/*
+ test request timeouts
+*/
+#if 0 /* this test needs fixing to work over ncacn_np */
+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_echo_TestSleep_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_echo_TestSleep_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;
+}
+#endif
+
+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);
+#if 0 /* this test needs fixing to work over ncacn_np */
+ torture_rpc_tcase_add_test(tcase, "timeout", test_timeout);
+#endif
+
+ return suite;
+}
diff --git a/source4/torture/rpc/epmapper.c b/source4/torture/rpc/epmapper.c
new file mode 100644
index 0000000..72b5165
--- /dev/null
+++ b/source4/torture/rpc/epmapper.c
@@ -0,0 +1,679 @@
+/*
+ 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 "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "librpc/ndr/ndr_table.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "torture/rpc/torture_rpc.h"
+#include "lib/util/util_net.h"
+#include "librpc/rpc/rpc_common.h"
+
+/*
+ display any protocol tower
+ */
+static void display_tower(struct torture_context *tctx, struct epm_tower *twr)
+{
+ int i;
+
+ for (i = 0; i < twr->num_floors; i++) {
+ torture_comment(tctx,
+ " %s",
+ epm_floor_string(tctx, &twr->floors[i]));
+ }
+ torture_comment(tctx, "\n");
+}
+
+static bool test_Insert(struct torture_context *tctx,
+ struct dcerpc_binding_handle *h,
+ struct ndr_syntax_id object,
+ const char *annotation,
+ const struct dcerpc_binding *b)
+{
+ struct epm_Insert r;
+ NTSTATUS status;
+
+ r.in.num_ents = 1;
+ r.in.entries = talloc_array(tctx, struct epm_entry_t, 1);
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_skip(tctx, "Skip Insert test against Samba4");
+ }
+
+ /* FIXME zero */
+ ZERO_STRUCT(r.in.entries[0].object);
+ r.in.entries[0].annotation = annotation;
+
+ r.in.entries[0].tower = talloc(tctx, struct epm_twr_t);
+
+ status = dcerpc_binding_build_tower(tctx,
+ b,
+ &r.in.entries[0].tower->tower);
+
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "Unable to build tower from binding struct");
+ r.in.replace = 0;
+
+ /* shoot! */
+ status = dcerpc_epm_Insert_r(h, tctx, &r);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ torture_comment(tctx,
+ "epm_Insert failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (r.out.result != EPMAPPER_STATUS_OK) {
+ torture_comment(tctx,
+ "epm_Insert failed - internal error: 0x%.4x\n",
+ r.out.result);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_Delete(struct torture_context *tctx,
+ struct dcerpc_binding_handle *h,
+ const char *annotation,
+ const struct dcerpc_binding *b)
+{
+ NTSTATUS status;
+ struct epm_Delete r;
+
+ 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 = annotation;
+
+ r.in.entries[0].tower = talloc(tctx, struct epm_twr_t);
+
+ status = dcerpc_binding_build_tower(tctx,
+ b,
+ &r.in.entries[0].tower->tower);
+
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "Unable to build tower from binding struct");
+ r.in.num_ents = 1;
+
+ status = dcerpc_epm_Delete_r(h, tctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ torture_comment(tctx,
+ "epm_Delete failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (r.out.result != EPMAPPER_STATUS_OK) {
+ torture_comment(tctx,
+ "epm_Delete failed - internal error: 0x%.4x\n",
+ r.out.result);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_Map_tcpip(struct torture_context *tctx,
+ struct dcerpc_binding_handle *h,
+ struct ndr_syntax_id map_syntax)
+{
+ struct epm_Map r;
+ struct GUID uuid;
+ struct policy_handle entry_handle;
+ struct ndr_syntax_id syntax;
+ struct dcerpc_binding *map_binding;
+ struct epm_twr_t map_tower;
+ struct epm_twr_p_t towers[20];
+ struct epm_tower t;
+ uint32_t num_towers;
+ uint32_t port;
+ uint32_t i;
+ long int p;
+ const char *tmp;
+ const char *ip;
+ char *ptr;
+ NTSTATUS status;
+
+ torture_comment(tctx, "Testing epm_Map\n");
+
+ ZERO_STRUCT(uuid);
+ ZERO_STRUCT(entry_handle);
+
+ r.in.object = &uuid;
+ r.in.map_tower = &map_tower;
+ r.in.entry_handle = &entry_handle;
+ r.out.entry_handle = &entry_handle;
+ r.in.max_towers = 10;
+ r.out.towers = towers;
+ r.out.num_towers = &num_towers;
+
+ /* Create map tower */
+ status = dcerpc_parse_binding(tctx, "ncacn_ip_tcp:[135]", &map_binding);
+ torture_assert_ntstatus_ok(tctx, status,
+ "epm_Map_tcpip failed: can't create map_binding");
+
+ status = dcerpc_binding_set_abstract_syntax(map_binding, &map_syntax);
+ torture_assert_ntstatus_ok(tctx, status,
+ "epm_Map_tcpip failed: set map_syntax");
+
+ status = dcerpc_binding_build_tower(tctx, map_binding,
+ &map_tower.tower);
+ torture_assert_ntstatus_ok(tctx, status,
+ "epm_Map_tcpip failed: can't create map_tower");
+
+ torture_comment(tctx,
+ "epm_Map request for '%s':\n",
+ ndr_interface_name(&map_syntax.uuid, map_syntax.if_version));
+ display_tower(tctx, &r.in.map_tower->tower);
+
+ status = dcerpc_epm_Map_r(h, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "epm_Map_simple failed");
+ torture_assert(tctx, r.out.result == EPMAPPER_STATUS_OK,
+ "epm_Map_tcpip failed: result is not EPMAPPER_STATUS_OK");
+
+ /* Check the result */
+ t = r.out.towers[0].twr->tower;
+
+ /* Check if we got the correct RPC interface identifier */
+ dcerpc_floor_get_lhs_data(&t.floors[0], &syntax);
+ torture_assert(tctx, ndr_syntax_id_equal(&syntax, &map_syntax),
+ "epm_Map_tcpip failed: Interface identifier mismatch");
+
+ torture_comment(tctx,
+ "epm_Map_tcpip response for '%s':\n",
+ ndr_interface_name(&syntax.uuid, syntax.if_version));
+
+ dcerpc_floor_get_lhs_data(&t.floors[1], &syntax);
+ torture_assert(tctx, ndr_syntax_id_equal(&syntax, &ndr_transfer_syntax_ndr),
+ "epm_Map_tcpip failed: floor 2 is not NDR encoded");
+
+ torture_assert(tctx, t.floors[2].lhs.protocol == EPM_PROTOCOL_NCACN,
+ "epm_Map_tcpip failed: floor 3 is not NCACN_IP_TCP");
+
+ tmp = dcerpc_floor_get_rhs_data(tctx, &t.floors[3]);
+ p = strtol(tmp, &ptr, 10);
+ port = p & 0xffff;
+ torture_assert(tctx, port > 1024 && port < 65535, "epm_Map_tcpip failed");
+
+ ip = dcerpc_floor_get_rhs_data(tctx, &t.floors[4]);
+ torture_assert(tctx, is_ipaddress(ip), "epm_Map_tcpip failed");
+
+ for (i = 0; i < *r.out.num_towers; i++) {
+ if (r.out.towers[i].twr) {
+ display_tower(tctx, &t);
+ }
+ }
+
+ return true;
+}
+
+static bool test_Map_full(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ const struct ndr_syntax_id obj = {
+ { 0x8a885d04, 0x1ceb, 0x11c9, {0x9f, 0xe8}, {0x08,0x00,0x2b,0x10,0x48,0x60} },
+ 2
+ };
+ struct dcerpc_binding_handle *h = p->binding_handle;
+ const char *annotation = "SMBTORTURE";
+ struct dcerpc_binding *b;
+ NTSTATUS status;
+ bool ok;
+
+ status = dcerpc_parse_binding(tctx, "ncacn_ip_tcp:216.83.154.106[41768]", &b);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "Unable to generate dcerpc_binding struct");
+ status = dcerpc_binding_set_abstract_syntax(b, &obj);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_binding_set_abstract_syntax");
+
+ ok = test_Insert(tctx, h, obj, annotation, b);
+ torture_assert(tctx, ok, "test_Insert failed");
+
+ ok = test_Map_tcpip(tctx, h, obj);
+ torture_assert(tctx, ok, "test_Map_tcpip failed");
+
+ ok = test_Delete(tctx, h, annotation, b);
+ torture_assert(tctx, ok, "test_Delete failed");
+
+ return true;
+}
+
+static bool test_Map_display(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct epm_entry_t *entry)
+
+{
+ NTSTATUS status;
+ struct epm_twr_t *twr = entry->tower;
+ struct epm_Map r;
+ struct GUID uuid = entry->object;
+ struct policy_handle handle;
+ struct ndr_syntax_id syntax;
+ uint32_t num_towers;
+ uint32_t i;
+
+ 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 = 10;
+ r.out.num_towers = &num_towers;
+
+ dcerpc_floor_get_lhs_data(&twr->tower.floors[0], &syntax);
+
+ torture_comment(tctx,
+ "epm_Map results for '%s':\n",
+ ndr_interface_name(&syntax.uuid, syntax.if_version));
+
+ status = dcerpc_epm_Map_r(b, tctx, &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(tctx, &r.out.towers[i].twr->tower);
+ }
+ }
+ }
+
+ /* RPC protocol identifier */
+ 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;
+
+ /* Port address */
+ 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;
+
+ /* Transport */
+ 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_r(b, tctx, &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(tctx, &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_r(b, tctx, &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(tctx, &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.udp.port = 0;
+
+ status = dcerpc_epm_Map_r(b, tctx, &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(tctx, &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_r(b, tctx, &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(tctx, &r.out.towers[i].twr->tower);
+ }
+ }
+ }
+
+ /* FIXME: Extend to do other protocols as well (ncacn_unix_stream, ncalrpc) */
+
+ return true;
+}
+
+static bool test_Map_simple(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct epm_Lookup r;
+ struct policy_handle entry_handle;
+ uint32_t num_ents = 0;
+ struct dcerpc_binding_handle *h = p->binding_handle;
+
+ ZERO_STRUCT(entry_handle);
+
+ torture_comment(tctx, "Testing epm_Map\n");
+
+ /* get all elements */
+ r.in.inquiry_type = RPC_C_EP_ALL_ELTS;
+ r.in.object = NULL;
+ r.in.interface_id = NULL;
+ r.in.vers_option = RPC_C_VERS_ALL;
+
+ r.in.entry_handle = &entry_handle;
+ r.in.max_ents = 10;
+
+ r.out.entry_handle = &entry_handle;
+ r.out.num_ents = &num_ents;
+
+ do {
+ int i;
+
+ status = dcerpc_epm_Lookup_r(h, tctx, &r);
+ if (!NT_STATUS_IS_OK(status) ||
+ r.out.result != EPMAPPER_STATUS_OK) {
+ break;
+ }
+
+ for (i = 0; i < *r.out.num_ents; i++) {
+ if (r.out.entries[i].tower->tower.num_floors == 5) {
+ test_Map_display(h, tctx, &r.out.entries[i]);
+ }
+ }
+ } while (NT_STATUS_IS_OK(status) &&
+ r.out.result == EPMAPPER_STATUS_OK &&
+ *r.out.num_ents == r.in.max_ents &&
+ !ndr_policy_handle_empty(&entry_handle));
+
+ torture_assert_ntstatus_ok(tctx, status, "epm_Map_simple failed");
+
+ torture_assert(tctx,
+ ndr_policy_handle_empty(&entry_handle),
+ "epm_Map_simple failed - The policy handle should be empty.");
+
+ return true;
+}
+
+static bool test_LookupHandleFree(struct torture_context *tctx,
+ struct dcerpc_binding_handle *h,
+ struct policy_handle *entry_handle) {
+ NTSTATUS status;
+ struct epm_LookupHandleFree r;
+
+ if (ndr_policy_handle_empty(entry_handle)) {
+ torture_comment(tctx,
+ "epm_LookupHandleFree failed - empty policy_handle\n");
+ return false;
+ }
+
+ r.in.entry_handle = entry_handle;
+ r.out.entry_handle = entry_handle;
+
+ status = dcerpc_epm_LookupHandleFree_r(h, tctx, &r);
+ if (NT_STATUS_IS_ERR(status)) {
+ torture_comment(tctx,
+ "epm_LookupHandleFree failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (r.out.result != EPMAPPER_STATUS_OK) {
+ torture_comment(tctx,
+ "epm_LookupHandleFree failed - internal error: "
+ "0x%.4x\n",
+ r.out.result);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_Lookup_simple(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct epm_Lookup r;
+ struct policy_handle entry_handle;
+ uint32_t num_ents = 0;
+ struct dcerpc_binding_handle *h = p->binding_handle;
+
+ ZERO_STRUCT(entry_handle);
+
+ torture_comment(tctx, "Testing epm_Lookup\n");
+
+ /* get all elements */
+ r.in.inquiry_type = RPC_C_EP_ALL_ELTS;
+ r.in.object = NULL;
+ r.in.interface_id = NULL;
+ r.in.vers_option = RPC_C_VERS_ALL;
+
+ r.in.entry_handle = &entry_handle;
+ r.in.max_ents = 10;
+
+ r.out.entry_handle = &entry_handle;
+ r.out.num_ents = &num_ents;
+
+ do {
+ int i;
+
+ status = dcerpc_epm_Lookup_r(h, tctx, &r);
+ if (!NT_STATUS_IS_OK(status) ||
+ r.out.result != EPMAPPER_STATUS_OK) {
+ break;
+ }
+
+ torture_comment(tctx,
+ "epm_Lookup returned %d events, entry_handle: %s\n",
+ *r.out.num_ents,
+ GUID_string(tctx, &entry_handle.uuid));
+
+ for (i = 0; i < *r.out.num_ents; i++) {
+ torture_comment(tctx,
+ "\n Found '%s' Object[%s]\n",
+ r.out.entries[i].annotation,
+ GUID_string(tctx, &r.out.entries[i].object));
+
+ display_tower(tctx, &r.out.entries[i].tower->tower);
+ }
+ } while (NT_STATUS_IS_OK(status) &&
+ r.out.result == EPMAPPER_STATUS_OK &&
+ *r.out.num_ents == r.in.max_ents &&
+ !ndr_policy_handle_empty(&entry_handle));
+
+ torture_assert_ntstatus_ok(tctx, status, "epm_Lookup failed");
+ torture_assert(tctx, r.out.result == EPMAPPER_STATUS_NO_MORE_ENTRIES, "epm_Lookup failed");
+
+ torture_assert(tctx,
+ ndr_policy_handle_empty(&entry_handle),
+ "epm_Lookup failed - The policy handle should be empty.");
+
+ return true;
+}
+
+/*
+ * This test starts a epm_Lookup request, but doesn't finish the
+ * call terminates the search. So it will call epm_LookupHandleFree.
+ */
+static bool test_Lookup_terminate_search(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ bool ok;
+ NTSTATUS status;
+ struct epm_Lookup r;
+ struct policy_handle entry_handle;
+ uint32_t i, num_ents = 0;
+ struct dcerpc_binding_handle *h = p->binding_handle;
+
+ ZERO_STRUCT(entry_handle);
+
+ torture_comment(tctx, "Testing epm_Lookup and epm_LookupHandleFree\n");
+
+ /* get all elements */
+ r.in.inquiry_type = RPC_C_EP_ALL_ELTS;
+ r.in.object = NULL;
+ r.in.interface_id = NULL;
+ r.in.vers_option = RPC_C_VERS_ALL;
+
+ r.in.entry_handle = &entry_handle;
+ r.in.max_ents = 2;
+
+ r.out.entry_handle = &entry_handle;
+ r.out.num_ents = &num_ents;
+
+ status = dcerpc_epm_Lookup_r(h, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "epm_Lookup failed");
+ torture_assert(tctx, r.out.result == EPMAPPER_STATUS_OK, "epm_Lookup failed");
+
+ torture_comment(tctx,
+ "epm_Lookup returned %d events, entry_handle: %s\n",
+ *r.out.num_ents,
+ GUID_string(tctx, &entry_handle.uuid));
+
+ for (i = 0; i < *r.out.num_ents; i++) {
+ torture_comment(tctx,
+ "\n Found '%s'\n",
+ r.out.entries[i].annotation);
+ }
+
+ ok = test_LookupHandleFree(tctx,
+ h,
+ &entry_handle);
+ if (!ok) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_Insert_noreplace(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ bool ok;
+ NTSTATUS status;
+ struct epm_Insert r;
+ struct dcerpc_binding *b;
+ struct dcerpc_binding_handle *h = p->binding_handle;
+
+ torture_comment(tctx, "Testing epm_Insert(noreplace) and epm_Delete\n");
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_skip(tctx, "Skip Insert test against Samba4");
+ }
+
+ 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]", &b);
+ 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,
+ b,
+ &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_r(h, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "epm_Insert failed");
+
+ torture_assert(tctx, r.out.result == 0, "epm_Insert failed");
+
+ ok = test_Delete(tctx, h, "smbtorture", b);
+ if (!ok) {
+ return false;
+ }
+
+ return true;
+}
+
+#if 0
+/*
+ * The MS-RPCE documentation states that this function isn't implemented and
+ * SHOULD NOT be called by a client.
+ */
+static bool test_InqObject(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct epm_InqObject r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.epm_object = talloc(tctx, struct GUID);
+ *r.in.epm_object = ndr_table_epmapper.syntax_id.uuid;
+
+ status = dcerpc_epm_InqObject_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "InqObject failed");
+
+ return true;
+}
+#endif
+
+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);
+
+ /* This is a stack */
+ torture_rpc_tcase_add_test(tcase,
+ "Map_simple",
+ test_Map_simple);
+ torture_rpc_tcase_add_test(tcase,
+ "Map_full",
+ test_Map_full);
+ torture_rpc_tcase_add_test(tcase,
+ "Lookup_simple",
+ test_Lookup_simple);
+ torture_rpc_tcase_add_test(tcase,
+ "Lookup_terminate_search",
+ test_Lookup_terminate_search);
+ torture_rpc_tcase_add_test(tcase,
+ "Insert_noreplace",
+ test_Insert_noreplace);
+
+ return suite;
+}
+
+/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */
diff --git a/source4/torture/rpc/eventlog.c b/source4/torture/rpc/eventlog.c
new file mode 100644
index 0000000..87dc6a2
--- /dev/null
+++ b/source4/torture/rpc/eventlog.c
@@ -0,0 +1,501 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for eventlog rpc operations
+
+ Copyright (C) Tim Potter 2003,2005
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Guenther Deschner 2009
+
+ 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_eventlog.h"
+#include "librpc/gen_ndr/ndr_eventlog_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+
+#define TEST_BACKUP_NAME "samrtorturetest"
+
+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_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct eventlog_OpenEventLogW r;
+ struct eventlog_OpenUnknown0 unknown0;
+ struct lsa_String logname, servername;
+
+ unknown0.unknown0 = 0x005c;
+ unknown0.unknown1 = 0x0001;
+
+ r.in.unknown0 = &unknown0;
+ init_lsa_String(&logname, "dns server");
+ init_lsa_String(&servername, NULL);
+ r.in.logname = &logname;
+ r.in.servername = &servername;
+ r.in.major_version = 0x00000001;
+ r.in.minor_version = 0x00000001;
+ r.out.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_OpenEventLogW_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!get_policy_handle(tctx, b, &handle))
+ return false;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &handle;
+ r.out.number = &number;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_GetNumRecords_r(b, tctx, &r),
+ "GetNumRecords failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "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_r(b, tctx, &cr),
+ "CloseEventLog failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result,
+ "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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ uint32_t sent_size = 0;
+ uint32_t real_size = 0;
+
+ if (!get_policy_handle(tctx, b, &handle))
+ return false;
+
+ ZERO_STRUCT(r);
+ r.in.offset = 0;
+ r.in.handle = &handle;
+ r.in.flags = 0;
+ r.out.data = NULL;
+ r.out.sent_size = &sent_size;
+ r.out.real_size = &real_size;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_ReadEventLogW_r(b, tctx, &r),
+ "ReadEventLog failed");
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER,
+ "ReadEventLog failed");
+
+ while (1) {
+ struct EVENTLOGRECORD rec;
+ enum ndr_err_code ndr_err;
+ uint32_t size = 0;
+ uint32_t pos = 0;
+
+ /* Read first for number of bytes in record */
+
+ r.in.number_of_bytes = 0;
+ r.in.flags = EVENTLOG_BACKWARDS_READ|EVENTLOG_SEQUENTIAL_READ;
+ r.out.data = NULL;
+ r.out.sent_size = &sent_size;
+ r.out.real_size = &real_size;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_ReadEventLogW_r(b, tctx, &r),
+ "ReadEventLogW failed");
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_END_OF_FILE)) {
+ /* FIXME: still need to decode then */
+ 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);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_ReadEventLogW_r(b, tctx, &r),
+ "ReadEventLogW failed");
+
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ReadEventLog failed");
+
+ /* Decode a user-marshalled record */
+ size = IVAL(r.out.data, pos);
+
+ while (size > 0) {
+ DATA_BLOB blob = data_blob_const(
+ r.out.data + pos, size);
+ dump_data(0, blob.data, blob.length);
+
+ ndr_err = ndr_pull_struct_blob_all(&blob, tctx, &rec,
+ (ndr_pull_flags_fn_t)ndr_pull_EVENTLOGRECORD);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ torture_assert_ntstatus_ok(tctx, status,
+ "ReadEventLog failed parsing event log record");
+ }
+
+ NDR_PRINT_DEBUG(EVENTLOGRECORD, &rec);
+
+ pos += size;
+
+ if (pos + 4 > *r.out.sent_size) {
+ break;
+ }
+
+ size = IVAL(r.out.data, pos);
+ }
+
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "ReadEventLog failed parsing event log record");
+
+ r.in.offset++;
+ }
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr),
+ "CloseEventLog failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result,
+ "CloseEventLog failed");
+
+ return true;
+}
+
+static bool test_ReportEventLog(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct eventlog_ReportEventW r;
+ struct eventlog_CloseEventLog cr;
+ struct policy_handle handle;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ uint32_t record_number = 0;
+ time_t time_written = 0;
+ struct lsa_String servername, *strings;
+
+ if (!get_policy_handle(tctx, b, &handle))
+ return false;
+
+ init_lsa_String(&servername, NULL);
+
+ strings = talloc_array(tctx, struct lsa_String, 1);
+ init_lsa_String(&strings[0], "Currently tortured by samba 4");
+
+ ZERO_STRUCT(r);
+
+ r.in.handle = &handle;
+ r.in.timestamp = time(NULL);
+ r.in.event_type = EVENTLOG_INFORMATION_TYPE;
+ r.in.event_category = 0;
+ r.in.event_id = 0;
+ r.in.num_of_strings = 1;
+ r.in.data_size = 0;
+ r.in.servername = &servername;
+ r.in.user_sid = NULL;
+ r.in.strings = &strings;
+ r.in.data = NULL;
+ r.in.flags = 0;
+ r.in.record_number = r.out.record_number = &record_number;
+ r.in.time_written = r.out.time_written = &time_written;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_ReportEventW_r(b, tctx, &r),
+ "ReportEventW failed");
+
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ReportEventW failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr),
+ "CloseEventLog failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result,
+ "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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!get_policy_handle(tctx, b, &handle))
+ return false;
+
+ r.in.handle = &handle;
+
+ /* Huh? Does this RPC always return access denied? */
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_FlushEventLog_r(b, tctx, &r),
+ "FlushEventLog failed");
+
+ torture_assert_ntstatus_equal(tctx,
+ r.out.result,
+ NT_STATUS_ACCESS_DENIED,
+ "FlushEventLog failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr),
+ "CloseEventLog failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result,
+ "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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!get_policy_handle(tctx, b, &handle))
+ return false;
+
+ r.in.handle = &handle;
+ r.in.backupfile = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_ClearEventLogW_r(b, tctx, &r),
+ "ClearEventLog failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "ClearEventLog failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr),
+ "CloseEventLog failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result,
+ "CloseEventLog failed");
+
+ return true;
+}
+
+static bool test_GetLogInformation(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct eventlog_GetLogInformation r;
+ struct eventlog_CloseEventLog cr;
+ struct policy_handle handle;
+ uint32_t bytes_needed = 0;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!get_policy_handle(tctx, b, &handle))
+ return false;
+
+ r.in.handle = &handle;
+ r.in.level = 1;
+ r.in.buf_size = 0;
+ r.out.buffer = NULL;
+ r.out.bytes_needed = &bytes_needed;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_GetLogInformation_r(b, tctx, &r),
+ "GetLogInformation failed");
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_LEVEL,
+ "GetLogInformation failed");
+
+ r.in.level = 0;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_GetLogInformation_r(b, tctx, &r),
+ "GetLogInformation failed");
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_BUFFER_TOO_SMALL,
+ "GetLogInformation failed");
+
+ r.in.buf_size = bytes_needed;
+ r.out.buffer = talloc_array(tctx, uint8_t, bytes_needed);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_GetLogInformation_r(b, tctx, &r),
+ "GetLogInformation failed");
+
+ torture_assert_ntstatus_ok(tctx, r.out.result, "GetLogInformation failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr),
+ "CloseEventLog failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result,
+ "CloseEventLog failed");
+
+ return true;
+}
+
+
+static bool test_OpenEventLog(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct policy_handle handle;
+ struct eventlog_CloseEventLog cr;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!get_policy_handle(tctx, b, &handle))
+ return false;
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr),
+ "CloseEventLog failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result,
+ "CloseEventLog failed");
+
+ return true;
+}
+
+static bool test_BackupLog(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct policy_handle handle, backup_handle;
+ struct eventlog_BackupEventLogW r;
+ struct eventlog_OpenBackupEventLogW br;
+ struct eventlog_CloseEventLog cr;
+ const char *tmp;
+ struct lsa_String backup_filename;
+ struct eventlog_OpenUnknown0 unknown0;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping BackupLog test against samba");
+ }
+
+ if (!get_policy_handle(tctx, b, &handle))
+ return false;
+
+ tmp = talloc_asprintf(tctx, "C:\\%s", TEST_BACKUP_NAME);
+ init_lsa_String(&backup_filename, tmp);
+
+ r.in.handle = &handle;
+ r.in.backup_filename = &backup_filename;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_BackupEventLogW_r(b, tctx, &r),
+ "BackupEventLogW failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result,
+ NT_STATUS_OBJECT_PATH_SYNTAX_BAD, "BackupEventLogW failed");
+
+ tmp = talloc_asprintf(tctx, "\\??\\C:\\%s", TEST_BACKUP_NAME);
+ init_lsa_String(&backup_filename, tmp);
+
+ r.in.handle = &handle;
+ r.in.backup_filename = &backup_filename;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_BackupEventLogW_r(b, tctx, &r),
+ "BackupEventLogW failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "BackupEventLogW failed");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_BackupEventLogW_r(b, tctx, &r),
+ "BackupEventLogW failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result,
+ NT_STATUS_OBJECT_NAME_COLLISION, "BackupEventLogW failed");
+
+ cr.in.handle = cr.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr),
+ "BackupLog failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result,
+ "BackupLog failed");
+
+ unknown0.unknown0 = 0x005c;
+ unknown0.unknown1 = 0x0001;
+
+ br.in.unknown0 = &unknown0;
+ br.in.backup_logname = &backup_filename;
+ br.in.major_version = 1;
+ br.in.minor_version = 1;
+ br.out.handle = &backup_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_eventlog_OpenBackupEventLogW_r(b, tctx, &br),
+ "OpenBackupEventLogW failed");
+
+ torture_assert_ntstatus_ok(tctx, br.out.result, "OpenBackupEventLogW failed");
+
+ cr.in.handle = cr.out.handle = &backup_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_eventlog_CloseEventLog_r(b, tctx, &cr),
+ "CloseEventLog failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result,
+ "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, "ReportEventLog", test_ReportEventLog);
+ torture_rpc_tcase_add_test(tcase, "FlushEventLog", test_FlushEventLog);
+ torture_rpc_tcase_add_test(tcase, "GetLogIntormation", test_GetLogInformation);
+ torture_rpc_tcase_add_test(tcase, "BackupLog", test_BackupLog);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/forest_trust.c b/source4/torture/rpc/forest_trust.c
new file mode 100644
index 0000000..ceb1a7e
--- /dev/null
+++ b/source4/torture/rpc/forest_trust.c
@@ -0,0 +1,914 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for forest trust
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2010
+
+ 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/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/credentials.h"
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#undef strcasecmp
+
+#define TEST_DOM "torturedom"
+#define TEST_DOM_DNS "torturedom.samba.example.com"
+#define TEST_DOM_SID "S-1-5-21-97398-379795-10000"
+#define TEST_MACHINE_NAME "lsatestmach"
+
+
+static bool test_get_policy_handle(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ uint32_t access_mask,
+ struct policy_handle **handle )
+{
+ struct policy_handle *h;
+ struct lsa_OpenPolicy2 pr;
+ struct lsa_ObjectAttribute attr;
+ NTSTATUS status;
+
+ h = talloc(tctx, struct policy_handle);
+ torture_assert(tctx, h != NULL, "talloc(tctx, struct policy_handle)");
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = NULL;
+
+ pr.in.system_name = "\\";
+ pr.in.attr = &attr;
+ pr.in.access_mask = access_mask;
+ pr.out.handle = h;
+
+ status = dcerpc_lsa_OpenPolicy2_r(p->binding_handle, tctx, &pr);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPolicy2 failed");
+ torture_assert_ntstatus_ok(tctx, pr.out.result, "OpenPolicy2 failed");
+
+ *handle = h;
+ return true;
+}
+
+static bool test_create_trust_and_set_info(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ const char *trust_name,
+ const char *trust_name_dns,
+ struct dom_sid *domsid,
+ struct lsa_TrustDomainInfoAuthInfoInternal *authinfo)
+{
+ struct policy_handle *handle;
+ struct lsa_lsaRSetForestTrustInformation fti;
+ struct lsa_ForestTrustCollisionInfo *collision_info = NULL;
+ struct lsa_Close cr;
+ struct policy_handle closed_handle;
+ struct lsa_CreateTrustedDomainEx2 r;
+ struct lsa_TrustDomainInfoInfoEx trustinfo;
+ struct policy_handle trustdom_handle;
+ struct lsa_QueryTrustedDomainInfo q;
+ union lsa_TrustedDomainInfo *info = NULL;
+
+ if (!test_get_policy_handle(tctx, p,
+ (LSA_POLICY_VIEW_LOCAL_INFORMATION |
+ LSA_POLICY_TRUST_ADMIN |
+ LSA_POLICY_CREATE_SECRET), &handle)) {
+ return false;
+ }
+
+ torture_comment(tctx, "\nTesting CreateTrustedDomainEx2\n");
+
+ trustinfo.sid = domsid;
+ trustinfo.netbios_name.string = trust_name;
+ trustinfo.domain_name.string = trust_name_dns;
+
+ trustinfo.trust_direction = LSA_TRUST_DIRECTION_INBOUND |
+ LSA_TRUST_DIRECTION_OUTBOUND;
+
+ trustinfo.trust_type = LSA_TRUST_TYPE_UPLEVEL;
+
+ /*
+ * MS-LSAD: Section 3.1.4.7.10 makes it clear that Win2k3
+ * functional level and above return
+ * NT_STATUS_INVALID_DOMAIN_STATE if
+ * TRUST_ATTRIBUTE_FOREST_TRANSITIVE or
+ * TRUST_ATTRIBUTE_CROSS_ORGANIZATION is set here.
+ *
+ * But we really want to test forest trusts here.
+ */
+ trustinfo.trust_attributes = LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
+
+ r.in.policy_handle = handle;
+ r.in.info = &trustinfo;
+ r.in.auth_info_internal = authinfo;
+ /* LSA_TRUSTED_QUERY_DOMAIN_NAME is needed for for following
+ * QueryTrustedDomainInfo call, although it seems that Windows does not
+ * expect this */
+ r.in.access_mask = LSA_TRUSTED_SET_POSIX | LSA_TRUSTED_SET_AUTH | LSA_TRUSTED_QUERY_DOMAIN_NAME;
+ r.out.trustdom_handle = &trustdom_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_CreateTrustedDomainEx2_r(p->binding_handle, tctx, &r),
+ "CreateTrustedDomainEx2 failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "CreateTrustedDomainEx2 failed");
+
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX;
+ q.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_QueryTrustedDomainInfo_r(p->binding_handle, tctx, &q),
+ "QueryTrustedDomainInfo failed");
+ torture_assert_ntstatus_ok(tctx, q.out.result, "QueryTrustedDomainInfo level 1");
+ torture_assert(tctx, q.out.info != NULL, "QueryTrustedDomainInfo level 1 failed to return an info pointer");
+ torture_assert_str_equal(tctx, info->info_ex.netbios_name.string,
+ trustinfo.netbios_name.string,
+ "QueryTrustedDomainInfo returned inconsistent short name");
+ torture_assert_int_equal(tctx, info->info_ex.trust_type, trustinfo.trust_type,
+ "QueryTrustedDomainInfo returned incorrect trust type");
+ torture_assert_int_equal(tctx, info->info_ex.trust_attributes, trustinfo.trust_attributes,
+ "QueryTrustedDomainInfo of returned incorrect trust attributes");
+ torture_assert_int_equal(tctx, info->info_ex.trust_direction, trustinfo.trust_direction,
+ "QueryTrustedDomainInfo of returned incorrect trust direction");
+
+ fti.in.handle = handle;
+ fti.in.trusted_domain_name = talloc_zero(tctx, struct lsa_StringLarge);
+ fti.in.trusted_domain_name->string = trust_name_dns;
+ fti.in.highest_record_type = 2;
+ fti.in.forest_trust_info = talloc_zero(tctx, struct lsa_ForestTrustInformation);
+ fti.in.forest_trust_info->count = 2;
+ fti.in.forest_trust_info->entries = talloc_array(tctx, struct lsa_ForestTrustRecord *, 2);
+ fti.in.forest_trust_info->entries[0] = talloc_zero(tctx, struct lsa_ForestTrustRecord);
+ fti.in.forest_trust_info->entries[0]->flags = 0;
+ fti.in.forest_trust_info->entries[0]->type = LSA_FOREST_TRUST_TOP_LEVEL_NAME;
+ fti.in.forest_trust_info->entries[0]->time = 0;
+ fti.in.forest_trust_info->entries[0]->forest_trust_data.top_level_name.string = trust_name_dns;
+ fti.in.forest_trust_info->entries[1] = talloc_zero(tctx, struct lsa_ForestTrustRecord);
+ fti.in.forest_trust_info->entries[1]->flags = 0;
+ fti.in.forest_trust_info->entries[1]->type = LSA_FOREST_TRUST_DOMAIN_INFO;
+ fti.in.forest_trust_info->entries[1]->time = 0;
+ fti.in.forest_trust_info->entries[1]->forest_trust_data.domain_info.domain_sid = domsid;
+ fti.in.forest_trust_info->entries[1]->forest_trust_data.domain_info.dns_domain_name.string = trust_name_dns;
+ fti.in.forest_trust_info->entries[1]->forest_trust_data.domain_info.netbios_domain_name.string = trust_name;
+ fti.in.check_only = 0;
+ fti.out.collision_info = &collision_info;
+
+ torture_comment(tctx, "\nTesting SetForestTrustInformation\n");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_lsaRSetForestTrustInformation_r(p->binding_handle, tctx, &fti),
+ "lsaRSetForestTrustInformation failed");
+ torture_assert_ntstatus_ok(tctx, fti.out.result, "lsaRSetForestTrustInformation failed");
+
+ cr.in.handle = handle;
+ cr.out.handle = &closed_handle;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr),
+ "Close failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed");
+
+ return true;
+}
+
+struct get_set_info {
+ enum lsa_TrustDomInfoEnum info_level;
+ NTSTATUS get_result;
+ NTSTATUS set_result;
+};
+
+static bool get_and_set_info(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ const char *name)
+{
+ struct policy_handle *handle;
+ NTSTATUS status;
+ struct lsa_QueryTrustedDomainInfoByName qr;
+ struct lsa_SetTrustedDomainInfoByName sr;
+ union lsa_TrustedDomainInfo *info;
+ struct lsa_Close cr;
+ struct policy_handle closed_handle;
+ size_t c;
+
+ struct get_set_info il[] = {
+ {LSA_TRUSTED_DOMAIN_INFO_NAME, NT_STATUS_OK, NT_STATUS_INVALID_PARAMETER},
+ /* {LSA_TRUSTED_DOMAIN_INFO_CONTROLLERS, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */
+ {LSA_TRUSTED_DOMAIN_INFO_POSIX_OFFSET, NT_STATUS_OK, NT_STATUS_OK},
+ /* {LSA_TRUSTED_DOMAIN_INFO_PASSWORD, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */
+ /* {LSA_TRUSTED_DOMAIN_INFO_BASIC, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */
+ {LSA_TRUSTED_DOMAIN_INFO_INFO_EX, NT_STATUS_OK, NT_STATUS_OK},
+ /* {LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */
+ {LSA_TRUSTED_DOMAIN_INFO_FULL_INFO, NT_STATUS_OK, NT_STATUS_OK},
+ /* {LSA_TRUSTED_DOMAIN_INFO_AUTH_INFO_INTERNAL, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */
+ /* {LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_INTERNAL, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */
+ /* {LSA_TRUSTED_DOMAIN_INFO_INFO_EX2_INTERNAL, NT_STATUS_INVALID_PARAMETER, NT_STATUS_INVALID_INFO_CLASS}, */
+ {LSA_TRUSTED_DOMAIN_INFO_FULL_INFO_2_INTERNAL, NT_STATUS_OK, NT_STATUS_INVALID_PARAMETER},
+ {LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES, NT_STATUS_OK, NT_STATUS_OK},
+ { .info_level = -1, },
+ };
+
+ torture_comment(tctx, "\nGetting/Setting dom info\n");
+
+ if(!test_get_policy_handle(tctx, p, LSA_POLICY_VIEW_LOCAL_INFORMATION,
+ &handle)) {
+ return false;
+ }
+
+ qr.in.handle = handle;
+ qr.in.trusted_domain = talloc_zero(tctx, struct lsa_String);
+ qr.in.trusted_domain->string = name;
+ qr.out.info = &info;
+
+ sr.in.handle = handle;
+ sr.in.trusted_domain = talloc_zero(tctx, struct lsa_String);
+ sr.in.trusted_domain->string = name;
+ sr.in.info = info;
+
+ for (c = 0; il[c].info_level != -1; c++) {
+ torture_comment(tctx, "\nGetting/Setting dom info [%d]\n",il[c].info_level);
+
+ qr.in.level = il[c].info_level;
+ status = dcerpc_lsa_QueryTrustedDomainInfoByName_r(p->binding_handle,
+ tctx, &qr);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK,
+ "QueryTrustedDomainInfoByName failed");
+ if (!NT_STATUS_EQUAL(qr.out.result, il[c].get_result)) {
+ torture_comment(tctx, "QueryTrustedDomainInfoByName did not return "
+ "%s but %s\n",
+ nt_errstr(il[c].get_result),
+ nt_errstr(qr.out.result));
+
+ /* We may be testing a server without support for this level */
+ if (qr.in.level == LSA_TRUSTED_DOMAIN_SUPPORTED_ENCRYPTION_TYPES && NT_STATUS_EQUAL(qr.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ return true;
+ }
+ return false;
+ }
+
+ sr.in.level = il[c].info_level;
+ sr.in.info = info;
+ status = dcerpc_lsa_SetTrustedDomainInfoByName_r(p->binding_handle,
+ tctx, &sr);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK,
+ "SetTrustedDomainInfoByName failed");
+ if (!NT_STATUS_EQUAL(sr.out.result, il[c].set_result)) {
+ torture_comment(tctx, "SetTrustedDomainInfoByName did not return "
+ "%s but %s\n",
+ nt_errstr(il[c].set_result),
+ nt_errstr(sr.out.result));
+ return false;
+ }
+ }
+
+ cr.in.handle = handle;
+ cr.out.handle = &closed_handle;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr),
+ "Close failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed");
+
+ return true;
+}
+
+static bool check_name(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *name)
+{
+ struct policy_handle *handle;
+ NTSTATUS status;
+ struct lsa_QueryTrustedDomainInfoByName qr;
+ union lsa_TrustedDomainInfo *info;
+ struct lsa_Close cr;
+ struct policy_handle closed_handle;
+
+ torture_comment(tctx, "\nGetting LSA_TRUSTED_DOMAIN_INFO_FULL_INFO\n");
+
+ if(!test_get_policy_handle(tctx, p, LSA_POLICY_VIEW_LOCAL_INFORMATION,
+ &handle)) {
+ return false;
+ }
+
+ qr.in.handle = handle;
+ qr.in.trusted_domain = talloc_zero(tctx, struct lsa_String);
+ qr.in.trusted_domain->string = name;
+ qr.in.level = LSA_TRUSTED_DOMAIN_INFO_FULL_INFO;
+ qr.out.info = &info;
+ status = dcerpc_lsa_QueryTrustedDomainInfoByName_r(p->binding_handle,
+ tctx, &qr);
+ torture_assert_ntstatus_ok(tctx, status,
+ "QueryInfoPolicy2 failed");
+ torture_assert_ntstatus_equal(tctx, qr.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ "QueryInfoPolicy2 did not return "
+ "NT_STATUS_OBJECT_NAME_NOT_FOUND");
+
+ cr.in.handle = handle;
+ cr.out.handle = &closed_handle;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr),
+ "Close failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed");
+
+ return true;
+}
+
+static bool get_lsa_policy_info_dns(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ union lsa_PolicyInformation **info)
+{
+ struct policy_handle *handle;
+ NTSTATUS status;
+ struct lsa_QueryInfoPolicy2 qr;
+ struct lsa_Close cr;
+ struct policy_handle closed_handle;
+
+ torture_comment(tctx, "\nGetting LSA_POLICY_INFO_DNS\n");
+
+ if (!test_get_policy_handle(tctx, p, LSA_POLICY_VIEW_LOCAL_INFORMATION,
+ &handle)) {
+ return false;
+ }
+
+ qr.in.handle = handle;
+ qr.in.level = LSA_POLICY_INFO_DNS;
+ qr.out.info = info;
+ status = dcerpc_lsa_QueryInfoPolicy2_r(p->binding_handle, tctx, &qr);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_OK,
+ "QueryInfoPolicy2 failed");
+ if (!NT_STATUS_IS_OK(qr.out.result)) {
+ torture_comment(tctx, "QueryInfoPolicy2 failed - %s\n",
+ nt_errstr(qr.out.result));
+ return false;
+ }
+
+ cr.in.handle = handle;
+ cr.out.handle = &closed_handle;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr),
+ "Close failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed");
+
+ return true;
+}
+
+static bool delete_trusted_domain_by_sid(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct dom_sid *domsid)
+{
+ struct policy_handle *handle;
+ struct lsa_Close cr;
+ struct policy_handle closed_handle;
+ struct lsa_DeleteTrustedDomain dr;
+
+ torture_comment(tctx, "\nDeleting trusted domain.\n");
+
+ /* Against a windows server it was sufficient to have
+ * LSA_POLICY_VIEW_LOCAL_INFORMATION although the documentations says
+ * otherwise. */
+ if (!test_get_policy_handle(tctx, p, LSA_POLICY_TRUST_ADMIN,
+ &handle)) {
+ return false;
+ }
+
+ dr.in.handle = handle;
+ dr.in.dom_sid = domsid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_DeleteTrustedDomain_r(p->binding_handle, tctx, &dr),
+ "DeleteTrustedDomain failed");
+ torture_assert_ntstatus_ok(tctx, dr.out.result, "DeleteTrustedDomain failed");
+
+ cr.in.handle = handle;
+ cr.out.handle = &closed_handle;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_Close_r(p->binding_handle, tctx, &cr),
+ "Close failed");
+ torture_assert_ntstatus_ok(tctx, cr.out.result, "Close failed");
+
+ return true;
+}
+
+/*
+static const uint8_t my_blob[] = {
+0xa3,0x0b,0x32,0x45,0x8b,0x84,0x3b,0x01,0x68,0xe8,0x2b,0xbb,0x00,0x13,0x69,0x1f,
+0x10,0x35,0x72,0xa9,0x4f,0x77,0xb7,0xeb,0x59,0x08,0x07,0xc3,0xe8,0x17,0x00,0xc5,
+0xf2,0xa9,0x6d,0xb7,0x69,0x45,0x63,0x20,0xcb,0x44,0x44,0x22,0x02,0xe3,0x28,0x84,
+0x9b,0xd5,0x43,0x6f,0x8d,0x36,0x9b,0x9b,0x3b,0x31,0x86,0x84,0x8b,0xf2,0x36,0xd4,
+0xe8,0xc4,0xee,0x90,0x0c,0xcb,0x3e,0x11,0x2f,0x86,0xfe,0x87,0x6d,0xce,0xae,0x0c,
+0x83,0xfb,0x21,0x22,0x6d,0x7f,0x5e,0x08,0x71,0x1a,0x35,0xf4,0x5a,0x76,0x9b,0xf7,
+0x54,0x62,0xa5,0x4c,0xcd,0xf6,0xa5,0xb0,0x0b,0xc7,0x79,0xe1,0x6f,0x85,0x16,0x6f,
+0x82,0xdd,0x15,0x11,0x4c,0x9d,0x26,0x01,0x74,0x7e,0xbb,0xec,0x88,0x1d,0x71,0x9e,
+0x5f,0xb2,0x9c,0xab,0x66,0x20,0x08,0x3d,0xae,0x07,0x2d,0xbb,0xa6,0xfb,0xec,0xcc,
+0x51,0x58,0x48,0x47,0x38,0x3b,0x47,0x66,0xe8,0x17,0xfa,0x54,0x5c,0x95,0x73,0x29,
+0xdf,0x7e,0x4a,0xb4,0x45,0x30,0xf7,0xbf,0xc0,0x56,0x6d,0x80,0xf6,0x11,0x56,0x93,
+0xeb,0x97,0xd5,0x10,0xd6,0xd6,0xf7,0x23,0xc3,0xc0,0x93,0xa7,0x5c,0xa9,0xc0,0x81,
+0x55,0x3d,0xec,0x03,0x31,0x7e,0x9d,0xf9,0xd0,0x9e,0xb5,0xc7,0xef,0xa8,0x54,0xf6,
+0x9c,0xdc,0x0d,0xd4,0xd7,0xee,0x8d,0x5f,0xbd,0x89,0x48,0x3b,0x63,0xff,0xe8,0xca,
+0x10,0x64,0x61,0xdf,0xfd,0x50,0xff,0x51,0xa0,0x2c,0xd7,0x8a,0xf1,0x13,0x02,0x02,
+0x71,0xe9,0xff,0x0d,0x03,0x48,0xf8,0x08,0x8d,0xd5,0xe6,0x31,0x9f,0xf0,0x26,0x07,
+0x91,0x6d,0xd3,0x01,0x91,0x92,0xc7,0x28,0x18,0x58,0xd8,0xf6,0x1b,0x97,0x8d,0xd0,
+0xd2,0xa1,0x7c,0xae,0xc1,0xca,0xfe,0x20,0x91,0x1c,0x4d,0x15,0x89,0x29,0x37,0xd5,
+0xf5,0xca,0x40,0x2b,0x03,0x8f,0x7b,0xc2,0x10,0xb4,0xd3,0xe8,0x14,0xb0,0x9b,0x5d,
+0x85,0x30,0xe5,0x13,0x24,0xf7,0x78,0xec,0xbe,0x0b,0x9a,0x3f,0xb5,0x76,0xd9,0x0d,
+0x49,0x64,0xa4,0xa7,0x33,0x88,0xdd,0xe9,0xe2,0x5f,0x04,0x51,0xdd,0x89,0xe2,0x68,
+0x5b,0x5f,0x64,0x35,0xe3,0x23,0x4a,0x0e,0x09,0x15,0xcc,0x97,0x47,0xf4,0xc2,0x4f,
+0x06,0xc3,0x96,0xa9,0x2f,0xb3,0xde,0x29,0x10,0xc7,0xf5,0x16,0xc5,0x3c,0x84,0xd2,
+0x9b,0x6b,0xaa,0x54,0x59,0x8d,0x94,0xde,0xd1,0x75,0xb6,0x08,0x0d,0x7d,0xf1,0x18,
+0xc8,0xf5,0xdf,0xaa,0xcd,0xec,0xab,0xb6,0xd1,0xcb,0xdb,0xe7,0x75,0x5d,0xbe,0x76,
+0xea,0x1d,0x01,0xc8,0x0b,0x2d,0x32,0xe9,0xa8,0x65,0xbb,0x4a,0xcb,0x72,0xbc,0xda,
+0x04,0x7f,0x82,0xfb,0x04,0xeb,0xd8,0xe1,0xb9,0xb1,0x1e,0xdc,0xb3,0x60,0xf3,0x55,
+0x1e,0xcf,0x90,0x6a,0x15,0x74,0x4d,0xff,0xb4,0xc7,0xc9,0xc2,0x4f,0x67,0x9e,0xeb,
+0x00,0x61,0x02,0xe3,0x9e,0x59,0x88,0x20,0xf1,0x0c,0xbe,0xe0,0x26,0x69,0x63,0x67,
+0x72,0x3c,0x06,0x00,0x9e,0x4f,0xc7,0xa6,0x4d,0x6c,0xbe,0x68,0x8e,0xf4,0x32,0x36,
+0x2e,0x5f,0xa6,0xcf,0xa7,0x19,0x40,0x2b,0xbd,0xa2,0x22,0x73,0xc4,0xb6,0xe3,0x86,
+0x64,0xeb,0xb1,0xc7,0x45,0x7d,0xd6,0xd9,0x36,0xf1,0x04,0xd4,0x61,0xdc,0x41,0xb7,
+0x01,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, 0x30,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x02,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x31,0x00,0x32,0x00,0x33,0x00,0x34,0x00,
+0x35,0x00,0x36,0x00,0x37,0x00,0x38,0x00,0x39,0x00,0x30,0x00,0x01,0x00,0x00,0x00,
+0x0c,0x00,0x00,0x00, 0x30,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
+0x14,0x00,0x00,0x00,0x31,0x00,0x32,0x00,0x33,0x00,0x34,0x00,0x35,0x00,0x36,0x00,
+0x37,0x00,0x38,0x00,0x39,0x00,0x30,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00
+};
+*/
+static bool get_trust_domain_passwords_auth_blob(TALLOC_CTX *mem_ctx,
+ const char *password,
+ DATA_BLOB *auth_blob)
+{
+ struct trustDomainPasswords auth_struct;
+ struct AuthenticationInformation *auth_info_array;
+ enum ndr_err_code ndr_err;
+ size_t converted_size;
+
+ generate_random_buffer(auth_struct.confounder,
+ sizeof(auth_struct.confounder));
+
+ auth_info_array = talloc_array(mem_ctx,
+ struct AuthenticationInformation, 1);
+ if (auth_info_array == NULL) {
+ return false;
+ }
+
+ auth_info_array[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
+ if (!convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16, password,
+ strlen(password),
+ &auth_info_array[0].AuthInfo.clear.password,
+ &converted_size)) {
+ return false;
+ }
+
+ auth_info_array[0].AuthInfo.clear.size = converted_size;
+
+ auth_struct.outgoing.count = 1;
+ auth_struct.outgoing.current.count = 1;
+ auth_struct.outgoing.current.array = auth_info_array;
+ auth_struct.outgoing.previous.count = 0;
+ auth_struct.outgoing.previous.array = NULL;
+
+ auth_struct.incoming.count = 1;
+ auth_struct.incoming.current.count = 1;
+ auth_struct.incoming.current.array = auth_info_array;
+ auth_struct.incoming.previous.count = 0;
+ auth_struct.incoming.previous.array = NULL;
+
+ ndr_err = ndr_push_struct_blob(auth_blob, mem_ctx, &auth_struct,
+ (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_validate_trust(struct torture_context *tctx,
+ const char *binding,
+ const char *trusting_dom_name,
+ const char *trusting_dom_dns_name,
+ const char *trusted_dom_name,
+ const char *trusted_dom_dns_name,
+ const char *trust_password)
+{
+ struct netr_ServerGetTrustInfo r;
+
+ struct netr_Authenticator a;
+ struct netr_Authenticator return_authenticator;
+ struct samr_Password new_owf_password;
+ struct samr_Password old_owf_password;
+ struct netr_TrustInfo *trust_info;
+
+ struct netlogon_creds_CredentialState *creds;
+
+ NTSTATUS status;
+ struct cli_credentials *credentials;
+ struct dcerpc_binding *b;
+ struct dcerpc_pipe *p1 = NULL;
+ struct dcerpc_pipe *p = NULL;
+
+ struct netr_GetForestTrustInformation fr;
+ struct lsa_ForestTrustInformation *forest_trust_info;
+ struct lsa_ForestTrustRecord *tln = NULL;
+ struct lsa_ForestTrustRecord *di = NULL;
+ int i;
+ struct samr_Password *new_nt_hash;
+ struct samr_Password *old_nt_hash;
+ char *dummy;
+ uint32_t trust_attributes = LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
+
+ status = dcerpc_parse_binding(tctx, binding, &b);
+ torture_assert_ntstatus_ok(tctx, status, "Bad binding string");
+
+ credentials = cli_credentials_init(tctx);
+ torture_assert(tctx, credentials != NULL, "cli_credentials_init()");
+
+ dummy = talloc_asprintf(tctx, "%s$", trusted_dom_name);
+ cli_credentials_set_username(credentials, dummy,
+ CRED_SPECIFIED);
+ cli_credentials_set_domain(credentials, trusting_dom_name,
+ CRED_SPECIFIED);
+ cli_credentials_set_realm(credentials, trusting_dom_dns_name,
+ CRED_SPECIFIED);
+ cli_credentials_set_password(credentials, trust_password, CRED_SPECIFIED);
+ cli_credentials_set_old_password(credentials, trust_password, CRED_SPECIFIED);
+ cli_credentials_set_workstation(credentials,
+ trusted_dom_name, CRED_SPECIFIED);
+ cli_credentials_set_secure_channel_type(credentials, SEC_CHAN_DOMAIN);
+
+ status = dcerpc_pipe_connect_b(tctx, &p1, b,
+ &ndr_table_netlogon, credentials,
+ tctx->ev, tctx->lp_ctx);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ torture_comment(tctx, "Failed to connect to remote server: %s with %s - %s\n",
+ binding,
+ cli_credentials_get_unparsed_name(credentials, tctx),
+ nt_errstr(status));
+ return false;
+ }
+
+ if (!test_SetupCredentials3(p1, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES,
+ credentials, &creds)) {
+ torture_comment(tctx, "test_SetupCredentials3 failed.\n");
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(p1, tctx, credentials, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p)) {
+ torture_comment(tctx, "test_SetupCredentialsPipe failed.\n");
+ return false;
+ }
+
+ netlogon_creds_client_authenticator(creds, &a);
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.account_name = talloc_asprintf(tctx, "%s$", trusted_dom_name);
+ r.in.secure_channel_type = cli_credentials_get_secure_channel_type(credentials);
+ r.in.computer_name = trusted_dom_name;
+ r.in.credential = &a;
+
+ r.out.return_authenticator = &return_authenticator;
+ r.out.new_owf_password = &new_owf_password;
+ r.out.old_owf_password = &old_owf_password;
+ r.out.trust_info = &trust_info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_ServerGetTrustInfo_r(p->binding_handle, tctx, &r),
+ "ServerGetTrustInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "ServerGetTrustInfo failed");
+
+ torture_assert(tctx, trust_info != NULL, "ServerGetTrustInfo got no trust_info");
+ torture_assert_int_equal(tctx, trust_info->count, 1,
+ "Unexpected number of results");
+ torture_assert_int_equal(tctx, trust_info->data[0], trust_attributes,
+ "Unexpected trust_attributes");
+
+ new_nt_hash = cli_credentials_get_nt_hash(credentials, tctx);
+ torture_assert(tctx, new_nt_hash != NULL, "cli_credentials_get_nt_hash()");
+
+ old_nt_hash = cli_credentials_get_old_nt_hash(credentials, tctx);
+ torture_assert(tctx, old_nt_hash != NULL, "cli_credentials_get_old_nt_hash()");
+
+ netlogon_creds_des_decrypt(creds, &new_owf_password);
+ netlogon_creds_des_decrypt(creds, &old_owf_password);
+
+ dump_data(1, new_owf_password.hash, 16);
+ dump_data(1, new_nt_hash->hash, 16);
+ dump_data(1, old_owf_password.hash, 16);
+ dump_data(1, old_nt_hash->hash, 16);
+
+ torture_assert_mem_equal(tctx, new_owf_password.hash, new_nt_hash->hash, 16,
+ "received unexpected new owf password\n");
+
+ torture_assert_mem_equal(tctx, old_owf_password.hash, old_nt_hash->hash, 16,
+ "received unexpected old owf password\n");
+
+ netlogon_creds_client_authenticator(creds, &a);
+
+ fr.in.server_name = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ fr.in.computer_name = trusted_dom_name;
+ fr.in.credential = &a;
+ fr.in.flags = 0;
+ fr.out.return_authenticator = &return_authenticator;
+ fr.out.forest_trust_info = &forest_trust_info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_GetForestTrustInformation_r(p->binding_handle, tctx, &fr),
+ "netr_GetForestTrustInformation failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "netr_GetForestTrustInformation failed");
+
+ for(i = 0; i < forest_trust_info->count; i++) {
+ struct lsa_ForestTrustRecord *e = forest_trust_info->entries[i];
+
+ switch (e->type) {
+ case LSA_FOREST_TRUST_TOP_LEVEL_NAME:
+ if (strcmp(e->forest_trust_data.top_level_name.string, trusting_dom_dns_name) != 0) {
+ break;
+ }
+
+ torture_assert(tctx, tln == NULL, "TOP_LEVEL_NAME found twice");
+
+ tln = e;
+ break;
+
+ case LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
+ break;
+
+ case LSA_FOREST_TRUST_DOMAIN_INFO:
+ if (strcmp(e->forest_trust_data.domain_info.dns_domain_name.string, trusting_dom_dns_name) != 0) {
+ break;
+ }
+
+ torture_assert(tctx, di == NULL, "DOMAIN_INFO found twice");
+
+ di = e;
+ break;
+ default:
+ torture_assert_int_equal(tctx, e->type, LSA_FOREST_TRUST_TOP_LEVEL_NAME,
+ "Unexptected LSA_FOREST_TRUST_* type");
+ }
+ }
+
+ torture_assert(tctx, tln != NULL, "TOP_LEVEL_NAME entry missing");
+ torture_assert(tctx, di != NULL, "DOMAIN_INFO entry missing");
+
+ torture_assert_str_equal(tctx, di->forest_trust_data.domain_info.netbios_domain_name.string,
+ trusting_dom_name,
+ "netbios_domain_name mismatch");
+
+ return true;
+}
+
+static bool test_setup_trust(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *netbios_name,
+ const char *dns_name,
+ struct dom_sid *sid,
+ DATA_BLOB *auth_blob)
+
+{
+ DATA_BLOB session_key;
+ struct lsa_TrustDomainInfoAuthInfoInternal authinfo;
+ NTSTATUS status;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t _session_key;
+
+ if (!check_name(p, tctx, netbios_name)) {
+ return false;
+ }
+ if (!check_name(p, tctx, dns_name)) {
+ return false;
+ }
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_fetch_session_key failed - %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ authinfo.auth_blob.data = talloc_memdup(tctx, auth_blob->data,
+ auth_blob->length);
+ if (authinfo.auth_blob.data == NULL) {
+ return false;
+ }
+ authinfo.auth_blob.size = auth_blob->length;
+
+ _session_key = (gnutls_datum_t) {
+ .data = session_key.data,
+ .size = session_key.length,
+ };
+
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &_session_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd,
+ authinfo.auth_blob.data,
+ authinfo.auth_blob.size);
+ gnutls_cipher_deinit(cipher_hnd);
+
+ if (!test_create_trust_and_set_info(p, tctx, netbios_name,
+ dns_name, sid, &authinfo)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool testcase_ForestTrusts(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ const char *dom2_binding_string;
+ const char * dom2_cred_string;
+ NTSTATUS status;
+ struct dom_sid *domsid;
+ DATA_BLOB auth_blob;
+ struct dcerpc_binding *dom2_binding;
+ struct dcerpc_pipe *dom2_p;
+ struct cli_credentials *dom2_credentials;
+ union lsa_PolicyInformation *dom1_info_dns = NULL;
+ union lsa_PolicyInformation *dom2_info_dns = NULL;
+ const char *binding = torture_setting_string(tctx, "binding", NULL);
+ char *test_password;
+
+ torture_comment(tctx, "Testing Forest Trusts\n");
+
+ test_password = generate_random_password(tctx, 32, 64);
+ torture_assert(tctx, test_password != NULL, "test password must be generated");
+
+ if (!get_trust_domain_passwords_auth_blob(tctx, test_password, &auth_blob)) {
+ torture_comment(tctx,
+ "get_trust_domain_passwords_auth_blob failed\n");
+ return false;
+ }
+
+#if 0
+ /* Use the following if get_trust_domain_passwords_auth_blob() cannot
+ * generate a usable blob due to errors in the IDL */
+ auth_blob.data = talloc_memdup(tctx, my_blob, sizeof(my_blob));
+ auth_blob.length = sizeof(my_blob);
+
+ test_password = "1234567890"
+#endif
+
+ domsid = dom_sid_parse_talloc(tctx, TEST_DOM_SID);
+ if (domsid == NULL) {
+ return false;
+ }
+
+ if (!test_setup_trust(tctx, p, TEST_DOM, TEST_DOM_DNS, domsid,
+ &auth_blob)) {
+ return false;
+ }
+
+ if (!get_lsa_policy_info_dns(p, tctx, &dom1_info_dns)) {
+ return false;
+ }
+
+ if (!get_and_set_info(p, tctx, TEST_DOM)) {
+ return false;
+ }
+
+ if (!test_validate_trust(tctx, binding,
+ dom1_info_dns->dns.name.string,
+ dom1_info_dns->dns.dns_domain.string,
+ TEST_DOM, TEST_DOM_DNS, test_password)) {
+ return false;
+ }
+
+ if (!delete_trusted_domain_by_sid(p, tctx, domsid)) {
+ return false;
+ }
+
+ dom2_binding_string = torture_setting_string(tctx,
+ "Forest_Trust_Dom2_Binding",
+ NULL);
+ if (dom2_binding_string == NULL) {
+ torture_skip(tctx, "torture:Forest_Trust_Dom2_Binding not specified\n");
+ }
+
+ status = dcerpc_parse_binding(tctx, dom2_binding_string, &dom2_binding);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_parse_binding()");
+
+ dom2_cred_string = torture_setting_string(tctx,
+ "Forest_Trust_Dom2_Creds",
+ NULL);
+ torture_assert(tctx, dom2_cred_string != NULL, "torture:Forest_Trust_Dom2_Creds missing");
+
+ dom2_credentials = cli_credentials_init(tctx);
+ torture_assert(tctx, dom2_credentials != NULL, "cli_credentials_init()");
+
+ cli_credentials_parse_string(dom2_credentials, dom2_cred_string,
+ CRED_SPECIFIED);
+ cli_credentials_set_workstation(dom2_credentials,
+ TEST_MACHINE_NAME, CRED_SPECIFIED);
+
+ status = dcerpc_pipe_connect_b(tctx, &dom2_p, dom2_binding,
+ &ndr_table_lsarpc, dom2_credentials,
+ tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status, talloc_asprintf(tctx,
+ "Failed to connect to remote server: %s\n",
+ dcerpc_binding_string(tctx, dom2_binding)));
+
+ if (!get_lsa_policy_info_dns(dom2_p, tctx, &dom2_info_dns)) {
+ return false;
+ }
+
+ if (strcasecmp(dom1_info_dns->dns.name.string,
+ dom2_info_dns->dns.name.string) == 0 ||
+ strcasecmp(dom1_info_dns->dns.dns_domain.string,
+ dom2_info_dns->dns.dns_domain.string) == 0)
+ {
+ torture_assert(tctx, false, talloc_asprintf(tctx,
+ "Trusting (%s;%s) and trusted domain (%s;%s) have the "
+ "same name",
+ dom1_info_dns->dns.name.string,
+ dom1_info_dns->dns.dns_domain.string,
+ dom2_info_dns->dns.name.string,
+ dom2_info_dns->dns.dns_domain.string));
+ }
+
+ if (!test_setup_trust(tctx, p, dom2_info_dns->dns.name.string,
+ dom2_info_dns->dns.dns_domain.string,
+ dom2_info_dns->dns.sid, &auth_blob)) {
+ return false;
+ }
+ if (!test_setup_trust(tctx, dom2_p, dom1_info_dns->dns.name.string,
+ dom1_info_dns->dns.dns_domain.string,
+ dom1_info_dns->dns.sid, &auth_blob)) {
+ return false;
+ }
+
+ if (!test_validate_trust(tctx, binding,
+ dom1_info_dns->dns.name.string,
+ dom1_info_dns->dns.dns_domain.string,
+ dom2_info_dns->dns.name.string,
+ dom2_info_dns->dns.dns_domain.string, test_password)) {
+ return false;
+ }
+
+ if (!test_validate_trust(tctx, dom2_binding_string,
+ dom2_info_dns->dns.name.string,
+ dom2_info_dns->dns.dns_domain.string,
+ dom1_info_dns->dns.name.string,
+ dom1_info_dns->dns.dns_domain.string, test_password)) {
+ return false;
+ }
+
+ if (!delete_trusted_domain_by_sid(p, tctx, dom2_info_dns->dns.sid)) {
+ return false;
+ }
+
+ if (!delete_trusted_domain_by_sid(dom2_p, tctx, dom1_info_dns->dns.sid)) {
+ return false;
+ }
+
+ return true;
+}
+
+/* By default this test creates a trust object in the destination server to a
+ * dummy domain. If a second server from a different domain is specified on the
+ * command line a trust is created between those two domains.
+ *
+ * Example:
+ * smbtorture ncacn_np:srv1.dom1.test[print] RPC-LSA-FOREST-TRUST \
+ * -U 'dom1\testadm1%12345678' \
+ * --option=torture:Forest_Trust_Dom2_Binding=ncacn_np:srv2.dom2.test[print] \
+ * --option=torture:Forest_Trust_Dom2_Creds='dom2\testadm2%12345678'
+ */
+
+struct torture_suite *torture_rpc_lsa_forest_trust(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+ struct torture_rpc_tcase *tcase;
+
+ suite = torture_suite_create(mem_ctx, "lsa.forest.trust");
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa-forest-trust",
+ &ndr_table_lsarpc);
+ torture_rpc_tcase_add_test(tcase, "ForestTrust", testcase_ForestTrusts);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/frsapi.c b/source4/torture/rpc/frsapi.c
new file mode 100644
index 0000000..710826a
--- /dev/null
+++ b/source4/torture/rpc/frsapi.c
@@ -0,0 +1,276 @@
+/*
+ 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/rpc/torture_rpc.h"
+#include "librpc/gen_ndr/ndr_frsapi_c.h"
+#include "param/param.h"
+
+static bool test_GetDsPollingIntervalW(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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_r(b, 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_binding_handle *b,
+ 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_r(b, 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)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t i1, i2, i3;
+ uint32_t k1, k2, k3;
+
+ if (!test_GetDsPollingIntervalW(tctx, b, &i1, &i2, &i3)) {
+ return false;
+ }
+
+ if (!test_SetDsPollingIntervalW(tctx, b, i1, i2, i3)) {
+ return false;
+ }
+
+ k1 = i1;
+ k2 = k3 = 0;
+
+ if (!test_SetDsPollingIntervalW(tctx, b, k1, k2, k3)) {
+ return false;
+ }
+
+ if (!test_GetDsPollingIntervalW(tctx, b, &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_binding_handle *b,
+ const char *path,
+ uint32_t type,
+ WERROR werr)
+{
+ struct frsapi_IsPathReplicated r;
+ struct GUID guid;
+ uint32_t replicated, primary, root;
+
+ ZERO_STRUCT(r);
+
+ r.in.path = path;
+ r.in.replica_set_type = type;
+ r.out.replicated = &replicated;
+ r.out.primary = &primary;
+ r.out.root = &root;
+ r.out.replica_set_guid = &guid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_frsapi_IsPathReplicated_r(b, 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_binding_handle *b,
+ const char *path,
+ uint32_t type)
+{
+ return test_IsPathReplicated_err(tctx, b, path, type, WERR_OK);
+}
+
+static bool test_IsPathReplicated(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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, b, NULL, 0,
+ WERR_FRS_ERR_INVALID_SERVICE_PARAMETER)) {
+ ret = false;
+ }
+
+ for (i=0; i<ARRAY_SIZE(lvls); i++) {
+ if (!_test_IsPathReplicated(tctx, b, 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, b, path, lvls[i])) {
+ ret = false;
+ }
+ }
+
+ for (i=0; i<ARRAY_SIZE(lvls); i++) {
+ if (!_test_IsPathReplicated(tctx, b,
+ "C:\\windows\\sysvol\\domain",
+ lvls[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_ForceReplication(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct frsapi_ForceReplication r;
+
+ ZERO_STRUCT(r);
+
+ r.in.replica_set_guid = NULL;
+ r.in.connection_guid = NULL;
+ r.in.replica_set_name = lpcfg_dnsdomain(tctx->lp_ctx);
+ r.in.partner_dns_name = dcerpc_server_name(p);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_frsapi_ForceReplication_r(b, 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)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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_r(b, 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");
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "frsapi",
+ &ndr_table_frsapi);
+
+ torture_rpc_tcase_add_test(tcase, "DsPollingIntervalW",
+ test_DsPollingIntervalW);
+
+ torture_rpc_tcase_add_test(tcase, "IsPathReplicated",
+ test_IsPathReplicated);
+
+ torture_rpc_tcase_add_test(tcase, "ForceReplication",
+ test_ForceReplication);
+
+ torture_rpc_tcase_add_test(tcase, "InfoW",
+ test_InfoW);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/fsrvp.c b/source4/torture/rpc/fsrvp.c
new file mode 100644
index 0000000..63b03f9
--- /dev/null
+++ b/source4/torture/rpc/fsrvp.c
@@ -0,0 +1,968 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for File Server Remote VSS Protocol operations
+
+ Copyright (C) David Disseldorp 2012-2013
+
+ 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/>.
+*/
+
+/*
+ * Windows Server "8" Beta is very picky in how it accepts FSRVP requests, the
+ * client must be a member of the same AD domain, ndr64 and signing must be
+ * negotiated for the DCE/RPC bind. E.g.
+ *
+ * smbtorture ncacn_np:LUTZE[/pipe/FssagentRpc,smb2,ndr64,sign] \
+ * -U 'DOM\user%pw' rpc.fsrvp
+ *
+ * This test suite requires a snapshotable share named FSHARE (see #def below).
+ */
+#include "includes.h"
+#include "lib/param/param.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/util/hresult.h"
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/security_descriptor.h"
+#include "torture/torture.h"
+#include "torture/smb2/proto.h"
+#include "torture/rpc/torture_rpc.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "librpc/gen_ndr/ndr_fsrvp_c.h"
+#include "lib/cmdline/cmdline.h"
+
+#define FSHARE "fsrvp_share"
+#define FNAME "testfss.dat"
+
+static bool test_fsrvp_is_path_supported(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct fss_IsPathSupported r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r);
+ r.in.ShareName = talloc_asprintf(tctx,"\\\\%s\\%s\\",
+ dcerpc_server_name(p),
+ FSHARE);
+ status = dcerpc_fss_IsPathSupported_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "IsPathSupported failed");
+
+ torture_assert(tctx, *r.out.SupportedByThisProvider,
+ "path not supported");
+
+ torture_comment(tctx, "path %s is supported by fsrvp server %s\n",
+ r.in.ShareName, *r.out.OwnerMachineName);
+
+ return true;
+}
+
+static bool test_fsrvp_get_version(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct fss_GetSupportedVersion r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r);
+ status = dcerpc_fss_GetSupportedVersion_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "GetSupportedVersion failed");
+
+ torture_comment(tctx, "got MinVersion %u\n", *r.out.MinVersion);
+ torture_comment(tctx, "got MaxVersion %u\n", *r.out.MaxVersion);
+
+ return true;
+}
+
+static bool test_fsrvp_set_ctx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct fss_SetContext r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r);
+ r.in.Context = FSRVP_CTX_BACKUP;
+ status = dcerpc_fss_SetContext_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "SetContext failed");
+
+ return true;
+}
+
+enum test_fsrvp_inject {
+ TEST_FSRVP_TOUT_NONE = 0,
+ TEST_FSRVP_TOUT_SET_CTX,
+ TEST_FSRVP_TOUT_START_SET,
+ TEST_FSRVP_TOUT_ADD_TO_SET,
+ TEST_FSRVP_TOUT_PREPARE,
+ TEST_FSRVP_TOUT_COMMIT,
+
+ TEST_FSRVP_STOP_B4_EXPOSE,
+};
+
+static bool test_fsrvp_sc_create(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *share,
+ enum test_fsrvp_inject inject,
+ struct fssagent_share_mapping_1 **sc_map)
+{
+ struct fss_IsPathSupported r_pathsupport_get;
+ struct fss_GetSupportedVersion r_version_get;
+ struct fss_SetContext r_context_set;
+ struct fss_StartShadowCopySet r_scset_start;
+ struct fss_AddToShadowCopySet r_scset_add1;
+ struct fss_AddToShadowCopySet r_scset_add2;
+ struct fss_PrepareShadowCopySet r_scset_prep;
+ struct fss_CommitShadowCopySet r_scset_commit;
+ struct fss_ExposeShadowCopySet r_scset_expose;
+ struct fss_GetShareMapping r_sharemap_get;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+ time_t start_time;
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+ struct fssagent_share_mapping_1 *map = NULL;
+ int sleep_time;
+
+ /*
+ * PrepareShadowCopySet & CommitShadowCopySet often exceed the default
+ * 60 second dcerpc request timeout against Windows Server "8" Beta.
+ */
+ dcerpc_binding_handle_set_timeout(b, 240);
+
+ ZERO_STRUCT(r_pathsupport_get);
+ r_pathsupport_get.in.ShareName = share;
+ status = dcerpc_fss_IsPathSupported_r(b, tmp_ctx, &r_pathsupport_get);
+ torture_assert_ntstatus_ok(tctx, status,
+ "IsPathSupported failed");
+ torture_assert_int_equal(tctx, r_pathsupport_get.out.result, 0,
+ "failed IsPathSupported response");
+ torture_assert(tctx, r_pathsupport_get.out.SupportedByThisProvider,
+ "path not supported");
+
+ ZERO_STRUCT(r_version_get);
+ status = dcerpc_fss_GetSupportedVersion_r(b, tmp_ctx, &r_version_get);
+ torture_assert_ntstatus_ok(tctx, status,
+ "GetSupportedVersion failed");
+ torture_assert_int_equal(tctx, r_version_get.out.result, 0,
+ "failed GetSupportedVersion response");
+
+ ZERO_STRUCT(r_context_set);
+ r_context_set.in.Context = FSRVP_CTX_BACKUP;
+ status = dcerpc_fss_SetContext_r(b, tmp_ctx, &r_context_set);
+ torture_assert_ntstatus_ok(tctx, status, "SetContext failed");
+ torture_assert_int_equal(tctx, r_context_set.out.result, 0,
+ "failed SetContext response");
+
+ if (inject == TEST_FSRVP_TOUT_SET_CTX) {
+ sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss",
+ "sequence timeout", 180);
+ torture_comment(tctx, "sleeping for %d\n", sleep_time);
+ smb_msleep((sleep_time * 1000) + 500);
+ }
+
+ ZERO_STRUCT(r_scset_start);
+ r_scset_start.in.ClientShadowCopySetId = GUID_random();
+ status = dcerpc_fss_StartShadowCopySet_r(b, tmp_ctx, &r_scset_start);
+ torture_assert_ntstatus_ok(tctx, status,
+ "StartShadowCopySet failed");
+ if (inject == TEST_FSRVP_TOUT_SET_CTX) {
+ /* expect error due to message sequence timeout after set_ctx */
+ torture_assert_int_equal(tctx, r_scset_start.out.result,
+ FSRVP_E_BAD_STATE,
+ "StartShadowCopySet timeout response");
+ goto done;
+ }
+ torture_assert_int_equal(tctx, r_scset_start.out.result, 0,
+ "failed StartShadowCopySet response");
+ torture_comment(tctx, "%s: shadow-copy set created\n",
+ GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId));
+
+ if (inject == TEST_FSRVP_TOUT_START_SET) {
+ sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss",
+ "sequence timeout", 180);
+ torture_comment(tctx, "sleeping for %d\n", sleep_time);
+ smb_msleep((sleep_time * 1000) + 500);
+ }
+
+ ZERO_STRUCT(r_scset_add1);
+ r_scset_add1.in.ClientShadowCopyId = GUID_random();
+ r_scset_add1.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId;
+ r_scset_add1.in.ShareName = share;
+ status = dcerpc_fss_AddToShadowCopySet_r(b, tmp_ctx, &r_scset_add1);
+ torture_assert_ntstatus_ok(tctx, status,
+ "AddToShadowCopySet failed");
+ if (inject == TEST_FSRVP_TOUT_START_SET) {
+ torture_assert_int_equal(tctx, r_scset_add1.out.result,
+ HRES_ERROR_V(HRES_E_INVALIDARG),
+ "AddToShadowCopySet timeout response");
+ goto done;
+ }
+ torture_assert_int_equal(tctx, r_scset_add1.out.result, 0,
+ "failed AddToShadowCopySet response");
+ torture_comment(tctx, "%s(%s): %s added to shadow-copy set\n",
+ GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId),
+ GUID_string(tmp_ctx, r_scset_add1.out.pShadowCopyId),
+ r_scset_add1.in.ShareName);
+
+ /* attempts to add the same share twice should fail */
+ ZERO_STRUCT(r_scset_add2);
+ r_scset_add2.in.ClientShadowCopyId = GUID_random();
+ r_scset_add2.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId;
+ r_scset_add2.in.ShareName = share;
+ status = dcerpc_fss_AddToShadowCopySet_r(b, tmp_ctx, &r_scset_add2);
+ torture_assert_ntstatus_ok(tctx, status,
+ "AddToShadowCopySet failed");
+ torture_assert_int_equal(tctx, r_scset_add2.out.result,
+ FSRVP_E_OBJECT_ALREADY_EXISTS,
+ "failed AddToShadowCopySet response");
+
+ if (inject == TEST_FSRVP_TOUT_ADD_TO_SET) {
+ sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss",
+ "sequence timeout", 1800);
+ torture_comment(tctx, "sleeping for %d\n", sleep_time);
+ smb_msleep((sleep_time * 1000) + 500);
+ }
+
+ start_time = time_mono(NULL);
+ ZERO_STRUCT(r_scset_prep);
+ r_scset_prep.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId;
+// r_scset_prep.in.TimeOutInMilliseconds = (1800 * 1000); /* win8 */
+ r_scset_prep.in.TimeOutInMilliseconds = (240 * 1000);
+ status = dcerpc_fss_PrepareShadowCopySet_r(b, tmp_ctx, &r_scset_prep);
+ torture_assert_ntstatus_ok(tctx, status,
+ "PrepareShadowCopySet failed");
+ if (inject == TEST_FSRVP_TOUT_ADD_TO_SET) {
+ torture_assert_int_equal(tctx, r_scset_prep.out.result,
+ HRES_ERROR_V(HRES_E_INVALIDARG),
+ "PrepareShadowCopySet tout response");
+ goto done;
+ }
+ torture_assert_int_equal(tctx, r_scset_prep.out.result, 0,
+ "failed PrepareShadowCopySet response");
+ torture_comment(tctx, "%s: prepare completed in %llu secs\n",
+ GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId),
+ (unsigned long long)(time_mono(NULL) - start_time));
+
+ if (inject == TEST_FSRVP_TOUT_PREPARE) {
+ sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss",
+ "sequence timeout", 1800);
+ torture_comment(tctx, "sleeping for %d\n", sleep_time);
+ smb_msleep((sleep_time * 1000) + 500);
+ }
+
+ start_time = time_mono(NULL);
+ ZERO_STRUCT(r_scset_commit);
+ r_scset_commit.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId;
+ r_scset_commit.in.TimeOutInMilliseconds = (180 * 1000); /* win8 */
+ status = dcerpc_fss_CommitShadowCopySet_r(b, tmp_ctx, &r_scset_commit);
+ torture_assert_ntstatus_ok(tctx, status,
+ "CommitShadowCopySet failed");
+ if (inject == TEST_FSRVP_TOUT_PREPARE) {
+ torture_assert_int_equal(tctx, r_scset_commit.out.result,
+ HRES_ERROR_V(HRES_E_INVALIDARG),
+ "CommitShadowCopySet tout response");
+ goto done;
+ }
+ torture_assert_int_equal(tctx, r_scset_commit.out.result, 0,
+ "failed CommitShadowCopySet response");
+ torture_comment(tctx, "%s: commit completed in %llu secs\n",
+ GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId),
+ (unsigned long long)(time_mono(NULL) - start_time));
+
+ if (inject == TEST_FSRVP_TOUT_COMMIT) {
+ sleep_time = lpcfg_parm_int(tctx->lp_ctx, NULL, "fss",
+ "sequence timeout", 180);
+ torture_comment(tctx, "sleeping for %d\n", sleep_time);
+ smb_msleep((sleep_time * 1000) + 500);
+ } else if (inject == TEST_FSRVP_STOP_B4_EXPOSE) {
+ /* return partial snapshot information */
+ map = talloc_zero(tctx, struct fssagent_share_mapping_1);
+ map->ShadowCopySetId = *r_scset_start.out.pShadowCopySetId;
+ map->ShadowCopyId = *r_scset_add1.out.pShadowCopyId;
+ goto done;
+ }
+
+ start_time = time_mono(NULL);
+ ZERO_STRUCT(r_scset_expose);
+ r_scset_expose.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId;
+ r_scset_expose.in.TimeOutInMilliseconds = (120 * 1000); /* win8 */
+ status = dcerpc_fss_ExposeShadowCopySet_r(b, tmp_ctx, &r_scset_expose);
+ torture_assert_ntstatus_ok(tctx, status,
+ "ExposeShadowCopySet failed");
+ if (inject == TEST_FSRVP_TOUT_COMMIT) {
+ torture_assert_int_equal(tctx, r_scset_expose.out.result,
+ HRES_ERROR_V(HRES_E_INVALIDARG),
+ "ExposeShadowCopySet tout response");
+ goto done;
+ }
+ torture_assert_int_equal(tctx, r_scset_expose.out.result, 0,
+ "failed ExposeShadowCopySet response");
+ torture_comment(tctx, "%s: expose completed in %llu secs\n",
+ GUID_string(tmp_ctx, r_scset_start.out.pShadowCopySetId),
+ (unsigned long long)(time_mono(NULL) - start_time));
+
+ ZERO_STRUCT(r_sharemap_get);
+ r_sharemap_get.in.ShadowCopyId = *r_scset_add1.out.pShadowCopyId;
+ r_sharemap_get.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId;
+ r_sharemap_get.in.ShareName = r_scset_add1.in.ShareName;
+ r_sharemap_get.in.Level = 1;
+ status = dcerpc_fss_GetShareMapping_r(b, tmp_ctx, &r_sharemap_get);
+ torture_assert_ntstatus_ok(tctx, status, "GetShareMapping failed");
+ torture_assert_int_equal(tctx, r_sharemap_get.out.result, 0,
+ "failed GetShareMapping response");
+ torture_comment(tctx, "%s(%s): %s is a snapshot of %s at %s\n",
+ GUID_string(tmp_ctx, &r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopySetId),
+ GUID_string(tmp_ctx, &r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopyId),
+ r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopyShareName,
+ r_sharemap_get.out.ShareMapping->ShareMapping1->ShareNameUNC,
+ nt_time_string(tmp_ctx, r_sharemap_get.out.ShareMapping->ShareMapping1->tstamp));
+
+ map = talloc_zero(tctx, struct fssagent_share_mapping_1);
+ map->ShadowCopySetId = r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopySetId;
+ map->ShadowCopyId = r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopyId;
+ map->ShadowCopyShareName
+ = talloc_strdup(tctx, r_sharemap_get.out.ShareMapping->ShareMapping1->ShadowCopyShareName);
+ map->ShareNameUNC
+ = talloc_strdup(tctx, r_sharemap_get.out.ShareMapping->ShareMapping1->ShareNameUNC);
+ map->tstamp = r_sharemap_get.out.ShareMapping->ShareMapping1->tstamp;
+
+ torture_assert(tctx, !GUID_compare(&r_sharemap_get.in.ShadowCopySetId,
+ &map->ShadowCopySetId),
+ "sc_set GUID mismatch in GetShareMapping");
+ torture_assert(tctx, !GUID_compare(&r_sharemap_get.in.ShadowCopyId,
+ &map->ShadowCopyId),
+ "sc GUID mismatch in GetShareMapping");
+
+done:
+ talloc_free(tmp_ctx);
+ *sc_map = map;
+
+ return true;
+}
+
+static bool test_fsrvp_sc_delete(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct fssagent_share_mapping_1 *sc_map)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct fss_DeleteShareMapping r_sharemap_del;
+ NTSTATUS status;
+
+ ZERO_STRUCT(r_sharemap_del);
+ r_sharemap_del.in.ShadowCopySetId = sc_map->ShadowCopySetId;
+ r_sharemap_del.in.ShadowCopyId = sc_map->ShadowCopyId;
+ r_sharemap_del.in.ShareName = sc_map->ShareNameUNC;
+ status = dcerpc_fss_DeleteShareMapping_r(b, tctx, &r_sharemap_del);
+ torture_assert_ntstatus_ok(tctx, status, "DeleteShareMapping failed");
+ torture_assert_int_equal(tctx, r_sharemap_del.out.result, 0,
+ "failed DeleteShareMapping response");
+
+ return true;
+}
+
+static bool test_fsrvp_sc_create_simple(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct fssagent_share_mapping_1 *sc_map;
+ /* no trailing backslash - should work. See note in cmd_fss.c */
+ char *share_unc = talloc_asprintf(tctx, "\\\\%s\\%s",
+ dcerpc_server_name(p), FSHARE);
+
+ torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map),
+ "sc create");
+
+ torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map), "sc del");
+
+ return true;
+}
+
+static bool test_fsrvp_sc_set_abort(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ char *share_unc = talloc_asprintf(tctx, "\\\\%s\\%s\\",
+ dcerpc_server_name(p), FSHARE);
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct fss_IsPathSupported r_pathsupport_get;
+ struct fss_GetSupportedVersion r_version_get;
+ struct fss_SetContext r_context_set;
+ struct fss_StartShadowCopySet r_scset_start;
+ struct fss_AbortShadowCopySet r_scset_abort;
+ struct fss_AddToShadowCopySet r_scset_add;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+
+ ZERO_STRUCT(r_pathsupport_get);
+ r_pathsupport_get.in.ShareName = share_unc;
+ status = dcerpc_fss_IsPathSupported_r(b, tmp_ctx, &r_pathsupport_get);
+ torture_assert_ntstatus_ok(tctx, status,
+ "IsPathSupported failed");
+ torture_assert(tctx, r_pathsupport_get.out.SupportedByThisProvider,
+ "path not supported");
+
+ ZERO_STRUCT(r_version_get);
+ status = dcerpc_fss_GetSupportedVersion_r(b, tmp_ctx, &r_version_get);
+ torture_assert_ntstatus_ok(tctx, status,
+ "GetSupportedVersion failed");
+
+ ZERO_STRUCT(r_context_set);
+ r_context_set.in.Context = FSRVP_CTX_BACKUP;
+ status = dcerpc_fss_SetContext_r(b, tmp_ctx, &r_context_set);
+ torture_assert_ntstatus_ok(tctx, status, "SetContext failed");
+
+ ZERO_STRUCT(r_scset_start);
+ r_scset_start.in.ClientShadowCopySetId = GUID_random();
+ status = dcerpc_fss_StartShadowCopySet_r(b, tmp_ctx, &r_scset_start);
+ torture_assert_ntstatus_ok(tctx, status,
+ "StartShadowCopySet failed");
+
+ ZERO_STRUCT(r_scset_abort);
+ r_scset_abort.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId;
+ status = dcerpc_fss_AbortShadowCopySet_r(b, tmp_ctx, &r_scset_abort);
+ torture_assert_ntstatus_ok(tctx, status,
+ "AbortShadowCopySet failed");
+
+ ZERO_STRUCT(r_scset_add);
+ r_scset_add.in.ClientShadowCopyId = GUID_random();
+ r_scset_add.in.ShadowCopySetId = *r_scset_start.out.pShadowCopySetId;
+ r_scset_add.in.ShareName = share_unc;
+ status = dcerpc_fss_AddToShadowCopySet_r(b, tmp_ctx, &r_scset_add);
+ torture_assert_ntstatus_ok(tctx, status, "AddToShadowCopySet failed "
+ "following abort");
+ /*
+ * XXX Windows 8 server beta returns FSRVP_E_BAD_STATE here rather than
+ * FSRVP_E_BAD_ID / HRES_E_INVALIDARG.
+ */
+ torture_assert(tctx, (r_scset_add.out.result != 0),
+ "incorrect AddToShadowCopySet response following abort");
+
+ talloc_free(tmp_ctx);
+ return true;
+}
+
+static bool test_fsrvp_bad_id(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct fssagent_share_mapping_1 *sc_map;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct fss_DeleteShareMapping r_sharemap_del;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+ char *share_unc = talloc_asprintf(tmp_ctx, "\\\\%s\\%s\\",
+ dcerpc_server_name(p), FSHARE);
+
+ torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map),
+ "sc create");
+
+ ZERO_STRUCT(r_sharemap_del);
+ r_sharemap_del.in.ShadowCopySetId = sc_map->ShadowCopySetId;
+ r_sharemap_del.in.ShadowCopySetId.time_low++; /* bogus */
+ r_sharemap_del.in.ShadowCopyId = sc_map->ShadowCopyId;
+ r_sharemap_del.in.ShareName = sc_map->ShareNameUNC;
+ status = dcerpc_fss_DeleteShareMapping_r(b, tmp_ctx, &r_sharemap_del);
+ torture_assert_ntstatus_ok(tctx, status,
+ "DeleteShareMapping failed");
+ torture_assert_int_equal(tctx, r_sharemap_del.out.result,
+ FSRVP_E_OBJECT_NOT_FOUND,
+ "incorrect DeleteShareMapping response");
+
+ r_sharemap_del.in.ShadowCopySetId = sc_map->ShadowCopySetId;
+ r_sharemap_del.in.ShadowCopyId.time_mid++; /* bogus */
+ status = dcerpc_fss_DeleteShareMapping_r(b, tmp_ctx, &r_sharemap_del);
+ torture_assert_ntstatus_ok(tctx, status,
+ "DeleteShareMapping failed");
+ torture_assert_int_equal(tctx, r_sharemap_del.out.result,
+ HRES_ERROR_V(HRES_E_INVALIDARG),
+ "incorrect DeleteShareMapping response");
+
+ torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map), "sc del");
+
+ talloc_free(sc_map);
+ talloc_free(tmp_ctx);
+
+ return true;
+}
+
+static bool test_fsrvp_sc_share_io(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct fssagent_share_mapping_1 *sc_map;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+ char *share_unc = talloc_asprintf(tmp_ctx, "\\\\%s\\%s",
+ dcerpc_server_name(p), FSHARE);
+ struct smb2_tree *tree_base;
+ struct smb2_tree *tree_snap;
+ struct smbcli_options options;
+ struct smb2_handle base_fh;
+ struct smb2_read r;
+ struct smb2_create io;
+ lpcfg_smbcli_options(tctx->lp_ctx, &options);
+
+ status = smb2_connect(tmp_ctx,
+ dcerpc_server_name(p),
+ lpcfg_smb_ports(tctx->lp_ctx),
+ FSHARE,
+ lpcfg_resolve_context(tctx->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree_base,
+ tctx->ev,
+ &options,
+ lpcfg_socket_options(tctx->lp_ctx),
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to connect to SMB2 share");
+
+ smb2_util_unlink(tree_base, FNAME);
+ status = torture_smb2_testfile(tree_base, FNAME, &base_fh);
+ torture_assert_ntstatus_ok(tctx, status, "base write open");
+
+ status = smb2_util_write(tree_base, base_fh, "pre-snap", 0,
+ sizeof("pre-snap"));
+ torture_assert_ntstatus_ok(tctx, status, "src write");
+
+
+ torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map),
+ "sc create");
+
+ status = smb2_util_write(tree_base, base_fh, "post-snap", 0,
+ sizeof("post-snap"));
+ torture_assert_ntstatus_ok(tctx, status, "base write");
+
+ /* connect to snapshot share and verify pre-snapshot data */
+ status = smb2_connect(tmp_ctx,
+ dcerpc_server_name(p),
+ lpcfg_smb_ports(tctx->lp_ctx),
+ sc_map->ShadowCopyShareName,
+ lpcfg_resolve_context(tctx->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree_snap,
+ tctx->ev,
+ &options,
+ lpcfg_socket_options(tctx->lp_ctx),
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to connect to SMB2 shadow-copy share");
+ /* Windows server 8 allows RW open to succeed here for a ro snapshot */
+ ZERO_STRUCT(io);
+ io.in.desired_access = SEC_RIGHTS_FILE_READ;
+ io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+ io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_DELETE|
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = 0;
+ io.in.fname = FNAME;
+ status = smb2_create(tree_snap, tmp_ctx, &io);
+ torture_assert_ntstatus_ok(tctx, status, "snap read open");
+
+ ZERO_STRUCT(r);
+ r.in.file.handle = io.out.file.handle;
+ r.in.length = sizeof("pre-snap");
+ status = smb2_read(tree_snap, tmp_ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "read");
+ torture_assert_u64_equal(tctx, r.out.data.length, r.in.length,
+ "read data len mismatch");
+ torture_assert_str_equal(tctx, (char *)r.out.data.data, "pre-snap",
+ "bad snapshot data");
+
+ torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map), "sc del");
+
+ talloc_free(sc_map);
+ talloc_free(tmp_ctx);
+
+ return true;
+}
+
+static bool test_fsrvp_enum_snaps(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct smb2_tree *tree,
+ struct smb2_handle fh,
+ int *_count)
+{
+ struct smb2_ioctl io;
+ NTSTATUS status;
+
+ ZERO_STRUCT(io);
+ io.level = RAW_IOCTL_SMB2;
+ io.in.file.handle = fh;
+ io.in.function = FSCTL_SRV_ENUM_SNAPS;
+ io.in.max_output_response = 16;
+ io.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
+
+ status = smb2_ioctl(tree, mem_ctx, &io);
+ torture_assert_ntstatus_ok(tctx, status, "enum ioctl");
+
+ *_count = IVAL(io.out.out.data, 0);
+
+ /* with max_output_response=16, no labels should be sent */
+ torture_assert_int_equal(tctx, IVAL(io.out.out.data, 4), 0,
+ "enum snaps labels");
+
+ /* TODO with 0 snaps, needed_data_count should be 0? */
+ if (*_count != 0) {
+ torture_assert(tctx, IVAL(io.out.out.data, 8) != 0,
+ "enum snaps needed non-zero");
+ }
+
+ return true;
+}
+
+static bool test_fsrvp_enum_created(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct fssagent_share_mapping_1 *sc_map;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+ char *share_unc = talloc_asprintf(tmp_ctx, "\\\\%s\\%s\\",
+ dcerpc_server_name(p), FSHARE);
+ struct smb2_tree *tree_base;
+ struct smbcli_options options;
+ struct smb2_handle base_fh;
+ int count;
+ lpcfg_smbcli_options(tctx->lp_ctx, &options);
+
+ status = smb2_connect(tmp_ctx,
+ dcerpc_server_name(p),
+ lpcfg_smb_ports(tctx->lp_ctx),
+ FSHARE,
+ lpcfg_resolve_context(tctx->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree_base,
+ tctx->ev,
+ &options,
+ lpcfg_socket_options(tctx->lp_ctx),
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+ torture_assert_ntstatus_ok(tctx, status,
+ "Failed to connect to SMB2 share");
+
+ smb2_util_unlink(tree_base, FNAME);
+ status = torture_smb2_testfile(tree_base, FNAME, &base_fh);
+ torture_assert_ntstatus_ok(tctx, status, "base write open");
+
+ status = smb2_util_write(tree_base, base_fh, "pre-snap", 0,
+ sizeof("pre-snap"));
+ torture_assert_ntstatus_ok(tctx, status, "src write");
+
+ torture_assert(tctx,
+ test_fsrvp_enum_snaps(tctx, tmp_ctx, tree_base, base_fh,
+ &count),
+ "count");
+ torture_assert_int_equal(tctx, count, 0, "num snaps");
+
+ torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map),
+ "sc create");
+ talloc_free(sc_map);
+
+ torture_assert(tctx,
+ test_fsrvp_enum_snaps(tctx, tmp_ctx, tree_base, base_fh,
+ &count),
+ "count");
+ /*
+ * Snapshots created via FSRVP on Windows Server 2012 are not added to
+ * the previous versions list, so it will fail here...
+ */
+ torture_assert_int_equal(tctx, count, 1, "num snaps");
+
+ smb_msleep(1100); /* @GMT tokens have a 1 second resolution */
+ torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc, TEST_FSRVP_TOUT_NONE, &sc_map),
+ "sc create");
+ talloc_free(sc_map);
+
+ torture_assert(tctx,
+ test_fsrvp_enum_snaps(tctx, tmp_ctx, tree_base, base_fh,
+ &count),
+ "count");
+ torture_assert_int_equal(tctx, count, 2, "num snaps");
+
+ talloc_free(tmp_ctx);
+
+ return true;
+}
+
+static bool test_fsrvp_seq_timeout(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ int i;
+ struct fssagent_share_mapping_1 *sc_map;
+ char *share_unc = talloc_asprintf(tctx, "\\\\%s\\%s",
+ dcerpc_server_name(p), FSHARE);
+
+ for (i = TEST_FSRVP_TOUT_NONE; i <= TEST_FSRVP_TOUT_COMMIT; i++) {
+ torture_assert(tctx, test_fsrvp_sc_create(tctx, p, share_unc,
+ i, &sc_map),
+ "sc create");
+
+ /* only need to delete if create process didn't timeout */
+ if (i == TEST_FSRVP_TOUT_NONE) {
+ torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map),
+ "sc del");
+ }
+ }
+
+ return true;
+}
+
+static bool test_fsrvp_share_sd(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *srvsvc_p;
+ struct srvsvc_NetShareGetInfo q;
+ struct srvsvc_NetShareSetInfo s;
+ struct srvsvc_NetShareInfo502 *info502;
+ struct fssagent_share_mapping_1 *sc_map;
+ struct fss_ExposeShadowCopySet r_scset_expose;
+ struct fss_GetShareMapping r_sharemap_get;
+ struct security_descriptor *sd_old;
+ struct security_descriptor *sd_base;
+ struct security_descriptor *sd_snap;
+ struct security_ace *ace;
+ int i;
+ int aces_found;
+ char *share_unc = talloc_asprintf(tctx, "\\\\%s\\%s",
+ dcerpc_server_name(p), FSHARE);
+ ZERO_STRUCT(q);
+ q.in.server_unc = dcerpc_server_name(p);
+ q.in.share_name = FSHARE;
+ q.in.level = 502;
+
+ status = torture_rpc_connection(tctx, &srvsvc_p, &ndr_table_srvsvc);
+ torture_assert_ntstatus_ok(tctx, status, "srvsvc rpc conn failed");
+
+ /* ensure srvsvc out pointers are allocated during unmarshalling */
+ srvsvc_p->conn->flags |= DCERPC_NDR_REF_ALLOC;
+
+ /* obtain the existing DACL for the base share */
+ status = dcerpc_srvsvc_NetShareGetInfo_r(srvsvc_p->binding_handle,
+ tctx, &q);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed");
+ torture_assert_werr_ok(tctx, q.out.result, "NetShareGetInfo failed");
+
+ info502 = q.out.info->info502;
+
+ /* back up the existing share SD, so it can be restored on completion */
+ sd_old = info502->sd_buf.sd;
+ sd_base = security_descriptor_copy(tctx, info502->sd_buf.sd);
+ torture_assert(tctx, sd_base != NULL, "sd dup");
+ torture_assert(tctx, sd_base->dacl != NULL, "no existing share DACL");
+
+ /* the Builtin_X_Operators placeholder ACEs need to be unique */
+ for (i = 0; i < sd_base->dacl->num_aces; i++) {
+ ace = &sd_base->dacl->aces[i];
+ if (dom_sid_equal(&ace->trustee,
+ &global_sid_Builtin_Backup_Operators)
+ || dom_sid_equal(&ace->trustee,
+ &global_sid_Builtin_Print_Operators)) {
+ torture_skip(tctx, "placeholder ACE already exists\n");
+ }
+ }
+
+ /* add Backup_Operators placeholder ACE and set base share DACL */
+ ace = talloc_zero(tctx, struct security_ace);
+ ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace->access_mask = SEC_STD_SYNCHRONIZE;
+ ace->trustee = global_sid_Builtin_Backup_Operators;
+
+ status = security_descriptor_dacl_add(sd_base, ace);
+ torture_assert_ntstatus_ok(tctx, status,
+ "failed to add placeholder ACE to DACL");
+
+ info502->sd_buf.sd = sd_base;
+ info502->sd_buf.sd_size = ndr_size_security_descriptor(sd_base, 0);
+
+ ZERO_STRUCT(s);
+ s.in.server_unc = dcerpc_server_name(p);
+ s.in.share_name = FSHARE;
+ s.in.level = 502;
+ s.in.info = q.out.info;
+
+ status = dcerpc_srvsvc_NetShareSetInfo_r(srvsvc_p->binding_handle,
+ tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareSetInfo failed");
+ torture_assert_werr_ok(tctx, s.out.result, "NetShareSetInfo failed");
+
+ /* create a snapshot, but don't expose yet */
+ torture_assert(tctx,
+ test_fsrvp_sc_create(tctx, p, share_unc,
+ TEST_FSRVP_STOP_B4_EXPOSE, &sc_map),
+ "sc create");
+
+ /*
+ * Add another unique placeholder ACE.
+ * By changing the share DACL between snapshot creation and exposure we
+ * can determine at which point the server clones the base share DACL.
+ */
+ ace = talloc_zero(tctx, struct security_ace);
+ ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace->access_mask = SEC_STD_SYNCHRONIZE;
+ ace->trustee = global_sid_Builtin_Print_Operators;
+
+ status = security_descriptor_dacl_add(sd_base, ace);
+ torture_assert_ntstatus_ok(tctx, status,
+ "failed to add placeholder ACE to DACL");
+
+ info502->sd_buf.sd = sd_base;
+ info502->sd_buf.sd_size = ndr_size_security_descriptor(sd_base, 0);
+
+ ZERO_STRUCT(s);
+ s.in.server_unc = dcerpc_server_name(p);
+ s.in.share_name = FSHARE;
+ s.in.level = 502;
+ s.in.info = q.out.info;
+
+ status = dcerpc_srvsvc_NetShareSetInfo_r(srvsvc_p->binding_handle,
+ tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareSetInfo failed");
+ torture_assert_werr_ok(tctx, s.out.result, "NetShareSetInfo failed");
+
+ /* expose the snapshot share and get the new share details */
+ ZERO_STRUCT(r_scset_expose);
+ r_scset_expose.in.ShadowCopySetId = sc_map->ShadowCopySetId;
+ r_scset_expose.in.TimeOutInMilliseconds = (120 * 1000); /* win8 */
+ status = dcerpc_fss_ExposeShadowCopySet_r(p->binding_handle, tctx,
+ &r_scset_expose);
+ torture_assert_ntstatus_ok(tctx, status,
+ "ExposeShadowCopySet failed");
+ torture_assert_int_equal(tctx, r_scset_expose.out.result, 0,
+ "failed ExposeShadowCopySet response");
+
+ ZERO_STRUCT(r_sharemap_get);
+ r_sharemap_get.in.ShadowCopyId = sc_map->ShadowCopyId;
+ r_sharemap_get.in.ShadowCopySetId = sc_map->ShadowCopySetId;
+ r_sharemap_get.in.ShareName = share_unc;
+ r_sharemap_get.in.Level = 1;
+ status = dcerpc_fss_GetShareMapping_r(p->binding_handle, tctx,
+ &r_sharemap_get);
+ torture_assert_ntstatus_ok(tctx, status, "GetShareMapping failed");
+ torture_assert_int_equal(tctx, r_sharemap_get.out.result, 0,
+ "failed GetShareMapping response");
+ talloc_free(sc_map);
+ sc_map = r_sharemap_get.out.ShareMapping->ShareMapping1;
+
+ /* restore the original base share ACL */
+ info502->sd_buf.sd = sd_old;
+ info502->sd_buf.sd_size = ndr_size_security_descriptor(sd_old, 0);
+ status = dcerpc_srvsvc_NetShareSetInfo_r(srvsvc_p->binding_handle,
+ tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareSetInfo failed");
+ torture_assert_werr_ok(tctx, s.out.result, "NetShareSetInfo failed");
+
+ /* check for placeholder ACEs in the snapshot share DACL */
+ ZERO_STRUCT(q);
+ q.in.server_unc = dcerpc_server_name(p);
+ q.in.share_name = sc_map->ShadowCopyShareName;
+ q.in.level = 502;
+ status = dcerpc_srvsvc_NetShareGetInfo_r(srvsvc_p->binding_handle,
+ tctx, &q);
+ torture_assert_ntstatus_ok(tctx, status, "NetShareGetInfo failed");
+ torture_assert_werr_ok(tctx, q.out.result, "NetShareGetInfo failed");
+ info502 = q.out.info->info502;
+
+ sd_snap = info502->sd_buf.sd;
+ torture_assert(tctx, sd_snap != NULL, "sd");
+ torture_assert(tctx, sd_snap->dacl != NULL, "no snap share DACL");
+
+ aces_found = 0;
+ for (i = 0; i < sd_snap->dacl->num_aces; i++) {
+ ace = &sd_snap->dacl->aces[i];
+ if (dom_sid_equal(&ace->trustee,
+ &global_sid_Builtin_Backup_Operators)) {
+ torture_comment(tctx,
+ "found share ACE added before snapshot\n");
+ aces_found++;
+ } else if (dom_sid_equal(&ace->trustee,
+ &global_sid_Builtin_Print_Operators)) {
+ torture_comment(tctx,
+ "found share ACE added after snapshot\n");
+ aces_found++;
+ }
+ }
+ /*
+ * Expect snapshot share to match the base share DACL at the time of
+ * exposure, not at the time of snapshot creation. This is in line with
+ * Windows Server 2012 behaviour.
+ */
+ torture_assert_int_equal(tctx, aces_found, 2,
+ "placeholder ACE missing from snap share DACL");
+
+ torture_assert(tctx, test_fsrvp_sc_delete(tctx, p, sc_map), "sc del");
+
+ return true;
+}
+
+static bool fsrvp_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 = samba_cmdline_get_creds();
+
+ status = torture_rpc_connection(tctx,
+ &(tcase_data->pipe),
+ tcase->table);
+
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ /* XXX required, otherwise ndr out ptrs are not allocated */
+ tcase_data->pipe->conn->flags |= DCERPC_NDR_REF_ALLOC;
+
+ return true;
+}
+
+/*
+ testing of FSRVP (FSS agent)
+*/
+struct torture_suite *torture_rpc_fsrvp(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "fsrvp");
+
+ struct torture_rpc_tcase *tcase
+ = torture_suite_add_rpc_iface_tcase(suite, "fsrvp",
+ &ndr_table_FileServerVssAgent);
+ /* override torture_rpc_setup() to set DCERPC_NDR_REF_ALLOC */
+ tcase->tcase.setup = fsrvp_rpc_setup;
+
+ torture_rpc_tcase_add_test(tcase, "share_sd",
+ test_fsrvp_share_sd);
+ torture_rpc_tcase_add_test(tcase, "enum_created",
+ test_fsrvp_enum_created);
+ torture_rpc_tcase_add_test(tcase, "sc_share_io",
+ test_fsrvp_sc_share_io);
+ torture_rpc_tcase_add_test(tcase, "bad_id",
+ test_fsrvp_bad_id);
+ torture_rpc_tcase_add_test(tcase, "sc_set_abort",
+ test_fsrvp_sc_set_abort);
+ torture_rpc_tcase_add_test(tcase, "create_simple",
+ test_fsrvp_sc_create_simple);
+ torture_rpc_tcase_add_test(tcase, "set_ctx",
+ test_fsrvp_set_ctx);
+ torture_rpc_tcase_add_test(tcase, "get_version",
+ test_fsrvp_get_version);
+ torture_rpc_tcase_add_test(tcase, "is_path_supported",
+ test_fsrvp_is_path_supported);
+ torture_rpc_tcase_add_test(tcase, "seq_timeout",
+ test_fsrvp_seq_timeout);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/handles.c b/source4/torture/rpc/handles.c
new file mode 100644
index 0000000..7c108e5
--- /dev/null
+++ b/source4/torture/rpc/handles.c
@@ -0,0 +1,622 @@
+/*
+ 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 "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/torture_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 dcerpc_binding_handle *b1, *b2;
+ 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");
+ b1 = p1->binding_handle;
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1");
+ b2 = p2->binding_handle;
+
+ 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_assert_ntstatus_ok(torture, dcerpc_lsa_OpenPolicy_r(b1, mem_ctx, &r),
+ "OpenPolicy failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ 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_r(b2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "closing policy handle on p2");
+
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_Close_r(b1, mem_ctx, &c),
+ "Close failed");
+ torture_assert_ntstatus_ok(torture, c.out.result, "closing policy handle on p1");
+
+ status = dcerpc_lsa_Close_r(b1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_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 dcerpc_binding_handle *b1, *b2, *b3, *b4;
+ 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;
+ struct sec_desc_buf *sdbuf = NULL;
+ 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");
+ b1 = p1->binding_handle;
+
+ transport = p1->conn->transport.transport;
+ assoc_group_id = dcerpc_binding_get_assoc_group_id(p1->binding);
+
+ 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,
+ 0);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2");
+ b2 = p2->binding_handle;
+
+ torture_comment(torture, "got assoc_group_id[0x%08X] for p2\n",
+ dcerpc_binding_get_assoc_group_id(p2->binding));
+
+ 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");
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_OpenPolicy_r(b1, mem_ctx, &r),
+ "OpenPolicy failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ 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,
+ 0);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe3");
+ b3 = p3->binding_handle;
+
+ qsec.in.handle = &handle;
+ qsec.in.sec_info = 0;
+ qsec.out.sdbuf = &sdbuf;
+ 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");
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b1, mem_ctx, &qsec),
+ "QuerySecurity failed");
+ torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK,
+ "use policy handle on p1");
+
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b2, mem_ctx, &qsec),
+ "QuerySecurity failed");
+ torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK,
+ "use policy handle on p2");
+
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b3, mem_ctx, &qsec),
+ "QuerySecurity failed");
+ torture_assert_ntstatus_equal(torture, qsec.out.result, 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");
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_Close_r(b2, mem_ctx, &c),
+ "Close failed");
+ torture_assert_ntstatus_equal(torture, c.out.result, NT_STATUS_OK,
+ "closing policy handle on p2");
+
+ status = dcerpc_lsa_Close_r(b1, mem_ctx, &c);
+
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "closing policy handle on p1 again");
+
+ status = dcerpc_lsa_Close_r(b3, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "closing policy handle on p3");
+
+ status = dcerpc_lsa_Close_r(b2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_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");
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_OpenPolicy_r(b3, mem_ctx, &r),
+ "OpenPolicy failed");
+ torture_assert_ntstatus_equal(torture, r.out.result, 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");
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b1, mem_ctx, &qsec),
+ "Query Security failed");
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p1");
+
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b2, mem_ctx, &qsec),
+ "Query Security failed");
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_OK,
+ "use policy handle on p2");
+
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b3, mem_ctx, &qsec),
+ "Query Security failed");
+ 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");
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_Close_r(b2, mem_ctx, &c),
+ "Close failed");
+ torture_assert_ntstatus_equal(torture, c.out.result, NT_STATUS_OK,
+ "closing policy handle on p2");
+
+ status = dcerpc_lsa_Close_r(b1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "closing policy handle on p1 again");
+
+ status = dcerpc_lsa_Close_r(b3, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "closing policy handle on p3");
+
+ status = dcerpc_lsa_Close_r(b2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_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");
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_OpenPolicy_r(b1, mem_ctx, &r),
+ "OpenPolicy failed");
+ torture_assert_ntstatus_equal(torture, r.out.result, NT_STATUS_OK,
+ "open 2nd policy handle on p1");
+
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b1, mem_ctx, &qsec),
+ "QuerySecurity failed");
+ torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK,
+ "QuerySecurity handle on p1");
+
+ /* close first connection */
+ torture_comment(torture, "disconnect p1\n");
+ talloc_free(p1);
+ smb_msleep(5);
+
+ /*
+ * and it's still available on p2,p3
+ */
+ torture_comment(torture, "use policy handle on p2,p3\n");
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b2, mem_ctx, &qsec),
+ "QuerySecurity failed");
+ torture_assert_ntstatus_equal(torture, qsec.out.result, NT_STATUS_OK,
+ "QuerySecurity handle on p2 after p1 was disconnected");
+
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b3, mem_ctx, &qsec),
+ "QuerySecurity failed");
+ torture_assert_ntstatus_equal(torture, qsec.out.result, 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,
+ 0);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe4");
+ b4 = p4->binding_handle;
+
+ torture_assert_ntstatus_ok(torture, dcerpc_lsa_QuerySecurity_r(b4, mem_ctx, &qsec),
+ "QuerySecurity failed");
+ torture_assert_ntstatus_equal(torture, qsec.out.result, 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);
+ smb_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,
+ 0);
+ 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 dcerpc_binding_handle *b1, *b2;
+ 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");
+ b1 = p1->binding_handle;
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_samr);
+ torture_assert_ntstatus_ok(torture, status, "opening samr pipe2");
+ b2 = p2->binding_handle;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = &handle;
+
+ torture_assert_ntstatus_ok(torture, dcerpc_samr_Connect_r(b1, mem_ctx, &r),
+ "Connect failed");
+ torture_assert_ntstatus_ok(torture, r.out.result, "opening policy handle on p1");
+
+ c.in.handle = &handle;
+ c.out.handle = &handle2;
+
+ status = dcerpc_samr_Close_r(b2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "closing policy handle on p2");
+
+ torture_assert_ntstatus_ok(torture, dcerpc_samr_Close_r(b1, mem_ctx, &c),
+ "Close failed");
+ torture_assert_ntstatus_ok(torture, c.out.result, "closing policy handle on p1");
+
+ status = dcerpc_samr_Close_r(b1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_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 dcerpc_binding_handle *b1, *b2;
+ 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");
+ b1 = p1->binding_handle;
+
+ transport = p1->conn->transport.transport;
+ assoc_group_id = dcerpc_binding_get_assoc_group_id(p1->binding);
+
+ 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,
+ 0);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe2");
+ b2 = p2->binding_handle;
+
+ torture_comment(torture, "got assoc_group_id[0x%08X] for p2\n",
+ dcerpc_binding_get_assoc_group_id(p2->binding));
+ 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");
+ torture_assert_ntstatus_ok(torture, dcerpc_samr_Connect_r(b1, mem_ctx, &r),
+ "Connect failed");
+ torture_assert_ntstatus_ok(torture, r.out.result, "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_r(b2, mem_ctx, &lc);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "closing handle on lsa p2");
+
+ torture_comment(torture, "closing policy handle on samr p1\n");
+ torture_assert_ntstatus_ok(torture, dcerpc_samr_Close_r(b1, mem_ctx, &sc),
+ "Close failed");
+ torture_assert_ntstatus_ok(torture, sc.out.result, "closing policy handle on p1");
+
+ talloc_free(p1);
+ talloc_free(p2);
+ smb_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,
+ 0);
+ 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,
+ 0);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_UNSUCCESSFUL,
+ "opening lsa pipe4");
+
+ /*
+ * We use ~assoc_group_id instead of p1->assoc_group_id, because
+ * this way we are less likely to use an id which is already in use.
+ */
+ assoc_group_id = ~assoc_group_id;
+ 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,
+ 0);
+ 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,
+ 0);
+ 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");
+
+ torture_comment(torture, "pipe1 uses assoc_group_id[0x%08X]\n",
+ dcerpc_binding_get_assoc_group_id(p1->binding));
+
+ transport = p1->conn->transport.transport;
+ /*
+ * We use ~p1->assoc_group_id instead of p1->assoc_group_id, because
+ * this way we are less likely to use an id which is already in use.
+ *
+ * And make sure it doesn't wrap.
+ */
+ assoc_group_id = dcerpc_binding_get_assoc_group_id(p1->binding);
+ assoc_group_id = ~MIN(assoc_group_id, UINT32_MAX - 3);
+
+ 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,
+ 0);
+ 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,
+ 0);
+ 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 dcerpc_binding_handle *b1, *b2;
+ 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");
+ b1 = p1->binding_handle;
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_drsuapi);
+ torture_assert_ntstatus_ok(torture, status, "opening drsuapi pipe1");
+ b2 = p2->binding_handle;
+
+ 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_r(b1, 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_r(b2, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "closing policy handle on p2");
+
+ status = dcerpc_drsuapi_DsUnbind_r(b1, mem_ctx, &c);
+ torture_assert_ntstatus_ok(torture, status, "closing policy handle on p1");
+
+ status = dcerpc_drsuapi_DsUnbind_r(b1, mem_ctx, &c);
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_RPC_SS_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 0000000..28eaacd
--- /dev/null
+++ b/source4/torture/rpc/initshutdown.c
@@ -0,0 +1,116 @@
+/*
+ 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 "librpc/gen_ndr/ndr_initshutdown_c.h"
+#include "torture/rpc/torture_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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server = &server;
+
+ status = dcerpc_initshutdown_Abort_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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.do_reboot = 1;
+
+ status = dcerpc_initshutdown_Init_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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.do_reboot = 1;
+ r.in.reason = 0;
+
+ status = dcerpc_initshutdown_InitEx_r(b, 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/iremotewinspool.c b/source4/torture/rpc/iremotewinspool.c
new file mode 100644
index 0000000..3e8010d
--- /dev/null
+++ b/source4/torture/rpc/iremotewinspool.c
@@ -0,0 +1,855 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for iremotewinspool rpc operations
+
+ Copyright (C) Guenther Deschner 2013
+
+ 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_winspool.h"
+#include "librpc/gen_ndr/ndr_winspool_c.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include "libcli/registry/util_reg.h"
+#include "torture/rpc/iremotewinspool_common.h"
+
+static bool torture_rpc_iremotewinspool_setup_common(struct torture_context *tctx,
+ struct test_iremotewinspool_context *t)
+{
+ const char *printer_name;
+ struct spoolss_UserLevel1 client_info;
+ struct dcerpc_binding *binding;
+
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(IREMOTEWINSPOOL_OBJECT_GUID, &t->object_uuid),
+ "failed to parse GUID");
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_binding(tctx, &binding),
+ "failed to retrieve torture binding");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_binding_set_object(binding, t->object_uuid),
+ "failed to set object_uuid");
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection_with_binding(tctx, binding, &t->iremotewinspool_pipe, &ndr_table_iremotewinspool),
+ "Error connecting to server");
+
+ printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(t->iremotewinspool_pipe));
+
+ client_info = test_get_client_info(tctx, WIN_7, 6, 1, "testclient_machine", "testclient_user");
+
+ torture_assert(tctx,
+ test_AsyncOpenPrinter_byprinter(tctx, t,
+ t->iremotewinspool_pipe, printer_name,
+ client_info, &t->server_handle),
+ "failed to open printserver");
+ torture_assert(tctx,
+ test_get_environment(tctx,
+ t->iremotewinspool_pipe->binding_handle,
+ &t->server_handle, &t->environment),
+ "failed to get environment");
+
+ return true;
+}
+
+static bool torture_rpc_iremotewinspool_setup(struct torture_context *tctx,
+ void **data)
+{
+ struct test_iremotewinspool_context *t;
+
+ *data = t = talloc_zero(tctx, struct test_iremotewinspool_context);
+
+ return torture_rpc_iremotewinspool_setup_common(tctx, t);
+}
+
+static bool torture_rpc_iremotewinspool_teardown_common(struct torture_context *tctx,
+ struct test_iremotewinspool_context *t)
+{
+
+ test_AsyncClosePrinter_byhandle(tctx, t, t->iremotewinspool_pipe, &t->server_handle);
+
+ return true;
+}
+
+static bool torture_rpc_iremotewinspool_teardown(struct torture_context *tctx,
+ void *data)
+{
+ struct test_iremotewinspool_context *t = talloc_get_type(data, struct test_iremotewinspool_context);
+ bool ret;
+
+ ret = torture_rpc_iremotewinspool_teardown_common(tctx, t);
+ talloc_free(t);
+
+ return ret;
+}
+
+static bool test_AsyncClosePrinter(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ const char *printer_name;
+ struct spoolss_UserLevel1 client_info;
+ struct policy_handle handle;
+
+ printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ client_info = test_get_client_info(tctx, WIN_7, 6, 1, "testclient_machine", "testclient_user");
+
+ torture_assert(tctx,
+ test_AsyncOpenPrinter_byprinter(tctx, ctx, p, printer_name, client_info, &handle),
+ "failed to test AsyncOpenPrinter");
+
+ torture_assert(tctx,
+ test_AsyncClosePrinter_byhandle(tctx, ctx, p, &handle),
+ "failed to test AsyncClosePrinter");
+
+ return true;
+}
+
+static bool test_AsyncOpenPrinter(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ const char *printer_name;
+ struct spoolss_UserLevel1 client_info;
+ struct policy_handle handle;
+
+ printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ client_info = test_get_client_info(tctx, WIN_7, 6, 1, "testclient_machine", "testclient_user");
+
+ torture_assert(tctx,
+ test_AsyncOpenPrinter_byprinter(tctx, ctx, p, printer_name, client_info, &handle),
+ "failed to test AsyncOpenPrinter");
+
+ test_AsyncClosePrinter_byhandle(tctx, ctx, p, &handle);
+
+ return true;
+}
+
+/*
+ * Validate the result of AsyncOpenPrinter calls based on client info
+ * build number. Windows Server 2016 rejects an advertised build
+ * number less than 6000(Windows Vista and Windows Server 2008, or older)
+ */
+static bool test_AsyncOpenPrinterValidateBuildNumber(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ const char *printer_name;
+ struct spoolss_UserLevel1 client_info;
+ struct policy_handle handle;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct spoolss_UserLevelCtr client_info_ctr = {
+ .level = 1,
+ };
+ uint32_t access_mask = SERVER_ALL_ACCESS;
+ struct winspool_AsyncOpenPrinter r;
+ NTSTATUS status;
+ bool ok = false;
+
+ printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ torture_assert_not_null(tctx, printer_name, "Cannot allocate memory");
+
+ /* fail with Windows 2000 build number */
+ client_info = test_get_client_info(tctx, WIN_2000, 3, SPOOLSS_MINOR_VERSION_0,
+ "testclient_machine", "testclient_user");
+
+ ZERO_STRUCT(devmode_ctr);
+
+ client_info_ctr.user_info.level1 = &client_info;
+
+ r.in.pPrinterName = printer_name;
+ r.in.pDatatype = NULL;
+ r.in.pDevModeContainer = &devmode_ctr;
+ r.in.AccessRequired = access_mask;
+ r.in.pClientInfo = &client_info_ctr;
+ r.out.pHandle = &handle;
+
+ status = dcerpc_winspool_AsyncOpenPrinter_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "AsyncOpenPrinter failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_ACCESS_DENIED,
+ "AsyncOpenPrinter should have failed");
+
+ /* succeed with Windows 7 build number */
+ client_info = test_get_client_info(tctx, WIN_7, 6, 1,
+ "testclient_machine", "testclient_user");
+ client_info_ctr.user_info.level1 = &client_info;
+ r.in.pClientInfo = &client_info_ctr;
+
+ status = dcerpc_winspool_AsyncOpenPrinter_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "AsyncOpenPrinter failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "AsyncOpenPrinter failed");
+
+ ok = test_AsyncClosePrinter_byhandle(tctx, ctx, p, &handle);
+ torture_assert(tctx, ok, "failed to AsyncClosePrinter handle");
+
+ return true;
+
+}
+
+static struct spoolss_NotifyOption *setup_printserver_NotifyOption(struct torture_context *tctx)
+{
+ struct spoolss_NotifyOption *o;
+
+ o = talloc_zero(tctx, struct spoolss_NotifyOption);
+ if (o == NULL) {
+ return NULL;
+ }
+
+ o->version = 2;
+ o->flags = PRINTER_NOTIFY_OPTIONS_REFRESH;
+
+ o->count = 2;
+ o->types = talloc_zero_array(o, struct spoolss_NotifyOptionType, o->count);
+ if (o->types == NULL) {
+ talloc_free(o);
+ return NULL;
+ }
+
+ o->types[0].type = PRINTER_NOTIFY_TYPE;
+ o->types[0].count = 1;
+ o->types[0].fields = talloc_array(o->types, union spoolss_Field, o->types[0].count);
+ if (o->types[0].fields == NULL) {
+ talloc_free(o);
+ return NULL;
+ }
+ o->types[0].fields[0].field = PRINTER_NOTIFY_FIELD_SERVER_NAME;
+
+ o->types[1].type = JOB_NOTIFY_TYPE;
+ o->types[1].count = 1;
+ o->types[1].fields = talloc_array(o->types, union spoolss_Field, o->types[1].count);
+ if (o->types[1].fields == NULL) {
+ talloc_free(o);
+ return NULL;
+ }
+ o->types[1].fields[0].field = JOB_NOTIFY_FIELD_MACHINE_NAME;
+
+ return o;
+}
+
+static bool test_SyncUnRegisterForRemoteNotifications_args(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *notify_handle)
+{
+ struct winspool_SyncUnRegisterForRemoteNotifications r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.phRpcHandle = notify_handle;
+ r.out.phRpcHandle = notify_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_SyncUnRegisterForRemoteNotifications_r(b, tctx, &r),
+ "SyncUnRegisterForRemoteNotifications failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "SyncUnRegisterForRemoteNotifications failed");
+
+ return true;
+}
+
+static bool test_SyncRegisterForRemoteNotifications_args(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *server_handle,
+ struct policy_handle *notify_handle);
+
+static bool test_SyncUnRegisterForRemoteNotifications(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+ struct policy_handle notify_handle;
+
+ torture_assert(tctx,
+ test_SyncRegisterForRemoteNotifications_args(tctx,
+ ctx->iremotewinspool_pipe,
+ &ctx->server_handle,
+ &notify_handle),
+ "failed to test SyncRegisterForRemoteNotifications");
+
+ torture_assert(tctx,
+ test_SyncUnRegisterForRemoteNotifications_args(tctx,
+ ctx->iremotewinspool_pipe,
+ &notify_handle),
+ "failed to test UnSyncRegisterForRemoteNotifications");
+
+ return true;
+}
+
+static bool test_SyncRegisterForRemoteNotifications_args(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *server_handle,
+ struct policy_handle *notify_handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ struct winspool_SyncRegisterForRemoteNotifications r;
+ struct winspool_PrintPropertiesCollection NotifyFilter;
+ struct winspool_PrintNamedProperty *c;
+ struct spoolss_NotifyOption *options;
+
+ ZERO_STRUCT(NotifyFilter);
+
+ options = setup_printserver_NotifyOption(tctx);
+ torture_assert(tctx, options, "out of memory");
+
+ c = talloc_zero_array(tctx, struct winspool_PrintNamedProperty, 4);
+ torture_assert(tctx, c, "out of memory");
+
+ c[0].propertyName = "RemoteNotifyFilter Flags";
+ c[0].propertyValue.PropertyType = winspool_PropertyTypeInt32;
+ c[0].propertyValue.value.propertyInt32 = 0xff;
+
+ c[1].propertyName = "RemoteNotifyFilter Options";
+ c[1].propertyValue.PropertyType = winspool_PropertyTypeInt32;
+ c[1].propertyValue.value.propertyInt32 = 0;
+
+ c[2].propertyName = "RemoteNotifyFilter Color";
+ c[2].propertyValue.PropertyType = winspool_PropertyTypeInt32;
+ c[2].propertyValue.value.propertyInt32 = 0;
+
+ c[3].propertyName = "RemoteNotifyFilter NotifyOptions";
+ c[3].propertyValue.PropertyType = winspool_PropertyTypeNotificationOptions;
+ c[3].propertyValue.value.propertyOptionsContainer.pOptions = options;
+
+ NotifyFilter.numberOfProperties = 4;
+ NotifyFilter.propertiesCollection = c;
+
+ r.in.hPrinter = *server_handle;
+ r.in.pNotifyFilter = &NotifyFilter;
+ r.out.phRpcHandle = notify_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_SyncRegisterForRemoteNotifications_r(b, tctx, &r),
+ "SyncRegisterForRemoteNotifications failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "SyncRegisterForRemoteNotifications failed");
+
+ return true;
+}
+
+static bool test_SyncRegisterForRemoteNotifications(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+ struct policy_handle notify_handle;
+
+ torture_assert(tctx,
+ test_SyncRegisterForRemoteNotifications_args(tctx,
+ ctx->iremotewinspool_pipe,
+ &ctx->server_handle,
+ &notify_handle),
+ "failed to test SyncRegisterForRemoteNotifications");
+
+ test_SyncUnRegisterForRemoteNotifications_args(tctx, ctx->iremotewinspool_pipe, &notify_handle);
+
+ return true;
+}
+
+static bool test_AsyncUploadPrinterDriverPackage(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ struct winspool_AsyncUploadPrinterDriverPackage r;
+ uint32_t pcchDestInfPath = 0;
+
+ r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.pszInfPath = "";
+ r.in.pszEnvironment = "";
+ r.in.dwFlags = 0;
+ r.in.pszDestInfPath = NULL;
+ r.in.pcchDestInfPath = &pcchDestInfPath;
+ r.out.pszDestInfPath = NULL;
+ r.out.pcchDestInfPath = &pcchDestInfPath;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r),
+ "AsyncUploadPrinterDriverPackage failed");
+ torture_assert_hresult_equal(tctx, r.out.result, HRES_E_INVALIDARG,
+ "AsyncUploadPrinterDriverPackage failed");
+
+ pcchDestInfPath = 260;
+ r.in.pszDestInfPath = talloc_zero(tctx, const char);
+ r.out.pszDestInfPath = talloc_zero(tctx, const char);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r),
+ "AsyncUploadPrinterDriverPackage failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT,
+ "AsyncUploadPrinterDriverPackage failed");
+
+ r.in.pszEnvironment = SPOOLSS_ARCHITECTURE_x64;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r),
+ "AsyncUploadPrinterDriverPackage failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_FILE_NOT_FOUND,
+ "AsyncUploadPrinterDriverPackage failed");
+
+ r.in.pszInfPath = "\\\\mthelena\\print$\\x64\\{BD443844-ED00-4D96-8CAE-95E49492312A}\\prnbrcl1.inf";
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r),
+ "AsyncUploadPrinterDriverPackage failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_FILE_NOT_FOUND,
+ "AsyncUploadPrinterDriverPackage failed");
+
+ return true;
+}
+
+static bool test_AsyncEnumPrinters(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ struct winspool_AsyncEnumPrinters r;
+ uint32_t levels[] = { 1, 2, /*3,*/ 4, 5 };
+ int i;
+
+ uint32_t needed;
+ uint32_t returned;
+
+ for (i = 0; i < ARRAY_SIZE(levels); i++) {
+
+ r.in.Flags = PRINTER_ENUM_LOCAL;
+ r.in.pName = NULL;
+ r.in.Level = levels[i];
+ r.in.cbBuf = 0;
+ r.in.pPrinterEnum = NULL;
+ r.out.pcbNeeded = &needed;
+ r.out.pcReturned = &returned;
+ r.out.pPrinterEnum = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncEnumPrinters_r(b, tctx, &r),
+ "AsyncEnumPrinters failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "AsyncEnumPrinters failed");
+
+ r.in.cbBuf = needed;
+ r.in.pPrinterEnum = talloc_zero_array(tctx, uint8_t, r.in.cbBuf);
+ r.out.pPrinterEnum = r.in.pPrinterEnum;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncEnumPrinters_r(b, tctx, &r),
+ "AsyncEnumPrinters failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "AsyncEnumPrinters failed");
+ }
+
+ return true;
+}
+
+static bool test_AsyncGetPrinterData(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ DATA_BLOB blob;
+ const char *s;
+ bool ok;
+
+ uint32_t pType;
+ uint32_t pcbNeeded;
+ uint8_t *pData;
+
+ torture_assert(tctx,
+ test_AsyncGetPrinterData_args(tctx, b, &ctx->server_handle,
+ "MajorVersion",
+ &pType, &pData, &pcbNeeded),
+ "failed to check for MajorVersion");
+
+ torture_assert_int_equal(tctx, pcbNeeded, 4, "pcbNeeded");
+ torture_assert_int_equal(tctx, pType, REG_DWORD, "pType");
+ torture_assert_int_equal(tctx, IVAL(pData, 0), 3, "pData");
+
+ torture_assert(tctx,
+ test_AsyncGetPrinterData_args(tctx, b, &ctx->server_handle,
+ "Architecture",
+ &pType, &pData, &pcbNeeded),
+ "failed to check for Architecture");
+
+ blob = data_blob_const(pData, pcbNeeded);
+
+ torture_assert_int_equal(tctx, pType, REG_SZ, "pType");
+ torture_assert(tctx, pull_reg_sz(tctx, &blob, &s), "");
+ ok = strequal(s, SPOOLSS_ARCHITECTURE_x64) || strequal(s, SPOOLSS_ARCHITECTURE_NT_X86);
+ torture_assert(tctx, ok, "unexpected architecture returned");
+
+ return true;
+}
+
+static bool test_AsyncCorePrinterDriverInstalled(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ struct winspool_AsyncCorePrinterDriverInstalled r;
+ int32_t pbDriverInstalled;
+ struct GUID guid;
+
+ r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.pszEnvironment = "";
+ r.in.CoreDriverGUID = GUID_zero();
+ r.in.ftDriverDate = 0;
+ r.in.dwlDriverVersion = 0;
+ r.out.pbDriverInstalled = &pbDriverInstalled;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r),
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT,
+ "AsyncCorePrinterDriverInstalled failed");
+
+ r.in.pszEnvironment = SPOOLSS_ARCHITECTURE_x64;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r),
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, false,
+ "unexpected driver installed");
+
+ r.in.CoreDriverGUID = GUID_random();
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r),
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, false,
+ "unexpected driver installed");
+
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV, &guid), "");
+
+ r.in.CoreDriverGUID = guid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r),
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, true,
+ "xps core driver not installed?");
+
+ r.in.dwlDriverVersion = 0xffffffff;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r),
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, true,
+ "xps core driver not installed?");
+
+ r.in.dwlDriverVersion = 1234;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r),
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, true,
+ "xps core driver not installed?");
+
+ r.in.ftDriverDate = unix_timespec_to_nt_time(timespec_current());
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r),
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, false,
+ "driver too old ?");
+
+ r.in.dwlDriverVersion = 0;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncCorePrinterDriverInstalled_r(b, tctx, &r),
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "AsyncCorePrinterDriverInstalled failed");
+ torture_assert_int_equal(tctx, *r.out.pbDriverInstalled, false,
+ "unexpected driver installed");
+
+ return true;
+}
+
+static bool test_get_core_printer_drivers_arch_guid(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *architecture,
+ const char *guid_str,
+ const char **package_id)
+{
+ struct winspool_AsyncGetCorePrinterDrivers r;
+ DATA_BLOB blob;
+ const char **s;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ s = talloc_zero_array(tctx, const char *, 2);
+ s[0] = guid_str;
+
+ torture_assert(tctx,
+ push_reg_multi_sz(tctx, &blob, s),
+ "push_reg_multi_sz failed");
+
+ r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.pszEnvironment = architecture;
+ r.in.cchCoreDrivers = blob.length/2;
+ r.in.pszzCoreDriverDependencies = (uint16_t *)blob.data;
+ r.in.cCorePrinterDrivers = 1;
+ r.out.pCorePrinterDrivers = talloc_zero_array(tctx, struct spoolss_CorePrinterDriver, r.in.cCorePrinterDrivers);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncGetCorePrinterDrivers_r(b, tctx, &r),
+ "winspool_AsyncCorePrinterDrivers failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "winspool_AsyncCorePrinterDrivers failed");
+
+ if (package_id) {
+ *package_id = r.out.pCorePrinterDrivers[0].szPackageID;
+ }
+
+ return true;
+}
+
+static bool test_AsyncDeletePrintDriverPackage(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct winspool_AsyncDeletePrinterDriverPackage r;
+
+ const char *architectures[] = {
+/* SPOOLSS_ARCHITECTURE_NT_X86, */
+ SPOOLSS_ARCHITECTURE_x64
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(architectures); i++) {
+
+ const char *package_id;
+
+ torture_assert(tctx,
+ test_get_core_printer_drivers_arch_guid(tctx, p,
+ architectures[i],
+ SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV,
+ &package_id),
+ "failed to get core printer driver");
+
+ r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.pszEnvironment = "";
+ r.in.pszInfPath = "";
+
+ torture_comment(tctx, "Testing AsyncDeletePrinterDriverPackage(%s, %s, %s)\n",
+ r.in.pszServer, architectures[i], package_id);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncDeletePrinterDriverPackage_r(b, tctx, &r),
+ "AsyncDeletePrinterDriverPackage failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_NOT_FOUND,
+ "AsyncDeletePrinterDriverPackage failed");
+
+ r.in.pszInfPath = package_id;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncDeletePrinterDriverPackage_r(b, tctx, &r),
+ "AsyncDeletePrinterDriverPackage failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT,
+ "AsyncDeletePrinterDriverPackage failed");
+
+ r.in.pszEnvironment = architectures[i];
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncDeletePrinterDriverPackage_r(b, tctx, &r),
+ "AsyncDeletePrinterDriverPackage failed");
+ torture_assert_hresult_equal(tctx, r.out.result, HRES_E_ACCESSDENIED,
+ "AsyncDeletePrinterDriverPackage failed");
+ }
+
+ return true;
+}
+
+static bool test_AsyncGetPrinterDriverDirectory(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct winspool_AsyncGetPrinterDriverDirectory r;
+ uint32_t pcbNeeded;
+ DATA_BLOB blob;
+ const char *s;
+
+ r.in.pName = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.pEnvironment = ctx->environment;
+ r.in.Level = 1;
+ r.in.cbBuf = 0x200;
+ r.in.pDriverDirectory = talloc_zero_array(tctx, uint8_t, r.in.cbBuf);
+ r.out.pcbNeeded = &pcbNeeded;
+ r.out.pDriverDirectory = r.in.pDriverDirectory;
+
+ torture_comment(tctx, "Testing AsyncGetPrinterDriverDirectory(%s, %s)\n",
+ r.in.pName, r.in.pEnvironment);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winspool_AsyncGetPrinterDriverDirectory_r(b, tctx, &r),
+ "AsyncGetPrinterDriverDirectory failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "AsyncGetPrinterDriverDirectory failed");
+
+ blob = data_blob_const(r.out.pDriverDirectory, pcbNeeded);
+
+ torture_assert(tctx,
+ pull_reg_sz(tctx, &blob, &s),
+ "failed to pull reg_sz");
+
+ torture_comment(tctx, "got: %s\n", s);
+
+ return true;
+}
+
+/*
+ * Test if one can close a printserver handle that has been acquired via
+ * winspool_AsyncOpenPrinter with a spoolss_ClosePrinter operation.
+ */
+
+static bool test_OpenPrinter(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ const char *printer_name;
+ struct policy_handle handle;
+ struct dcerpc_pipe *s;
+ struct dcerpc_binding *binding;
+ struct spoolss_UserLevel1 client_info;
+ struct spoolss_ClosePrinter r;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_binding(tctx, &binding),
+ "failed to get binding");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_binding_set_transport(binding, NCACN_NP),
+ "failed to set ncacn_np transport");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_binding_set_object(binding, GUID_zero()),
+ "failed to set object uuid to zero");
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection_with_binding(tctx, binding, &s, &ndr_table_spoolss),
+ "failed to connect to spoolss");
+
+ printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ client_info = test_get_client_info(tctx, WIN_7, 6, 1, "testclient_machine", "testclient_user");
+
+ torture_assert(tctx,
+ test_AsyncOpenPrinter_byprinter(tctx, ctx, p, printer_name, client_info, &handle),
+ "failed to open printserver via winspool");
+
+
+ r.in.handle = &handle;
+ r.out.handle = &handle;
+
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_spoolss_ClosePrinter_r(s->binding_handle, tctx, &r),
+ NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "ClosePrinter failed");
+
+ talloc_free(s);
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_iremotewinspool(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "iremotewinspool");
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "printserver");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_iremotewinspool_setup,
+ torture_rpc_iremotewinspool_teardown);
+
+ torture_tcase_add_simple_test(tcase, "AsyncOpenPrinter", test_AsyncOpenPrinter);
+ torture_tcase_add_simple_test(tcase, "SyncRegisterForRemoteNotifications", test_SyncRegisterForRemoteNotifications);
+ torture_tcase_add_simple_test(tcase, "SyncUnRegisterForRemoteNotifications", test_SyncUnRegisterForRemoteNotifications);
+ torture_tcase_add_simple_test(tcase, "AsyncClosePrinter", test_AsyncClosePrinter);
+ torture_tcase_add_simple_test(tcase, "AsyncUploadPrinterDriverPackage", test_AsyncUploadPrinterDriverPackage);
+ torture_tcase_add_simple_test(tcase, "AsyncEnumPrinters", test_AsyncEnumPrinters);
+ torture_tcase_add_simple_test(tcase, "AsyncGetPrinterData", test_AsyncGetPrinterData);
+ torture_tcase_add_simple_test(tcase, "AsyncCorePrinterDriverInstalled", test_AsyncCorePrinterDriverInstalled);
+ torture_tcase_add_simple_test(tcase, "AsyncDeletePrintDriverPackage", test_AsyncDeletePrintDriverPackage);
+ torture_tcase_add_simple_test(tcase, "AsyncGetPrinterDriverDirectory", test_AsyncGetPrinterDriverDirectory);
+ torture_tcase_add_simple_test(tcase, "AsyncOpenPrinterValidateBuildNumber", test_AsyncOpenPrinterValidateBuildNumber);
+
+ tcase = torture_suite_add_tcase(suite, "handles");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_iremotewinspool_setup,
+ torture_rpc_iremotewinspool_teardown);
+
+ torture_tcase_add_simple_test(tcase, "OpenPrinter", test_OpenPrinter);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/iremotewinspool_common.c b/source4/torture/rpc/iremotewinspool_common.c
new file mode 100644
index 0000000..33dd469
--- /dev/null
+++ b/source4/torture/rpc/iremotewinspool_common.c
@@ -0,0 +1,247 @@
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_winspool.h"
+#include "librpc/gen_ndr/ndr_winspool_c.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include "libcli/registry/util_reg.h"
+#include "torture/rpc/iremotewinspool_common.h"
+#include "lib/printer_driver/printer_driver.h"
+
+void init_winreg_String(struct winreg_String *name, const char *s)
+{
+ name->name = s;
+ if (s != NULL) {
+ name->name_len = 2 * (strlen_m(s) + 1);
+ name->name_size = name->name_len;
+ } else {
+ name->name_len = 0;
+ name->name_size = 0;
+ }
+}
+
+struct spoolss_UserLevel1 test_get_client_info(struct torture_context *tctx,
+ enum client_os_version os,
+ enum spoolss_MajorVersion major_number,
+ enum spoolss_MinorVersion minor_number,
+ const char *machine,
+ const char *user)
+{
+ struct spoolss_UserLevel1 level1;
+
+ level1.size = 28;
+ level1.client = talloc_asprintf(tctx, "\\\\%s", machine);
+ level1.user = user;
+ level1.processor = PROCESSOR_ARCHITECTURE_AMD64;
+ level1.major = major_number;
+ level1.minor = minor_number;
+
+ if (os == WIN_SERVER_2016 || os == WIN_10) {
+ level1.build = 10586;
+ } else if (os == WIN_SERVER_2012 || os == WIN_8) {
+ level1.build = 9200;
+ } else if (os == WIN_SERVER_2008R2 || os == WIN_7) {
+ level1.build = 7007;
+ } else if (os == WIN_SERVER_2008 || os == WIN_VISTA) {
+ level1.build = 6000;
+ } else if (os == WIN_2000) {
+ level1.build = 1382;
+ }
+
+ return level1;
+}
+
+bool test_AsyncOpenPrinter_byprinter(struct torture_context *tctx,
+ struct test_iremotewinspool_context *ctx,
+ struct dcerpc_pipe *p,
+ const char *printer_name,
+ struct spoolss_UserLevel1 cinfo,
+ struct policy_handle *handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct spoolss_UserLevelCtr client_info_ctr;
+ uint32_t access_mask = SERVER_ALL_ACCESS;
+ struct winspool_AsyncOpenPrinter r;
+ NTSTATUS status;
+ bool ok = true;
+
+ ZERO_STRUCT(devmode_ctr);
+
+ client_info_ctr.level = 1;
+ client_info_ctr.user_info.level1 = &cinfo;
+
+ r.in.pPrinterName = printer_name;
+ r.in.pDatatype = NULL;
+ r.in.pDevModeContainer = &devmode_ctr;
+ r.in.AccessRequired = access_mask;
+ r.in.pClientInfo = &client_info_ctr;
+ r.out.pHandle = handle;
+
+ status = dcerpc_winspool_AsyncOpenPrinter_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncOpenPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "AsyncOpenPrinter failed");
+
+done:
+
+ return ok;
+}
+
+bool test_get_environment(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char **architecture)
+{
+ DATA_BLOB blob;
+ enum winreg_Type type;
+ uint8_t *data;
+ uint32_t needed;
+ bool ok;
+
+ ok = test_AsyncGetPrinterData_args(tctx, b, handle, "Architecture", &type, &data, &needed);
+ torture_assert(tctx, ok, "failed to get Architecture");
+
+ torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");
+
+ blob = data_blob_const(data, needed);
+
+ torture_assert(tctx,
+ pull_reg_sz(tctx, &blob, architecture),
+ "failed to pull environment");
+
+ return true;
+}
+
+bool test_AsyncClosePrinter_byhandle(struct torture_context *tctx,
+ struct test_iremotewinspool_context *ctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ struct winspool_AsyncClosePrinter r;
+ NTSTATUS status;
+ bool ok = true;
+
+ r.in.phPrinter = handle;
+ r.out.phPrinter = handle;
+
+ status = dcerpc_winspool_AsyncClosePrinter_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncClosePrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "AsyncClosePrinter failed");
+
+done:
+
+ return ok;
+}
+
+static bool test_AsyncGetPrinterData_checktype(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type *expected_type,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ struct winspool_AsyncGetPrinterData r;
+ enum winreg_Type type;
+ uint32_t needed;
+ NTSTATUS status;
+ bool ok = true;
+
+ r.in.hPrinter = *handle;
+ r.in.pValueName = value_name;
+ r.in.nSize = 0;
+ r.out.pType = &type;
+ r.out.pData = talloc_zero_array(tctx, uint8_t, r.in.nSize);
+ r.out.pcbNeeded = &needed;
+
+ torture_comment(tctx, "Testing AsyncGetPrinterData(%s)\n",
+ r.in.pValueName);
+
+ status = dcerpc_winspool_AsyncGetPrinterData_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncGetPrinterData failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ if (expected_type) {
+ torture_assert_int_equal(tctx, type, *expected_type, "unexpected type");
+ }
+ r.in.nSize = needed;
+ r.out.pData = talloc_zero_array(tctx, uint8_t, r.in.nSize);
+
+ status = dcerpc_winspool_AsyncGetPrinterData_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncGetPrinterData failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "AsyncGetPrinterData failed");
+
+ if (type_p) {
+ *type_p = type;
+ }
+
+ if (data_p) {
+ *data_p = r.out.pData;
+ }
+
+ if (needed_p) {
+ *needed_p = needed;
+ }
+
+done:
+
+ return ok;
+}
+
+bool test_AsyncGetPrinterData_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ return test_AsyncGetPrinterData_checktype(tctx, b, handle,
+ value_name,
+ NULL,
+ type_p, data_p, needed_p);
+}
+
+/* Parse a driver inf file */
+bool parse_inf_driver(struct torture_context *tctx,
+ const char *driver_name,
+ const char *abs_inf_path,
+ const char *driver_arch,
+ const char *core_driver_inf,
+ struct spoolss_AddDriverInfo8 **_parsed_dinfo)
+{
+ struct spoolss_AddDriverInfo8 *drv_info;
+ const char *source_disk_name = NULL;
+ NTSTATUS status;
+ bool ok = true;
+
+ drv_info = talloc_zero(tctx, struct spoolss_AddDriverInfo8);
+ torture_assert_not_null_goto(tctx, drv_info, ok, done, "Cannot allocate memory");
+
+ status = driver_inf_parse(tctx,
+ core_driver_inf,
+ abs_inf_path,
+ driver_arch,
+ driver_name,
+ drv_info,
+ &source_disk_name);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_DRIVER_INTERNAL_ERROR)) {
+ torture_comment(tctx, "--- Verify the correct torture option:driver_name is provided\n");
+ }
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to parse driver inf\n");
+
+ *_parsed_dinfo = drv_info;
+done:
+ return ok;
+}
diff --git a/source4/torture/rpc/iremotewinspool_common.h b/source4/torture/rpc/iremotewinspool_common.h
new file mode 100644
index 0000000..fb6efd9
--- /dev/null
+++ b/source4/torture/rpc/iremotewinspool_common.h
@@ -0,0 +1,101 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ iremotewinspool rpc test operations
+
+ Copyright (C) 2018 Justin Stephenson
+
+ 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 "torture/rpc/torture_rpc.h"
+
+#define REG_DRIVER_CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\Print"
+
+struct test_driver_info {
+ struct smbcli_state *cli;
+ struct spoolss_AddDriverInfo8 *info;
+ const char *local_driver_path;
+ size_t driver_path_len;
+ char *server_name;
+ char *share_name;
+ char *print_upload_guid_dir;
+ const char *inf_file;
+ const char *uploaded_inf_path;
+ const char *driver_name;
+ const char *driver_arch;
+ const char *core_driver_inf;
+};
+
+struct test_iremotewinspool_context {
+ struct GUID object_uuid;
+ struct dcerpc_pipe *iremotewinspool_pipe;
+ struct policy_handle server_handle;
+ struct test_driver_info *dinfo;
+ const char *environment;
+};
+
+enum client_os_version
+{
+ WIN_2000,
+ WIN_VISTA,
+ WIN_SERVER_2008,
+ WIN_7,
+ WIN_SERVER_2008R2,
+ WIN_8,
+ WIN_SERVER_2012,
+ WIN_10,
+ WIN_SERVER_2016
+};
+
+void init_winreg_String(struct winreg_String *name, const char *s);
+
+struct spoolss_UserLevel1 test_get_client_info(struct torture_context *tctx,
+ enum client_os_version os,
+ enum spoolss_MajorVersion major_number,
+ enum spoolss_MinorVersion minor_number,
+ const char *machine,
+ const char *user);
+
+bool test_AsyncOpenPrinter_byprinter(struct torture_context *tctx,
+ struct test_iremotewinspool_context *ctx,
+ struct dcerpc_pipe *p,
+ const char *printer_name,
+ struct spoolss_UserLevel1 cinfo,
+ struct policy_handle *handle);
+
+bool test_get_environment(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char **architecture);
+
+bool test_AsyncClosePrinter_byhandle(struct torture_context *tctx,
+ struct test_iremotewinspool_context *ctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle);
+
+bool test_AsyncGetPrinterData_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p);
+
+bool parse_inf_driver(struct torture_context *tctx,
+ const char *driver_name,
+ const char *abs_inf_path,
+ const char *driver_arch,
+ const char *core_driver_inf,
+ struct spoolss_AddDriverInfo8 **_parsed_dinfo);
diff --git a/source4/torture/rpc/iremotewinspool_driver.c b/source4/torture/rpc/iremotewinspool_driver.c
new file mode 100644
index 0000000..f88b13a
--- /dev/null
+++ b/source4/torture/rpc/iremotewinspool_driver.c
@@ -0,0 +1,840 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for iremotewinspool driver rpc operations
+
+ Copyright (C) Justin Stephenson 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <dirent.h>
+#include <talloc.h>
+#include <libgen.h>
+#include "includes.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_winspool.h"
+#include "librpc/gen_ndr/ndr_winspool_c.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include "libcli/registry/util_reg.h"
+#include "torture/rpc/iremotewinspool_common.h"
+#include "libcli/libcli.h"
+#include "param/param.h"
+#include "lib/registry/registry.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "lib/cmdline/cmdline.h"
+#include "system/filesys.h"
+#include "lib/util/tftw.h"
+
+/* Connect to print driver share //server_name/share */
+static bool smb_connect_print_share(struct torture_context *tctx,
+ const char *server_name,
+ const char *share_name,
+ struct smbcli_state **cli)
+{
+ NTSTATUS status;
+ bool ok = true;
+
+ struct smbcli_options smb_options;
+ struct smbcli_session_options smb_session_options;
+
+ torture_comment(tctx, "Connecting to printer driver share '//%s/%s'\n",
+ server_name, share_name);
+
+ lpcfg_smbcli_options(tctx->lp_ctx, &smb_options);
+ lpcfg_smbcli_session_options(tctx->lp_ctx, &smb_session_options);
+
+ /* On Windows, SMB1 must be enabled! */
+ status = smbcli_full_connection(tctx, cli, server_name,
+ lpcfg_smb_ports(tctx->lp_ctx),
+ share_name, NULL,
+ lpcfg_socket_options(tctx->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(tctx->lp_ctx),
+ tctx->ev,
+ &smb_options,
+ &smb_session_options,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to connect to print$ share");
+
+done:
+
+ return ok;
+}
+
+/* Copy file to destination where dst_fpath is a smb share path,
+ * files are either created or overwritten */
+static bool smb_copy_files(TALLOC_CTX *tctx,
+ const char *fpath,
+ const char *dst_fpath,
+ struct test_driver_info *dinfo)
+{
+ FILE *fp;
+ int smbfp = 0;
+ char *buffer = NULL;
+ int maxwrite = 64512;
+ size_t nread;
+ ssize_t nwrote;
+ bool ok = true;
+ size_t total_read;
+
+ fp = fopen(fpath, "r");
+ torture_assert_goto(tctx, fp, ok, done, "Failed to open local file\n");
+
+ smbfp = smbcli_open(dinfo->cli->tree, dst_fpath, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+ torture_assert_int_not_equal_goto(tctx, smbfp, -1, ok, done, "Failed to open dst file\n");
+
+ buffer = talloc_array(tctx, char, maxwrite);
+ torture_assert_not_null_goto(tctx, buffer, ok, done, "Failed to allocate buffer\n");
+
+ total_read = 0;
+
+ while (!feof(fp)) {
+ nread = fread(buffer, 1, maxwrite, fp);
+ if (ferror(fp)) {
+ torture_warning(tctx, "Error reading file [%s]\n", fpath);
+ continue;
+ }
+
+ nwrote = smbcli_write(dinfo->cli->tree, smbfp, 0, buffer, total_read, nread);
+ if (nwrote != nread) {
+ torture_warning(tctx, "Not all data in stream written!\n");
+ }
+
+ total_read += nread;
+ }
+
+ fclose(fp);
+ smbcli_close(dinfo->cli->tree, smbfp);
+done:
+
+ TALLOC_FREE(buffer);
+ return ok;
+}
+
+/* Callback function provided to tftw() to
+ * copy driver files to smb share */
+static int copy_driver_files(TALLOC_CTX *tctx,
+ const char *fpath,
+ const struct stat *sb,
+ enum tftw_flags_e flag,
+ void *userdata)
+{
+ char *dst_fpath = NULL;
+ struct test_driver_info *dinfo = userdata;
+ char *path = NULL;
+ NTSTATUS status;
+ bool ok = true;
+
+ path = talloc_strdup(tctx, fpath + dinfo->driver_path_len);
+ torture_assert_not_null_goto(tctx, path, ok, done, "Cannot allocate memory");
+
+ string_replace(path, '/', '\\');
+
+ dst_fpath = talloc_asprintf(tctx, "%s%s", dinfo->print_upload_guid_dir, path);
+ torture_assert_not_null_goto(tctx, dst_fpath, ok, done, "Cannot allocate memory");
+
+ switch (flag) {
+ case TFTW_FLAG_FILE:
+ ok = smb_copy_files(tctx, fpath, dst_fpath, dinfo);
+ torture_assert_goto(tctx, ok, ok, done, "Failed to copy files over smb");
+ break;
+ case TFTW_FLAG_DIR:
+ status = smbcli_mkdir(dinfo->cli->tree, dst_fpath);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to create directories");
+ break;
+ case TFTW_FLAG_SLINK:
+ case TFTW_FLAG_DNR:
+ case TFTW_FLAG_NSTAT:
+ case TFTW_FLAG_SPEC:
+ case TFTW_FLAG_DP:
+ case TFTW_FLAG_SLN:
+ torture_warning(tctx, "WARN: Unhandled typeflag [%s]\n", fpath);
+ break;
+ }
+
+done:
+ TALLOC_FREE(path);
+ TALLOC_FREE(dst_fpath);
+
+ if (ok == true) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static bool test_get_driver_torture_options(struct torture_context *tctx,
+ const char **_local_driver_path,
+ const char **_inf_file,
+ const char **_driver_name,
+ const char **_driver_arch,
+ const char **_core_driver_inf)
+{
+ const char *local_driver_path = NULL;
+ const char *inf_file = NULL;
+ const char *driver_name = NULL;
+ const char *driver_arch = NULL;
+ const char *core_driver_inf = NULL;
+ const char *arches_list[] = {
+ SPOOLSS_ARCHITECTURE_x64,
+ SPOOLSS_ARCHITECTURE_NT_X86,
+ SPOOLSS_ARCHITECTURE_IA_64,
+ SPOOLSS_ARCHITECTURE_ARM,
+ SPOOLSS_ARCHITECTURE_4_0,
+ NULL,
+ };
+ const char **p;
+ bool valid = false;
+ bool ok = true;
+
+ local_driver_path = torture_setting_string(tctx, "driver_path", NULL);
+ if (local_driver_path == NULL) {
+ torture_fail(tctx,
+ "option --option=torture:driver_path="
+ "/full/path/to/local/driver/dir\n");
+ }
+
+ inf_file = torture_setting_string(tctx, "inf_file", NULL);
+ if (inf_file == NULL) {
+ torture_fail(tctx,
+ "option --option=torture:inf_file="
+ "filename.inf\n");
+ }
+
+ driver_name = torture_setting_string(tctx, "driver_name", NULL);
+ if (driver_name == NULL) {
+ torture_fail(tctx,
+ "option --option=torture:driver_name="
+ "driver name\n");
+ }
+
+ driver_arch = torture_setting_string(tctx, "driver_arch", NULL);
+ if (driver_arch == NULL) {
+ torture_fail(tctx,
+ "option --option=torture:driver_arch="
+ "driver arch\n");
+ }
+
+ core_driver_inf = torture_setting_string(tctx, "core_driver_inf", NULL);
+
+ for (p = arches_list; *p != NULL; p++) {
+ if (strequal(*p, driver_arch) == 0) {
+ valid = true;
+ break;
+ }
+ }
+ torture_assert_goto(tctx, valid, ok, done, "Invalid driver arch provided");
+
+ *_local_driver_path = local_driver_path;
+ *_inf_file = inf_file;
+ *_driver_name = driver_name;
+ *_driver_arch = driver_arch;
+ *_core_driver_inf = core_driver_inf;
+done:
+ return ok;
+}
+
+
+static bool test_get_misc_driver_info(struct torture_context *tctx,
+ struct test_driver_info *dinfo,
+ const char **_abs_inf_path,
+ size_t *_driver_path_len)
+{
+ const char *abs_inf_path;
+ size_t driver_path_len;
+ bool ok = true;
+
+ driver_path_len = strlen(dinfo->local_driver_path);
+ torture_assert_int_not_equal_goto(tctx, driver_path_len, 0, ok, done, "driver path length is 0");
+
+ abs_inf_path = talloc_asprintf(tctx, "%s/%s", dinfo->local_driver_path, dinfo->inf_file);
+ torture_assert_not_null_goto(tctx, abs_inf_path, ok, done, "Cannot allocate memory");
+
+ *_abs_inf_path = abs_inf_path;
+ *_driver_path_len = driver_path_len;
+done:
+
+ return ok;
+}
+
+/* Uninstall the previously installed print driver */
+static bool test_uninstall_printer_driver(struct torture_context *tctx,
+ struct test_iremotewinspool_context *ctx)
+{
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct winspool_AsyncDeletePrinterDriverEx r;
+ bool ok = true;
+ NTSTATUS status;
+
+ r.in.pName = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ r.in.pDriverName = talloc_strdup(tctx, ctx->dinfo->driver_name);
+ torture_assert_not_null_goto(tctx, r.in.pDriverName, ok, done, "Cannot allocate memory");
+
+ r.in.pEnvironment = SPOOLSS_ARCHITECTURE_x64;
+
+ r.in.dwDeleteFlag = 0;
+ r.in.dwVersionNum = 0;
+
+ status = dcerpc_winspool_AsyncDeletePrinterDriverEx_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncDeletePrinterDriverEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "AsyncDeletePrinterDriverEx failed");
+done:
+
+ return ok;
+}
+
+/* Remove the leftover print driver package files from the driver store */
+static bool test_remove_driver_package(struct torture_context *tctx,
+ struct test_iremotewinspool_context *ctx)
+{
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct winspool_AsyncDeletePrinterDriverPackage r;
+ bool ok = true;
+ NTSTATUS status;
+
+ r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ torture_assert_not_null_goto(tctx, r.in.pszServer, ok, done, "Cannot allocate memory");
+
+ r.in.pszInfPath = ctx->dinfo->uploaded_inf_path;
+
+ r.in.pszEnvironment = SPOOLSS_ARCHITECTURE_x64;
+
+ status = dcerpc_winspool_AsyncDeletePrinterDriverPackage_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncDeletePrinterPackage failed");
+
+ torture_assert_hresult_ok(tctx, r.out.result, "AsyncDeletePrinterDriverPackage failed");
+done:
+
+ return ok;
+}
+
+static bool test_winreg_iremotewinspool_openhklm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *winreg_bh,
+ struct policy_handle *_hklm_handle)
+{
+ struct winreg_OpenHKLM r;
+ NTSTATUS status;
+ bool ok = true;
+
+ r.in.system_name = NULL;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = _hklm_handle;
+
+ status = dcerpc_winreg_OpenHKLM_r(winreg_bh, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to Open HKLM");
+
+ torture_assert_werr_ok(tctx, r.out.result, "Failed to Open HKLM");
+done:
+
+ return ok;
+}
+
+static bool test_winreg_iremotewinspool_openkey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *winreg_bh,
+ struct policy_handle *hklm_handle,
+ const char *keyname,
+ struct policy_handle *_key_handle)
+{
+ struct winreg_OpenKey r;
+ NTSTATUS status;
+ bool ok = true;
+
+ r.in.parent_handle = hklm_handle;
+ init_winreg_String(&r.in.keyname, keyname);
+ r.in.options = REG_OPTION_NON_VOLATILE;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = _key_handle;
+
+ status = dcerpc_winreg_OpenKey_r(winreg_bh, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "OpenKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "OpenKey failed");
+done:
+
+ return ok;
+}
+
+static bool test_winreg_iremotewinspool_queryvalue(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *key_handle,
+ const char *value_name,
+ const char **_valuestr)
+{
+ struct winreg_QueryValue r;
+ enum winreg_Type type = REG_NONE;
+ struct winreg_String valuename;
+ DATA_BLOB blob;
+ const char *str;
+ uint32_t data_size = 0;
+ uint32_t data_length = 0;
+ uint8_t *data = NULL;
+ NTSTATUS status;
+ bool ok = true;
+
+ init_winreg_String(&valuename, value_name);
+
+ data = talloc_zero_array(tctx, uint8_t, 0);
+
+ r.in.handle = key_handle;
+ r.in.value_name = &valuename;
+ r.in.type = &type;
+ r.in.data_size = &data_size;
+ r.in.data_length = &data_length;
+ r.in.data = data;
+
+ r.out.type = &type;
+ r.out.data = data;
+ r.out.data_size = &data_size;
+ r.out.data_length = &data_length;
+
+ status = dcerpc_winreg_QueryValue_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "winreg_QueryValue failure");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ *r.in.data_size = *r.out.data_size;
+ data = talloc_zero_array(tctx, uint8_t, *r.in.data_size);
+ r.in.data = data;
+ r.out.data = data;
+ status = dcerpc_winreg_QueryValue_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "QueryValue failed");
+ }
+ torture_assert_werr_ok(tctx, r.out.result, "QueryValue failed");
+
+ torture_assert_int_equal_goto(tctx, *r.out.type, REG_SZ, ok, done, "unexpected type");
+ blob = data_blob(r.out.data, *r.out.data_size);
+ str = reg_val_data_string(tctx, REG_SZ, blob);
+
+ *_valuestr = str;
+done:
+
+ return ok;
+}
+
+/* Validate the installed driver subkey exists, and the InfPath
+ * value matches the pszDestInfPath from test_UploadPrinterDriverPackage */
+static bool test_winreg_validate_driver(struct torture_context *tctx,
+ struct dcerpc_pipe *winreg_pipe,
+ struct test_driver_info *dinfo)
+{
+ struct policy_handle hklm_handle;
+ struct policy_handle key_handle;
+ char *driver_key = NULL;
+ const char *val_name = NULL;
+ const char *val_str = NULL;
+ bool ok = true;
+
+ struct dcerpc_binding_handle *winreg_bh;
+ struct spoolss_AddDriverInfo8 *parsed_dinfo;
+
+ winreg_bh = winreg_pipe->binding_handle;
+ parsed_dinfo = dinfo->info;
+
+ /* OpenHKLM */
+ ok = test_winreg_iremotewinspool_openhklm(tctx, winreg_bh, &hklm_handle);
+ torture_assert_goto(tctx, ok, ok, done, "Failed to perform winreg OpenHKLM");
+
+ /* Open registry subkey for the installed print driver */
+ driver_key = talloc_asprintf(tctx, "%s\\Environments\\%s\\Drivers\\Version-%d\\%s",
+ REG_DRIVER_CONTROL_KEY,
+ parsed_dinfo->architecture,
+ parsed_dinfo->version,
+ parsed_dinfo->driver_name);
+ torture_assert_not_null_goto(tctx, driver_key, ok, done, "Cannot allocate driver_key string");
+ ok = test_winreg_iremotewinspool_openkey(tctx, winreg_bh, &hklm_handle,
+ driver_key,
+ &key_handle);
+ torture_assert_goto(tctx, ok, ok, done, "Failed to perform winreg OpenKey");
+
+ /* Read infpath value and validate this matches what was uploaded */
+ val_name = "InfPath";
+ ok = test_winreg_iremotewinspool_queryvalue(tctx, winreg_bh, &key_handle, val_name,
+ &val_str);
+ torture_assert_goto(tctx, ok, ok, done, "QueryValue failed");
+
+ torture_assert_casestr_equal(tctx, val_str,
+ dinfo->uploaded_inf_path,
+ "InfPath does not match uploaded inf");
+done:
+
+ return ok;
+}
+
+static bool test_init_iremotewinspool_conn(struct torture_context *tctx,
+ struct test_iremotewinspool_context *t)
+{
+ struct dcerpc_binding *binding = {0};
+ bool ok = true;
+ NTSTATUS status;
+
+ status = GUID_from_string(IREMOTEWINSPOOL_OBJECT_GUID, &t->object_uuid);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "failed to parse GUID");
+
+ status = torture_rpc_binding(tctx, &binding);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "failed to retrieve torture binding");
+
+ status = dcerpc_binding_set_object(binding, t->object_uuid);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "failed to set object_uuid");
+
+ status = torture_rpc_connection_with_binding(tctx, binding, &t->iremotewinspool_pipe,
+ &ndr_table_iremotewinspool);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Error connecting to server");
+
+done:
+
+ return ok;
+
+}
+
+static bool test_init_iremotewinspool_openprinter(struct torture_context *tctx,
+ struct test_iremotewinspool_context *t)
+{
+ struct spoolss_UserLevel1 client_info = {0};
+ char *printer_name = NULL;
+ bool ok = true;
+
+ printer_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(t->iremotewinspool_pipe));
+ torture_assert_not_null_goto(tctx, printer_name, ok, done, "Cannot allocate memory");
+
+ client_info = test_get_client_info(tctx, WIN_7, 3, SPOOLSS_MINOR_VERSION_0,
+ "testclient_machine", "testclient_user");
+
+ ok = test_AsyncOpenPrinter_byprinter(tctx, t, t->iremotewinspool_pipe, printer_name,
+ client_info, &t->server_handle);
+ torture_assert_goto(tctx, ok, ok, done, "failed to open printserver");
+
+ ok = test_get_environment(tctx, t->iremotewinspool_pipe->binding_handle,
+ &t->server_handle, &t->environment);
+ torture_assert_goto(tctx, ok, ok, done, "failed to get environment");
+
+done:
+ TALLOC_FREE(printer_name);
+
+ return ok;
+}
+
+static bool test_init_driver_info(struct torture_context *tctx,
+ struct test_iremotewinspool_context *t)
+{
+ bool ok = true;
+ const char *abs_inf_path;
+ struct test_driver_info *drv_info = {0};
+
+ drv_info = talloc_zero(tctx, struct test_driver_info);
+ torture_assert_not_null_goto(tctx, drv_info, ok, done, "Cannot allocate memory");
+
+ t->dinfo = drv_info;
+
+ ok = test_get_driver_torture_options(tctx,
+ &drv_info->local_driver_path,
+ &drv_info->inf_file,
+ &drv_info->driver_name,
+ &drv_info->driver_arch,
+ &drv_info->core_driver_inf);
+ torture_assert_goto(tctx, ok, ok, done, "Failed to get driver torture options");
+
+ ok = test_get_misc_driver_info(tctx, drv_info,
+ &abs_inf_path,
+ &drv_info->driver_path_len);
+ torture_assert_goto(tctx, ok, ok, done, "Failed to get misc driver info");
+
+ ok = parse_inf_driver(tctx, drv_info->driver_name, abs_inf_path, drv_info->driver_arch,
+ drv_info->core_driver_inf, &drv_info->info);
+ torture_assert_goto(tctx, ok, ok, done, "Failed to parse inf driver");
+
+ /* Ensure that we are trying to install the correct device class:
+ * https://docs.microsoft.com/en-us/windows-hardware/drivers/install/system-defined-device-setup-classes-available-to-vendors
+ */
+ if (!(drv_info->info->printer_driver_attributes & PRINTER_DRIVER_CLASS)) {
+ ok = false;
+ torture_fail_goto(tctx, done, "Inf file Class value must be Printer");
+ }
+done:
+ return ok;
+
+}
+
+static bool test_init_server_and_share_info(struct torture_context *tctx,
+ struct test_iremotewinspool_context *t)
+{
+ struct GUID guid;
+ bool ok = true;
+
+ t->dinfo->server_name = talloc_asprintf(tctx, "%s", dcerpc_server_name(t->iremotewinspool_pipe));
+ torture_assert_not_null_goto(tctx, t->dinfo->server_name, ok, done, "Cannot allocate memory");
+
+ t->dinfo->share_name = talloc_strdup(tctx, "print$");
+ torture_assert_not_null_goto(tctx, t->dinfo->share_name, ok, done, "Cannot allocate memory");
+
+ guid = GUID_random();
+ t->dinfo->print_upload_guid_dir = GUID_string2(tctx, &guid);
+done:
+ return ok;
+}
+
+
+static bool torture_rpc_iremotewinspool_drv_setup_common(struct torture_context *tctx,
+ struct test_iremotewinspool_context *t)
+{
+ bool ok = true;
+ int ret = 0;
+
+ ok = test_init_driver_info(tctx, t);
+ torture_assert_goto(tctx, ok, ok, done, "failed to init driver info");
+
+ ok = test_init_iremotewinspool_conn(tctx, t);
+ torture_assert_goto(tctx, ok, ok, done, "failed to init iremotewinspool conn");
+
+ ok = test_init_iremotewinspool_openprinter(tctx, t);
+ torture_assert_goto(tctx, ok, ok, done, "failed to init iremotewinspool openprinter");
+
+ ok = test_init_server_and_share_info(tctx, t);
+ torture_assert_goto(tctx, ok, ok, done, "failed to init server and share info");
+
+ ret = smb_connect_print_share(tctx, t->dinfo->server_name, t->dinfo->share_name, &t->dinfo->cli);
+ torture_assert_goto(tctx, ret, ok, done, "Failed to connect to print share");
+
+done:
+
+ return ok;
+}
+
+static bool torture_rpc_iremotewinspool_drv_setup(struct torture_context *tctx,
+ void **data)
+{
+ struct test_iremotewinspool_context *t;
+
+ *data = t = talloc_zero(tctx, struct test_iremotewinspool_context);
+
+ return torture_rpc_iremotewinspool_drv_setup_common(tctx, t);
+}
+
+static bool torture_rpc_iremotewinspool_drv_teardown_common(struct torture_context *tctx,
+ struct test_iremotewinspool_context *t)
+{
+ smbcli_deltree(t->dinfo->cli->tree, t->dinfo->print_upload_guid_dir);
+ smb_raw_exit(t->dinfo->cli->session);
+
+ test_uninstall_printer_driver(tctx, t);
+ test_remove_driver_package(tctx, t);
+
+ test_AsyncClosePrinter_byhandle(tctx, t, t->iremotewinspool_pipe, &t->server_handle);
+
+ return true;
+}
+
+static bool torture_rpc_iremotewinspool_drv_teardown(struct torture_context *tctx,
+ void *data)
+{
+ struct test_iremotewinspool_context *t = talloc_get_type(data, struct test_iremotewinspool_context);
+ bool ret;
+
+ ret = torture_rpc_iremotewinspool_drv_teardown_common(tctx, t);
+ talloc_free(t);
+
+ return ret;
+}
+
+/* Creates {GUID} directory inside //server/print$ then copies driver files
+ * and directories from torture option driver_path to this directory over smb */
+static bool test_CopyDriverFiles(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ bool ret = false;
+ bool ok = true;
+ NTSTATUS status;
+
+ status = smbcli_mkdir(ctx->dinfo->cli->tree, ctx->dinfo->print_upload_guid_dir);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to create upload directory");
+
+ /* Walk the provided torture option driver_path file tree, creating the directory heirarchy and
+ * copying all files to print$/{GUID}/ share */
+ ret = tftw(tctx, ctx->dinfo->local_driver_path, copy_driver_files, TFTW_MAX_DEPTH, ctx->dinfo);
+ torture_assert_int_equal_goto(tctx, ret, 0, ok, done, "Failed to copy driver files to print$/{GUID}/ dir");
+
+done:
+
+ return ok;
+}
+
+/*
+ * Upload print driver package files and inf file, preparing the print server
+ * for driver installation
+ */
+static bool test_UploadPrinterDriverPackage(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ struct spoolss_AddDriverInfo8 *parsed_dinfo;
+ struct winspool_AsyncUploadPrinterDriverPackage r;
+ uint32_t pcchDestInfPath = 0;
+ NTSTATUS status;
+ bool ok = true;
+
+ parsed_dinfo = ctx->dinfo->info;
+
+ r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ torture_assert_not_null_goto(tctx, r.in.pszServer, ok, done, "Cannot allocate memory");
+
+ r.in.pszInfPath = talloc_asprintf(tctx, "\\\\%s\\%s\\%s\\%s", ctx->dinfo->server_name,
+ ctx->dinfo->share_name,
+ ctx->dinfo->print_upload_guid_dir,
+ ctx->dinfo->inf_file);
+ torture_assert_not_null_goto(tctx, r.in.pszInfPath, ok, done, "Cannot allocate memory");
+
+ r.in.pszEnvironment = parsed_dinfo->architecture;
+ /* Upload driver package files even if the driver package is already present
+ * on the print server */
+ r.in.dwFlags = UPDP_UPLOAD_ALWAYS;
+ pcchDestInfPath = 260;
+ r.in.pszDestInfPath = NULL;
+ r.in.pcchDestInfPath = &pcchDestInfPath;
+ r.out.pszDestInfPath = NULL;
+ r.out.pcchDestInfPath = &pcchDestInfPath;
+
+ r.in.pszDestInfPath = talloc_zero(tctx, const char);
+ torture_assert_not_null_goto(tctx, r.in.pszDestInfPath, ok, done, "Cannot allocate memory");
+ r.out.pszDestInfPath = talloc_zero(tctx, const char);
+ torture_assert_not_null_goto(tctx, r.out.pszDestInfPath, ok, done, "Cannot allocate memory");
+
+ status = dcerpc_winspool_AsyncUploadPrinterDriverPackage_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncUploadPrinterDriverPackage failed");
+
+ torture_assert_hresult_ok(tctx, r.out.result, "AsyncUploadPrinterDriverPackage failed");
+
+ ctx->dinfo->uploaded_inf_path = talloc_strdup(tctx, r.out.pszDestInfPath);
+ torture_assert_not_null_goto(tctx, ctx->dinfo->uploaded_inf_path, ok, done, "Cannot allocate memory");
+
+done:
+
+ return ok;
+}
+
+/* Install the driver that was successfully uploaded to the printer driver
+ * store, note that Windows validates the pszDriverName as mentioned below */
+static bool test_InstallPrinterDriverFromPackage(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *p = ctx->iremotewinspool_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ char *abs_inf_path = NULL;
+ struct spoolss_AddDriverInfo8 *parsed_dinfo;
+ struct winspool_AsyncInstallPrinterDriverFromPackage r;
+ bool ok = true;
+ NTSTATUS status;
+
+ parsed_dinfo = ctx->dinfo->info;
+
+ r.in.pszServer = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ torture_assert_not_null_goto(tctx, r.in.pszServer, ok, done, "Cannot allocate memory");
+
+ /* output string(pszDestInfPath) from test_UploadPrinterDriverPackage() */
+ r.in.pszInfPath = talloc_strdup(tctx, ctx->dinfo->uploaded_inf_path);
+ torture_assert_not_null_goto(tctx, r.in.pszInfPath, ok, done, "Cannot allocate memory");
+
+ abs_inf_path = talloc_asprintf(tctx, "%s/%s", ctx->dinfo->local_driver_path, ctx->dinfo->inf_file);
+ torture_assert_not_null_goto(tctx, abs_inf_path, ok, done, "Cannot allocate memory");
+
+ r.in.pszEnvironment = parsed_dinfo->architecture;
+ torture_assert_not_null_goto(tctx, r.in.pszEnvironment, ok, done, "Cannot allocate memory");
+
+ /* Windows validates the print driver name by checking the pszDriverName input against the inf file:
+ * 1) "DriverName" value
+ * 2) "CompatName" value
+ * 3) left-hand-side value under the [Model] section
+ * otherwise ERROR_UNKNOWN_PRINTER_DRIVER is returned */
+ r.in.pszDriverName = parsed_dinfo->driver_name;
+ torture_assert_not_null_goto(tctx, r.in.pszDriverName, ok, done, "Cannot allocate memory");
+
+ /* All files should be installed, even if doing so would overwrite some newer
+ * versions */
+ r.in.dwFlags = IPDFP_COPY_ALL_FILES;
+
+ status = dcerpc_winspool_AsyncInstallPrinterDriverFromPackage_r(b, tctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "AsyncInstallPrinterDriverFromPackage failed");
+
+ torture_assert_hresult_ok(tctx, r.out.result, "AsyncInstallPrinterDriverFromPackage failed");
+done:
+ TALLOC_FREE(abs_inf_path);
+
+ return ok;
+}
+
+/* Check the registry to validate the print driver installed successfully */
+static bool test_ValidatePrinterDriverInstalled(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_iremotewinspool_context *ctx =
+ talloc_get_type_abort(private_data, struct test_iremotewinspool_context);
+
+ struct dcerpc_pipe *winreg_pipe = NULL;
+ NTSTATUS status;
+ bool ok = true;
+
+ /* winreg is not available over ncacn_ip_tcp */
+ status = torture_rpc_connection_transport(tctx, &winreg_pipe, &ndr_table_winreg, NCACN_NP, 0, 0);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_NOT_AVAILABLE)) {
+ /* retry */
+ status = torture_rpc_connection_transport(tctx, &winreg_pipe, &ndr_table_winreg, NCACN_NP, 0, 0);
+ }
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done, "Failed to connect to winreg");
+
+ ok = test_winreg_validate_driver(tctx, winreg_pipe, ctx->dinfo);
+ torture_assert_goto(tctx, ok, ok, done, "Failed to validate driver with winreg");
+
+done:
+ TALLOC_FREE(winreg_pipe);
+
+ return ok;
+}
+
+struct torture_suite *torture_rpc_iremotewinspool_drv(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "iremotewinspool_driver");
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "drivers");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_iremotewinspool_drv_setup,
+ torture_rpc_iremotewinspool_drv_teardown);
+
+ torture_tcase_add_simple_test(tcase, "CopyDriverFiles", test_CopyDriverFiles);
+ torture_tcase_add_simple_test(tcase, "UploadPrinterDriverPackage", test_UploadPrinterDriverPackage);
+ torture_tcase_add_simple_test(tcase, "InstallPrinterDriverFromPackage", test_InstallPrinterDriverFromPackage);
+ torture_tcase_add_simple_test(tcase, "ValidatePrinterDriverInstalled", test_ValidatePrinterDriverInstalled);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/join.c b/source4/torture/rpc/join.c
new file mode 100644
index 0000000..6e0afca
--- /dev/null
+++ b/source4/torture/rpc/join.c
@@ -0,0 +1,86 @@
+#include "includes.h"
+#include "libcli/libcli.h"
+
+#include "torture/rpc/torture_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;
+ struct smbcli_session_options session_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;
+ }
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+ lpcfg_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(tj, &cli, host,
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lpcfg_socket_options(torture->lp_ctx),
+ machine_account,
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ 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,
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lpcfg_socket_options(torture->lp_ctx),
+ machine_account,
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ 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 0000000..d225468
--- /dev/null
+++ b/source4/torture/rpc/lsa.c
@@ -0,0 +1,5466 @@
+/*
+ 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 "libcli/cldap/cldap.h"
+#include "../lib/tsocket/tsocket.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "lib/events/events.h"
+#include "libcli/security/security.h"
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+#include "source4/auth/kerberos/kerberos.h"
+#include "source4/auth/kerberos/kerberos_util.h"
+#include "lib/util/util_net.h"
+#include "libcli/resolve/resolve.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#define TEST_MACHINENAME "lsatestmach"
+#define TRUSTPW "12345678"
+
+static void init_lsa_String(struct lsa_String *name, const char *s)
+{
+ name->string = s;
+}
+
+static bool test_OpenPolicy(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx)
+{
+ struct lsa_ObjectAttribute attr;
+ struct policy_handle handle;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy r;
+ uint16_t system_name = '\\';
+
+ torture_comment(tctx, "\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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenPolicy_r(b, tctx, &r),
+ "OpenPolicy failed");
+
+ torture_assert_ntstatus_ok(tctx,
+ r.out.result,
+ "OpenPolicy failed");
+
+ return true;
+}
+
+static bool test_OpenPolicy_fail(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx)
+{
+ struct lsa_ObjectAttribute attr;
+ struct policy_handle handle;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy r;
+ uint16_t system_name = '\\';
+ NTSTATUS status;
+
+ torture_comment(tctx, "\nTesting OpenPolicy_fail\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_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx,
+ "OpenPolicy correctly returned with "
+ "status: %s\n",
+ nt_errstr(status));
+ return true;
+ }
+
+ torture_assert_ntstatus_equal(tctx,
+ status,
+ NT_STATUS_ACCESS_DENIED,
+ "OpenPolicy return value should "
+ "be ACCESS_DENIED");
+ return true;
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ torture_comment(tctx,
+ "OpenPolicy correctly returned with "
+ "result: %s\n",
+ nt_errstr(r.out.result));
+ return true;
+ }
+ }
+
+ torture_assert_ntstatus_equal(tctx,
+ r.out.result,
+ NT_STATUS_OK,
+ "OpenPolicy return value should be "
+ "ACCESS_DENIED");
+
+ return false;
+}
+
+
+bool test_lsa_OpenPolicy2_ex(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle **handle,
+ NTSTATUS expected_status,
+ NTSTATUS expected_status2)
+{
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+ NTSTATUS status;
+
+ torture_comment(tctx, "\nTesting OpenPolicy2\n");
+
+ *handle = talloc(tctx, struct policy_handle);
+ torture_assert(tctx, *handle != NULL, "talloc(tctx, struct policy_handle)");
+
+ 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_r(b, tctx, &r);
+
+ /* Allow two possible failure status codes */
+ if (!NT_STATUS_EQUAL(status, expected_status2)) {
+ torture_assert_ntstatus_equal(tctx, status,
+ expected_status,
+ "OpenPolicy2 failed");
+ }
+ if (!NT_STATUS_IS_OK(expected_status) ||
+ !NT_STATUS_IS_OK(expected_status2)) {
+ return true;
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ r.out.result,
+ "OpenPolicy2 failed");
+
+ return true;
+}
+
+
+bool test_lsa_OpenPolicy2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle **handle)
+{
+ return test_lsa_OpenPolicy2_ex(b, tctx, handle,
+ NT_STATUS_OK, NT_STATUS_OK);
+}
+
+static bool test_OpenPolicy2_fail(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx)
+{
+ struct lsa_ObjectAttribute attr;
+ struct policy_handle handle;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+ NTSTATUS status;
+
+ torture_comment(tctx, "\nTesting OpenPolicy2_fail\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 = "\\";
+ r.in.attr = &attr;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ status = dcerpc_lsa_OpenPolicy2_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx,
+ "OpenPolicy2 correctly returned with "
+ "status: %s\n",
+ nt_errstr(status));
+ return true;
+ }
+
+ torture_assert_ntstatus_equal(tctx,
+ status,
+ NT_STATUS_ACCESS_DENIED,
+ "OpenPolicy2 return value should "
+ "be ACCESS_DENIED");
+ return true;
+ }
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ torture_comment(tctx,
+ "OpenPolicy2 correctly returned with "
+ "result: %s\n",
+ nt_errstr(r.out.result));
+ return true;
+ }
+
+ torture_fail(tctx,
+ "OpenPolicy2 return value should be "
+ "ACCESS_DENIED or RPC_PROTSEQ_NOT_SUPPORTED");
+
+ return false;
+}
+
+static bool test_LookupNames(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level,
+ struct lsa_TransNameArray *tnames)
+{
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_String *names;
+ uint32_t count = 0;
+ int i;
+ uint32_t *input_idx;
+
+ torture_comment(tctx, "\nTesting LookupNames with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+
+ r.in.num_names = 0;
+
+ input_idx = talloc_array(tctx, uint32_t, tnames->count);
+ names = talloc_array(tctx, struct lsa_String, tnames->count);
+
+ for (i=0;i<tnames->count;i++) {
+ if (tnames->names[i].sid_type != SID_NAME_UNKNOWN) {
+ init_lsa_String(&names[r.in.num_names], tnames->names[i].name.string);
+ input_idx[r.in.num_names] = i;
+ r.in.num_names++;
+ }
+ }
+
+ r.in.handle = handle;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = level;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames_r(b, tctx, &r),
+ "LookupNames failed");
+ if (NT_STATUS_EQUAL(r.out.result, STATUS_SOME_UNMAPPED) ||
+ NT_STATUS_EQUAL(r.out.result, NT_STATUS_NONE_MAPPED)) {
+ for (i=0;i< r.in.num_names;i++) {
+ if (i < count && sids.sids[i].sid_type == SID_NAME_UNKNOWN) {
+ torture_comment(tctx, "LookupName of %s was unmapped\n",
+ tnames->names[i].name.string);
+ } else if (i >=count) {
+ torture_comment(tctx, "LookupName of %s failed to return a result\n",
+ tnames->names[i].name.string);
+ }
+ }
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LookupNames failed");
+ } else if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LookupNames failed");
+ }
+
+ for (i=0;i< r.in.num_names;i++) {
+ torture_assert(tctx, (i < count),
+ talloc_asprintf(tctx,
+ "LookupName of %s failed to return a result\n",
+ tnames->names[input_idx[i]].name.string));
+
+ torture_assert_int_equal(tctx,
+ sids.sids[i].sid_type,
+ tnames->names[input_idx[i]].sid_type,
+ talloc_asprintf(tctx,
+ "LookupName of %s got unexpected name type: %s\n",
+ tnames->names[input_idx[i]].name.string,
+ sid_type_lookup(sids.sids[i].sid_type)));
+ if (sids.sids[i].sid_type != SID_NAME_DOMAIN) {
+ continue;
+ }
+ torture_assert_int_equal(tctx,
+ sids.sids[i].rid,
+ UINT32_MAX,
+ talloc_asprintf(tctx,
+ "LookupName of %s got unexpected rid: %d\n",
+ tnames->names[input_idx[i]].name.string,
+ sids.sids[i].rid));
+ }
+
+ return true;
+}
+
+static bool test_LookupNames_bogus(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level)
+{
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_String names[1];
+ uint32_t count = 0;
+
+ torture_comment(tctx, "\nTesting LookupNames with bogus name\n");
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ init_lsa_String(&names[0], "NT AUTHORITY\\BOGUS");
+
+ r.in.handle = handle;
+ r.in.num_names = 1;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = level;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames_r(b, tctx, &r),
+ "LookupNames bogus failed");
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_NONE_MAPPED)) {
+ torture_comment(tctx, "LookupNames failed - %s\n",
+ nt_errstr(r.out.result));
+ return false;
+ }
+
+ torture_comment(tctx, "\n");
+
+ return true;
+}
+
+static bool test_LookupNames_NULL(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level)
+{
+ struct lsa_LookupNames r;
+ struct lsa_TransSidArray sids;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_String names[1];
+ uint32_t count = 0;
+
+ torture_comment(tctx, "\nTesting LookupNames with NULL name\n");
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ names[0].string = NULL;
+
+ r.in.handle = handle;
+ r.in.num_names = 1;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = level;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ /* nt4 returns NT_STATUS_NONE_MAPPED with sid_type
+ * SID_NAME_UNKNOWN, rid 0, and sid_index -1;
+ *
+ * w2k3/w2k8 return NT_STATUS_OK with sid_type
+ * SID_NAME_DOMAIN, rid -1 and sid_index 0 and BUILTIN domain
+ */
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames_r(b, tctx, &r),
+ "LookupNames with NULL name failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LookupNames with NULL name failed");
+
+ torture_comment(tctx, "\n");
+
+ return true;
+}
+
+static bool test_LookupNames_wellknown(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level)
+{
+ struct lsa_TranslatedName name;
+ struct lsa_TransNameArray tnames;
+ bool ret = true;
+
+ torture_comment(tctx, "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(b, tctx, handle, level, &tnames);
+
+ name.name.string = "NT AUTHORITY\\ANONYMOUS LOGON";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(b, tctx, handle, level, &tnames);
+
+ name.name.string = "NT AUTHORITY\\Authenticated Users";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(b, tctx, handle, level, &tnames);
+
+#if 0
+ name.name.string = "NT AUTHORITY";
+ ret &= test_LookupNames(b, tctx, handle, level, &tnames);
+
+ name.name.string = "NT AUTHORITY\\";
+ ret &= test_LookupNames(b, tctx, handle, level, &tnames);
+#endif
+
+ name.name.string = "BUILTIN\\";
+ name.sid_type = SID_NAME_DOMAIN;
+ ret &= test_LookupNames(b, tctx, handle, level, &tnames);
+
+ name.name.string = "BUILTIN\\Administrators";
+ name.sid_type = SID_NAME_ALIAS;
+ ret &= test_LookupNames(b, tctx, handle, level, &tnames);
+
+ name.name.string = "SYSTEM";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(b, tctx, handle, level, &tnames);
+
+ name.name.string = "Everyone";
+ name.sid_type = SID_NAME_WKN_GRP;
+ ret &= test_LookupNames(b, tctx, handle, level, &tnames);
+ return ret;
+}
+
+static bool test_LookupNames2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level,
+ struct lsa_TransNameArray2 *tnames,
+ bool check_result)
+{
+ struct lsa_LookupNames2 r;
+ struct lsa_TransSidArray2 sids;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_String *names;
+ uint32_t *input_idx;
+ uint32_t count = 0;
+ int i;
+
+ torture_comment(tctx, "\nTesting LookupNames2 with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ r.in.num_names = 0;
+
+ input_idx = talloc_array(tctx, uint32_t, tnames->count);
+ names = talloc_array(tctx, struct lsa_String, tnames->count);
+
+ for (i=0;i<tnames->count;i++) {
+ if (tnames->names[i].sid_type != SID_NAME_UNKNOWN) {
+ init_lsa_String(&names[r.in.num_names], tnames->names[i].name.string);
+ input_idx[r.in.num_names] = i;
+ r.in.num_names++;
+ }
+ }
+
+ r.in.handle = handle;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = level;
+ r.in.count = &count;
+ r.in.lookup_options = 0;
+ r.in.client_revision = 0;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames2_r(b, tctx, &r),
+ "LookupNames2 failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LookupNames2 failed");
+
+ if (check_result) {
+ torture_assert_int_equal(tctx, count, sids.count,
+ "unexpected number of results returned");
+ if (sids.count > 0) {
+ torture_assert(tctx, sids.sids, "invalid sid buffer");
+ }
+ }
+
+ torture_comment(tctx, "\n");
+
+ return true;
+}
+
+
+static bool test_LookupNames3(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level,
+ struct lsa_TransNameArray2 *tnames,
+ bool check_result)
+{
+ struct lsa_LookupNames3 r;
+ struct lsa_TransSidArray3 sids;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_String *names;
+ uint32_t count = 0;
+ int i;
+ uint32_t *input_idx;
+
+ torture_comment(tctx, "\nTesting LookupNames3 with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ r.in.num_names = 0;
+
+ input_idx = talloc_array(tctx, uint32_t, tnames->count);
+ names = talloc_array(tctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ if (tnames->names[i].sid_type != SID_NAME_UNKNOWN) {
+ init_lsa_String(&names[r.in.num_names], tnames->names[i].name.string);
+ input_idx[r.in.num_names] = i;
+ r.in.num_names++;
+ }
+ }
+
+ r.in.handle = handle;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = level;
+ r.in.count = &count;
+ r.in.lookup_options = 0;
+ r.in.client_revision = 0;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames3_r(b, tctx, &r),
+ "LookupNames3 failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LookupNames3 failed");
+
+ if (check_result) {
+ torture_assert_int_equal(tctx, count, sids.count,
+ "unexpected number of results returned");
+ if (sids.count > 0) {
+ torture_assert(tctx, sids.sids, "invalid sid buffer");
+ }
+ }
+
+ torture_comment(tctx, "\n");
+
+ return true;
+}
+
+static bool test_LookupNames4(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ enum lsa_LookupNamesLevel level,
+ struct lsa_TransNameArray2 *tnames,
+ bool check_result)
+{
+ struct lsa_LookupNames4 r;
+ struct lsa_TransSidArray3 sids;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_String *names;
+ uint32_t count = 0;
+ int i;
+ uint32_t *input_idx;
+
+ torture_comment(tctx, "\nTesting LookupNames4 with %d names\n", tnames->count);
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ r.in.num_names = 0;
+
+ input_idx = talloc_array(tctx, uint32_t, tnames->count);
+ names = talloc_array(tctx, struct lsa_String, tnames->count);
+ for (i=0;i<tnames->count;i++) {
+ if (tnames->names[i].sid_type != SID_NAME_UNKNOWN) {
+ init_lsa_String(&names[r.in.num_names], tnames->names[i].name.string);
+ input_idx[r.in.num_names] = i;
+ r.in.num_names++;
+ }
+ }
+
+ r.in.num_names = tnames->count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = level;
+ r.in.count = &count;
+ r.in.lookup_options = 0;
+ r.in.client_revision = 0;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupNames4_r(b, tctx, &r),
+ "LookupNames4 failed");
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NONE_MAPPED)) {
+ torture_comment(tctx,
+ "LookupNames4 failed: %s - not considered as an error",
+ nt_errstr(r.out.result));
+
+ return true;
+ }
+ }
+ torture_assert_ntstatus_ok(tctx,
+ r.out.result,
+ "LookupNames4 failed");
+
+ if (check_result) {
+ torture_assert_int_equal(tctx, count, sids.count,
+ "unexpected number of results returned");
+ if (sids.count > 0) {
+ torture_assert(tctx, sids.sids, "invalid sid buffer");
+ }
+ }
+
+ torture_comment(tctx, "\n");
+
+ return true;
+}
+
+static bool test_LookupNames4_fail(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ enum lsa_LookupNamesLevel level)
+{
+ struct lsa_LookupNames4 r;
+ struct lsa_TransSidArray3 sids;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_String *names = NULL;
+ uint32_t count = 0;
+ NTSTATUS status;
+
+ torture_comment(tctx, "\nTesting LookupNames4_fail");
+
+ sids.count = 0;
+ sids.sids = NULL;
+
+ r.in.num_names = 0;
+
+ r.in.num_names = count;
+ r.in.names = names;
+ r.in.sids = &sids;
+ r.in.level = level;
+ r.in.count = &count;
+ r.in.lookup_options = 0;
+ r.in.client_revision = 0;
+ r.out.count = &count;
+ r.out.sids = &sids;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupNames4_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx,
+ "LookupNames4 correctly returned with "
+ "status: %s\n",
+ nt_errstr(status));
+ return true;
+ }
+
+ torture_assert_ntstatus_equal(tctx,
+ status,
+ NT_STATUS_ACCESS_DENIED,
+ "LookupNames4 return value should "
+ "be ACCESS_DENIED");
+ return true;
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ torture_comment(tctx,
+ "LookupSids3 correctly returned with "
+ "result: %s\n",
+ nt_errstr(r.out.result));
+ return true;
+ }
+ }
+
+ torture_fail(tctx,
+ "LookupNames4 return value should be "
+ "ACCESS_DENIED or RPC_PROTSEQ_NOT_SUPPORTED");
+
+ return false;
+}
+
+
+static bool test_LookupSids(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level,
+ struct lsa_SidArray *sids)
+{
+ struct lsa_LookupSids r;
+ struct lsa_TransNameArray names;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = sids->num_sids;
+
+ torture_comment(tctx, "\nTesting LookupSids\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.handle = handle;
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = level;
+ r.in.count = &count;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupSids_r(b, tctx, &r),
+ "LookupSids failed");
+ if (!NT_STATUS_EQUAL(r.out.result, STATUS_SOME_UNMAPPED)) {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LookupSids failed");
+ }
+
+ torture_comment(tctx, "\n");
+
+ if (!test_LookupNames(b, tctx, handle, level, &names)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_LookupSids2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level,
+ struct lsa_SidArray *sids)
+{
+ struct lsa_LookupSids2 r;
+ struct lsa_TransNameArray2 names;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = sids->num_sids;
+
+ torture_comment(tctx, "\nTesting LookupSids2\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.handle = handle;
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = level;
+ r.in.count = &count;
+ r.in.lookup_options = 0;
+ r.in.client_revision = 0;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupSids2_r(b, tctx, &r),
+ "LookupSids2 failed");
+ if (!NT_STATUS_IS_OK(r.out.result) &&
+ !NT_STATUS_EQUAL(r.out.result, STATUS_SOME_UNMAPPED)) {
+ torture_comment(tctx, "LookupSids2 failed - %s\n",
+ nt_errstr(r.out.result));
+ return false;
+ }
+
+ torture_comment(tctx, "\n");
+
+ if (!test_LookupNames2(b, tctx, handle, level, &names, false)) {
+ return false;
+ }
+
+ if (!test_LookupNames3(b, tctx, handle, level, &names, false)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_LookupSids3(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ enum lsa_LookupNamesLevel level,
+ struct lsa_SidArray *sids)
+{
+ struct lsa_LookupSids3 r;
+ struct lsa_TransNameArray2 names;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = sids->num_sids;
+
+ torture_comment(tctx, "\nTesting LookupSids3\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = level;
+ r.in.count = &count;
+ r.in.lookup_options = 0;
+ r.in.client_revision = 0;
+ r.out.domains = &domains;
+ r.out.count = &count;
+ r.out.names = &names;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupSids3_r(b, tctx, &r),
+ "LookupSids3 failed");
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NONE_MAPPED)) {
+ torture_comment(tctx,
+ "LookupSids3 failed: %s - not considered as an error",
+ nt_errstr(r.out.result));
+
+ return true;
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ r.out.result,
+ "LookupSids3 failed");
+
+ return false;
+ }
+
+ torture_comment(tctx, "\n");
+
+ if (!test_LookupNames4(b, tctx, level, &names, true)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_LookupSids3_fail(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ enum lsa_LookupNamesLevel level,
+ struct lsa_SidArray *sids)
+{
+ struct lsa_LookupSids3 r;
+ struct lsa_TransNameArray2 names;
+ struct lsa_RefDomainList *domains = NULL;
+ uint32_t count = sids->num_sids;
+ NTSTATUS status;
+
+ torture_comment(tctx, "\nTesting LookupSids3\n");
+
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.sids = sids;
+ r.in.names = &names;
+ r.in.level = level;
+ r.in.count = &count;
+ r.in.lookup_options = 0;
+ r.in.client_revision = 0;
+ r.out.domains = &domains;
+ r.out.count = &count;
+ r.out.names = &names;
+
+ status = dcerpc_lsa_LookupSids3_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx,
+ "LookupSids3 correctly returned with "
+ "status: %s\n",
+ nt_errstr(status));
+ return true;
+ }
+
+ torture_assert_ntstatus_equal(tctx,
+ status,
+ NT_STATUS_ACCESS_DENIED,
+ "LookupSids3 return value should "
+ "be ACCESS_DENIED");
+ return true;
+ }
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ torture_comment(tctx,
+ "LookupNames4 correctly returned with "
+ "result: %s\n",
+ nt_errstr(r.out.result));
+ return true;
+ }
+
+ torture_fail(tctx,
+ "LookupSids3 return value should be "
+ "ACCESS_DENIED or RPC_PROTSEQ_NOT_SUPPORTED");
+
+ return false;
+}
+
+bool test_many_LookupSids(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level)
+{
+ uint32_t count;
+ struct lsa_SidArray sids;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding);
+
+ torture_comment(tctx, "\nTesting LookupSids with lots of SIDs\n");
+
+ sids.num_sids = 100;
+
+ sids.sids = talloc_array(tctx, 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(tctx, sidstr);
+ }
+
+ count = sids.num_sids;
+
+ if (handle) {
+ struct lsa_LookupSids r;
+ struct lsa_TransNameArray names;
+ struct lsa_RefDomainList *domains = NULL;
+ names.count = 0;
+ names.names = NULL;
+
+ r.in.handle = handle;
+ r.in.sids = &sids;
+ r.in.names = &names;
+ r.in.level = level;
+ r.in.count = &names.count;
+ r.out.count = &count;
+ r.out.names = &names;
+ r.out.domains = &domains;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupSids_r(b, tctx, &r),
+ "LookupSids failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "LookupSids failed - %s\n",
+ nt_errstr(r.out.result));
+ return false;
+ }
+
+ torture_comment(tctx, "\n");
+
+ if (!test_LookupNames(b, tctx, handle, level, &names)) {
+ return false;
+ }
+ }
+
+ if (transport == NCACN_NP) {
+ if (!test_LookupSids3_fail(b, tctx, level, &sids)) {
+ return false;
+ }
+ if (!test_LookupNames4_fail(b, tctx, level)) {
+ return false;
+ }
+ } else if (transport == NCACN_IP_TCP) {
+ struct lsa_TransNameArray2 names;
+ enum dcerpc_AuthType auth_type;
+ enum dcerpc_AuthLevel auth_level;
+
+ names.count = 0;
+ names.names = NULL;
+
+ dcerpc_binding_handle_auth_info(p->binding_handle,
+ &auth_type, &auth_level);
+
+ if (auth_type == DCERPC_AUTH_TYPE_SCHANNEL &&
+ auth_level >= DCERPC_AUTH_LEVEL_INTEGRITY) {
+ if (!test_LookupSids3(b, tctx, level, &sids)) {
+ return false;
+ }
+ if (!test_LookupNames4(b, tctx, level, &names, true)) {
+ return false;
+ }
+ } else {
+ /*
+ * If we don't have a secure channel these tests must
+ * fail with ACCESS_DENIED.
+ */
+ if (!test_LookupSids3_fail(b, tctx, level, &sids)) {
+ return false;
+ }
+ if (!test_LookupNames4_fail(b, tctx, level)) {
+ return false;
+ }
+ }
+ }
+
+ torture_comment(tctx, "\n");
+
+
+
+ return true;
+}
+
+static void lookupsids_cb(struct tevent_req *subreq)
+{
+ int *replies = (int *)tevent_req_callback_data_void(subreq);
+ NTSTATUS status;
+
+ status = dcerpc_lsa_LookupSids_r_recv(subreq, subreq);
+ TALLOC_FREE(subreq);
+ 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_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ enum lsa_LookupNamesLevel level)
+{
+ struct lsa_SidArray sids;
+ struct lsa_SidPtr sidptr;
+ uint32_t *count;
+ struct lsa_TransNameArray *names;
+ struct lsa_LookupSids *r;
+ struct lsa_RefDomainList *domains = NULL;
+ struct tevent_req **req;
+ int i, replies;
+ bool ret = true;
+ const int num_async_requests = 50;
+
+ count = talloc_array(tctx, uint32_t, num_async_requests);
+ names = talloc_array(tctx, struct lsa_TransNameArray, num_async_requests);
+ r = talloc_array(tctx, struct lsa_LookupSids, num_async_requests);
+
+ torture_comment(tctx, "\nTesting %d async lookupsids request\n", num_async_requests);
+
+ req = talloc_array(tctx, struct tevent_req *, num_async_requests);
+
+ sids.num_sids = 1;
+ sids.sids = &sidptr;
+ sidptr.sid = dom_sid_parse_talloc(tctx, "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 = level;
+ r[i].in.count = &names[i].count;
+ r[i].out.count = &count[i];
+ r[i].out.names = &names[i];
+ r[i].out.domains = &domains;
+
+ req[i] = dcerpc_lsa_LookupSids_r_send(tctx, tctx->ev, b, &r[i]);
+ if (req[i] == NULL) {
+ ret = false;
+ break;
+ }
+
+ tevent_req_set_callback(req[i], lookupsids_cb, &replies);
+ }
+
+ while (replies >= 0 && replies < num_async_requests) {
+ tevent_loop_once(tctx->ev);
+ }
+
+ talloc_free(req);
+
+ if (replies < 0) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_LookupPrivValue(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_String *name)
+{
+ struct lsa_LookupPrivValue r;
+ struct lsa_LUID luid;
+
+ r.in.handle = handle;
+ r.in.name = name;
+ r.out.luid = &luid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupPrivValue_r(b, tctx, &r),
+ "LookupPrivValue failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LookupPrivValue failed");
+
+ return true;
+}
+
+static bool test_LookupPrivName(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_LUID *luid)
+{
+ struct lsa_LookupPrivName r;
+ struct lsa_StringLarge *name = NULL;
+
+ r.in.handle = handle;
+ r.in.luid = luid;
+ r.out.name = &name;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupPrivName_r(b, tctx, &r),
+ "LookupPrivName failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LookupPrivName failed");
+
+ return true;
+}
+
+static bool test_RemovePrivilegesFromAccount(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle,
+ struct lsa_LUID *luid)
+{
+ struct lsa_RemovePrivilegesFromAccount r;
+ struct lsa_PrivilegeSet privs;
+ bool ret = true;
+
+ torture_comment(tctx, "\nTesting 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(tctx, struct lsa_LUIDAttribute, 1);
+ privs.set[0].luid = *luid;
+ privs.set[0].attribute = 0;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_RemovePrivilegesFromAccount_r(b, tctx, &r),
+ "RemovePrivilegesFromAccount failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+
+ struct lsa_LookupPrivName r_name;
+ struct lsa_StringLarge *name = NULL;
+
+ r_name.in.handle = handle;
+ r_name.in.luid = luid;
+ r_name.out.name = &name;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupPrivName_r(b, tctx, &r_name),
+ "LookupPrivName failed");
+ if (!NT_STATUS_IS_OK(r_name.out.result)) {
+ torture_comment(tctx, "\nLookupPrivName failed - %s\n",
+ nt_errstr(r_name.out.result));
+ return false;
+ }
+ /* Windows 2008 does not allow this to be removed */
+ if (strcmp("SeAuditPrivilege", name->string) == 0) {
+ return ret;
+ }
+
+ torture_comment(tctx, "RemovePrivilegesFromAccount failed to remove %s - %s\n",
+ name->string,
+ nt_errstr(r.out.result));
+ return false;
+ }
+
+ return ret;
+}
+
+static bool test_AddPrivilegesToAccount(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *acct_handle,
+ struct lsa_LUID *luid)
+{
+ struct lsa_AddPrivilegesToAccount r;
+ struct lsa_PrivilegeSet privs;
+ bool ret = true;
+
+ torture_comment(tctx, "\nTesting AddPrivilegesToAccount\n");
+
+ r.in.handle = acct_handle;
+ r.in.privs = &privs;
+
+ privs.count = 1;
+ privs.unknown = 0;
+ privs.set = talloc_array(tctx, struct lsa_LUIDAttribute, 1);
+ privs.set[0].luid = *luid;
+ privs.set[0].attribute = 0;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_AddPrivilegesToAccount_r(b, tctx, &r),
+ "AddPrivilegesToAccount failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "AddPrivilegesToAccount failed");
+ return ret;
+}
+
+static bool test_EnumPrivsAccount(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle)
+{
+ struct lsa_EnumPrivsAccount r;
+ struct lsa_PrivilegeSet *privs = NULL;
+ bool ret = true;
+
+ torture_comment(tctx, "\nTesting EnumPrivsAccount\n");
+
+ r.in.handle = acct_handle;
+ r.out.privs = &privs;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumPrivsAccount_r(b, tctx, &r),
+ "EnumPrivsAccount failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "EnumPrivsAccount failed");
+
+ if (privs && privs->count > 0) {
+ int i;
+ for (i=0;i<privs->count;i++) {
+ test_LookupPrivName(b, tctx, handle,
+ &privs->set[i].luid);
+ }
+
+ ret &= test_RemovePrivilegesFromAccount(b, tctx, handle, acct_handle,
+ &privs->set[0].luid);
+ ret &= test_AddPrivilegesToAccount(b, tctx, acct_handle,
+ &privs->set[0].luid);
+ }
+
+ return ret;
+}
+
+static bool test_GetSystemAccessAccount(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle)
+{
+ uint32_t access_mask;
+ struct lsa_GetSystemAccessAccount r;
+
+ torture_comment(tctx, "\nTesting GetSystemAccessAccount\n");
+
+ r.in.handle = acct_handle;
+ r.out.access_mask = &access_mask;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetSystemAccessAccount_r(b, tctx, &r),
+ "GetSystemAccessAccount failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "GetSystemAccessAccount failed");
+
+ if (r.out.access_mask != NULL) {
+ torture_comment(tctx, "Rights:");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_INTERACTIVE)
+ torture_comment(tctx, " LSA_POLICY_MODE_INTERACTIVE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_NETWORK)
+ torture_comment(tctx, " LSA_POLICY_MODE_NETWORK");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_BATCH)
+ torture_comment(tctx, " LSA_POLICY_MODE_BATCH");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_SERVICE)
+ torture_comment(tctx, " LSA_POLICY_MODE_SERVICE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_PROXY)
+ torture_comment(tctx, " LSA_POLICY_MODE_PROXY");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_INTERACTIVE)
+ torture_comment(tctx, " LSA_POLICY_MODE_DENY_INTERACTIVE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_NETWORK)
+ torture_comment(tctx, " LSA_POLICY_MODE_DENY_NETWORK");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_BATCH)
+ torture_comment(tctx, " LSA_POLICY_MODE_DENY_BATCH");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_SERVICE)
+ torture_comment(tctx, " LSA_POLICY_MODE_DENY_SERVICE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_REMOTE_INTERACTIVE)
+ torture_comment(tctx, " LSA_POLICY_MODE_REMOTE_INTERACTIVE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_DENY_REMOTE_INTERACTIVE)
+ torture_comment(tctx, " LSA_POLICY_MODE_DENY_REMOTE_INTERACTIVE");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_ALL)
+ torture_comment(tctx, " LSA_POLICY_MODE_ALL");
+ if (*(r.out.access_mask) & LSA_POLICY_MODE_ALL_NT4)
+ torture_comment(tctx, " LSA_POLICY_MODE_ALL_NT4");
+ torture_comment(tctx, "\n");
+ }
+
+ return true;
+}
+
+static bool test_Delete(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_Delete r;
+
+ torture_comment(tctx, "\nTesting Delete\n");
+
+ r.in.handle = handle;
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Delete_r(b, tctx, &r),
+ "Delete failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_SUPPORTED,
+ "Delete should have failed NT_STATUS_NOT_SUPPORTED");
+
+ return true;
+}
+
+static bool test_DeleteObject(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_DeleteObject r;
+
+ torture_comment(tctx, "\nTesting DeleteObject\n");
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_DeleteObject_r(b, tctx, &r),
+ "DeleteObject failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "DeleteObject failed");
+
+ return true;
+}
+
+
+static bool test_CreateAccount(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_CreateAccount r;
+ struct dom_sid2 *newsid;
+ struct policy_handle acct_handle;
+
+ newsid = dom_sid_parse_talloc(tctx, "S-1-5-12349876-4321-2854");
+
+ torture_comment(tctx, "\nTesting CreateAccount\n");
+
+ r.in.handle = handle;
+ r.in.sid = newsid;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.acct_handle = &acct_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateAccount_r(b, tctx, &r),
+ "CreateAccount failed");
+ if (NT_STATUS_EQUAL(r.out.result, 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(b, tctx, &r_o),
+ "OpenAccount failed");
+ torture_assert_ntstatus_ok(tctx, r_o.out.result,
+ "OpenAccount failed");
+ } else {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "CreateAccount failed");
+ }
+
+ if (!test_Delete(b, tctx, &acct_handle)) {
+ return false;
+ }
+
+ if (!test_DeleteObject(b, tctx, &acct_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DeleteTrustedDomain(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_StringLarge name)
+{
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenTrustedDomainByName_r(b, tctx, &r),
+ "OpenTrustedDomainByName failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "OpenTrustedDomainByName failed");
+
+ if (!test_Delete(b, tctx, &trustdom_handle)) {
+ return false;
+ }
+
+ if (!test_DeleteObject(b, tctx, &trustdom_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_DeleteTrustedDomainBySid(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct dom_sid *sid)
+{
+ struct lsa_DeleteTrustedDomain r;
+
+ r.in.handle = handle;
+ r.in.dom_sid = sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_DeleteTrustedDomain_r(b, tctx, &r),
+ "DeleteTrustedDomain failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "DeleteTrustedDomain failed");
+
+ return true;
+}
+
+
+static bool test_CreateSecret(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ 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;
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ secname[LOCAL] = talloc_asprintf(tctx, "torturesecret-%u", (unsigned int)random());
+ secname[GLOBAL] = talloc_asprintf(tctx, "G$torturesecret-%u", (unsigned int)random());
+
+ for (i=0; i< 2; i++) {
+ torture_comment(tctx, "\nTesting 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateSecret_r(b, tctx, &r),
+ "CreateSecret failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "CreateSecret failed");
+
+ r.in.handle = handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.sec_handle = &sec_handle3;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateSecret_r(b, tctx, &r),
+ "CreateSecret failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_COLLISION,
+ "CreateSecret should have failed OBJECT_NAME_COLLISION");
+
+ r2.in.handle = handle;
+ r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r2.in.name = r.in.name;
+ r2.out.sec_handle = &sec_handle2;
+
+ torture_comment(tctx, "Testing OpenSecret\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(b, tctx, &r2),
+ "OpenSecret failed");
+ torture_assert_ntstatus_ok(tctx, r2.out.result,
+ "OpenSecret failed");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_fetch_session_key(p, &session_key),
+ "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");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r3),
+ "SetSecret failed");
+ torture_assert_ntstatus_ok(tctx, r3.out.result,
+ "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");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r3),
+ "SetSecret failed");
+ torture_assert_ntstatus_equal(tctx, r3.out.result, 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");
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(b, tctx, &r4),
+ "QuerySecret failed");
+ if (!NT_STATUS_IS_OK(r4.out.result)) {
+ torture_comment(tctx, "QuerySecret failed - %s\n", nt_errstr(r4.out.result));
+ ret = false;
+ } else {
+ if (r4.out.new_val == NULL || r4.out.new_val->buf == NULL) {
+ torture_comment(tctx, "No secret buffer returned\n");
+ ret = false;
+ } else {
+ blob1.data = r4.out.new_val->buf->data;
+ blob1.length = r4.out.new_val->buf->size;
+
+ secret2 = sess_decrypt_string(tctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret1, secret2) != 0) {
+ torture_comment(tctx, "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;
+
+
+ smb_msleep(200);
+ torture_comment(tctx, "Testing SetSecret (existing value should move to old)\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r5),
+ "SetSecret failed");
+ if (!NT_STATUS_IS_OK(r5.out.result)) {
+ torture_comment(tctx, "SetSecret failed - %s\n", nt_errstr(r5.out.result));
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(b, tctx, &r6),
+ "QuerySecret failed");
+ if (!NT_STATUS_IS_OK(r6.out.result)) {
+ torture_comment(tctx, "QuerySecret failed - %s\n", nt_errstr(r6.out.result));
+ 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) {
+ torture_comment(tctx, "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;
+
+ secret4 = sess_decrypt_string(tctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret3, secret4) != 0) {
+ torture_comment(tctx, "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;
+
+ secret2 = sess_decrypt_string(tctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret1, secret2) != 0) {
+ torture_comment(tctx, "Returned OLD secret %s doesn't match %s\n", secret2, secret1);
+ ret = false;
+ }
+
+ if (*r6.out.new_mtime == *r6.out.old_mtime) {
+ torture_comment(tctx, "Returned secret (r6-%d) %s must not have same mtime for both secrets: %s != %s\n",
+ i,
+ secname[i],
+ nt_time_string(tctx, *r6.out.old_mtime),
+ nt_time_string(tctx, *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;
+
+ torture_comment(tctx, "Testing SetSecret of old Secret only\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r7),
+ "SetSecret failed");
+ if (!NT_STATUS_IS_OK(r7.out.result)) {
+ torture_comment(tctx, "SetSecret failed - %s\n", nt_errstr(r7.out.result));
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(b, tctx, &r8),
+ "QuerySecret failed");
+ if (!NT_STATUS_IS_OK(r8.out.result)) {
+ torture_comment(tctx, "QuerySecret failed - %s\n", nt_errstr(r8.out.result));
+ ret = false;
+ } else {
+ if (!r8.out.new_val || !r8.out.old_val) {
+ torture_comment(tctx, "in/out pointers not returned, despite being set on in for QuerySecret\n");
+ ret = false;
+ } else if (r8.out.new_val->buf != NULL) {
+ torture_comment(tctx, "NEW secret buffer must not be returned after OLD set\n");
+ ret = false;
+ } else if (r8.out.old_val->buf == NULL) {
+ torture_comment(tctx, "OLD secret buffer was not returned after OLD set\n");
+ ret = false;
+ } else if (r8.out.new_mtime == NULL || r8.out.old_mtime == NULL) {
+ torture_comment(tctx, "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;
+
+ secret6 = sess_decrypt_string(tctx,
+ &blob1, &session_key);
+
+ if (strcmp(secret5, secret6) != 0) {
+ torture_comment(tctx, "Returned OLD secret %s doesn't match %s\n", secret5, secret6);
+ ret = false;
+ }
+
+ if (*r8.out.new_mtime != *r8.out.old_mtime) {
+ torture_comment(tctx, "Returned secret (r8) %s did not had same mtime for both secrets: %s != %s\n",
+ secname[i],
+ nt_time_string(tctx, *r8.out.old_mtime),
+ nt_time_string(tctx, *r8.out.new_mtime));
+ ret = false;
+ }
+ }
+ }
+
+ if (!test_Delete(b, tctx, &sec_handle)) {
+ ret = false;
+ }
+
+ if (!test_DeleteObject(b, tctx, &sec_handle)) {
+ return false;
+ }
+
+ d_o.in.handle = &sec_handle2;
+ d_o.out.handle = &sec_handle2;
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_DeleteObject_r(b, tctx, &d_o),
+ "DeleteObject failed");
+ torture_assert_ntstatus_equal(tctx, d_o.out.result, NT_STATUS_INVALID_HANDLE,
+ "OpenSecret expected INVALID_HANDLE");
+
+ torture_comment(tctx, "Testing OpenSecret of just-deleted secret\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenSecret_r(b, tctx, &r2),
+ "OpenSecret failed");
+ torture_assert_ntstatus_equal(tctx, r2.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ "OpenSecret expected OBJECT_NAME_NOT_FOUND");
+ }
+ return ret;
+}
+
+
+static bool test_EnumAccountRights(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *acct_handle,
+ struct dom_sid *sid)
+{
+ struct lsa_EnumAccountRights r;
+ struct lsa_RightSet rights;
+
+ torture_comment(tctx, "\nTesting EnumAccountRights\n");
+
+ r.in.handle = acct_handle;
+ r.in.sid = sid;
+ r.out.rights = &rights;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(b, tctx, &r),
+ "EnumAccountRights failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "EnumAccountRights of %s failed - %s\n",
+ dom_sid_string(tctx, sid), nt_errstr(r.out.result));
+ }
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "EnumAccountRights failed");
+
+ return true;
+}
+
+
+static bool test_QuerySecurity(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct policy_handle *acct_handle)
+{
+ struct lsa_QuerySecurity r;
+ struct sec_desc_buf *sdbuf = NULL;
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "\nskipping QuerySecurity test against Samba4\n");
+ return true;
+ }
+
+ torture_comment(tctx, "\nTesting QuerySecurity\n");
+
+ r.in.handle = acct_handle;
+ r.in.sec_info = SECINFO_OWNER |
+ SECINFO_GROUP |
+ SECINFO_DACL;
+ r.out.sdbuf = &sdbuf;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecurity_r(b, tctx, &r),
+ "QuerySecurity failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "QuerySecurity failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_OpenAccount(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct dom_sid *sid)
+{
+ struct lsa_OpenAccount r;
+ struct policy_handle acct_handle;
+
+ torture_comment(tctx, "\nTesting OpenAccount\n");
+
+ r.in.handle = handle;
+ r.in.sid = sid;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.acct_handle = &acct_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(b, tctx, &r),
+ "OpenAccount failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "OpenAccount failed");
+
+ if (!test_EnumPrivsAccount(b, tctx, handle, &acct_handle)) {
+ return false;
+ }
+
+ if (!test_GetSystemAccessAccount(b, tctx, handle, &acct_handle)) {
+ return false;
+ }
+
+ if (!test_QuerySecurity(b, tctx, handle, &acct_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_EnumAccounts(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_EnumAccounts r;
+ struct lsa_SidArray sids1, sids2;
+ uint32_t resume_handle = 0;
+ int i;
+ bool ret = true;
+
+ torture_comment(tctx, "\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) {
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(b, tctx, &r),
+ "EnumAccounts failed");
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES)) {
+ break;
+ }
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "EnumAccounts failed");
+
+ if (!test_LookupSids(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &sids1)) {
+ return false;
+ }
+
+ if (!test_LookupSids2(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &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 */
+
+ torture_comment(tctx, "Testing all accounts\n");
+ for (i=0;i<sids1.num_sids;i++) {
+ ret &= test_OpenAccount(b, tctx, handle, sids1.sids[i].sid);
+ ret &= test_EnumAccountRights(b, tctx, handle, sids1.sids[i].sid);
+ }
+ torture_comment(tctx, "\n");
+ }
+
+ if (sids1.num_sids < 3) {
+ return ret;
+ }
+
+ torture_comment(tctx, "Trying EnumAccounts partial listing (asking for 1 at 2)\n");
+ resume_handle = 2;
+ r.in.num_entries = 1;
+ r.out.sids = &sids2;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(b, tctx, &r),
+ "EnumAccounts failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "EnumAccounts failed");
+
+ if (sids2.num_sids != 1) {
+ torture_comment(tctx, "Returned wrong number of entries (%d)\n", sids2.num_sids);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_LookupPrivDisplayName(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_String *priv_name)
+{
+ struct lsa_LookupPrivDisplayName r;
+ /* produce a reasonable range of language output without screwing up
+ terminals */
+ uint16_t language_id = (random() % 4) + 0x409;
+ uint16_t returned_language_id = 0;
+ struct lsa_StringLarge *disp_name = NULL;
+
+ torture_comment(tctx, "\nTesting LookupPrivDisplayName(%s)\n", priv_name->string);
+
+ r.in.handle = handle;
+ r.in.name = priv_name;
+ r.in.language_id = language_id;
+ r.in.language_id_sys = 0;
+ r.out.returned_language_id = &returned_language_id;
+ r.out.disp_name = &disp_name;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupPrivDisplayName_r(b, tctx, &r),
+ "LookupPrivDisplayName failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "LookupPrivDisplayName failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+ torture_comment(tctx, "%s -> \"%s\" (language 0x%x/0x%x)\n",
+ priv_name->string, disp_name->string,
+ r.in.language_id, *r.out.returned_language_id);
+
+ return true;
+}
+
+static bool test_EnumAccountsWithUserRight(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_String *priv_name)
+{
+ struct lsa_EnumAccountsWithUserRight r;
+ struct lsa_SidArray sids;
+
+ ZERO_STRUCT(sids);
+
+ torture_comment(tctx, "\nTesting EnumAccountsWithUserRight(%s)\n", priv_name->string);
+
+ r.in.handle = handle;
+ r.in.name = priv_name;
+ r.out.sids = &sids;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountsWithUserRight_r(b, tctx, &r),
+ "EnumAccountsWithUserRight failed");
+
+ /* NT_STATUS_NO_MORE_ENTRIES means no one has this privilege */
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES)) {
+ return true;
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "EnumAccountsWithUserRight failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool test_EnumPrivs(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_EnumPrivs r;
+ struct lsa_PrivArray privs1;
+ uint32_t resume_handle = 0;
+ int i;
+ bool ret = true;
+
+ torture_comment(tctx, "\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;
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumPrivs_r(b, tctx, &r),
+ "EnumPrivs failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "EnumPrivs failed");
+
+ for (i = 0; i< privs1.count; i++) {
+ test_LookupPrivDisplayName(b, tctx, handle, (struct lsa_String *)&privs1.privs[i].name);
+ test_LookupPrivValue(b, tctx, handle, (struct lsa_String *)&privs1.privs[i].name);
+ if (!test_EnumAccountsWithUserRight(b, tctx, handle, (struct lsa_String *)&privs1.privs[i].name)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryForestTrustInformation(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *trusted_domain_name)
+{
+ bool ret = true;
+ struct lsa_lsaRQueryForestTrustInformation r;
+ struct lsa_String string;
+ struct lsa_ForestTrustInformation info, *info_ptr;
+
+ torture_comment(tctx, "\nTesting lsaRQueryForestTrustInformation\n");
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "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.highest_record_type = LSA_FOREST_TRUST_TOP_LEVEL_NAME;
+ r.out.forest_trust_info = &info_ptr;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_lsaRQueryForestTrustInformation_r(b, tctx, &r),
+ "lsaRQueryForestTrustInformation failed");
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "lsaRQueryForestTrustInformation of %s failed - %s\n", trusted_domain_name, nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_query_each_TrustDomEx(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ 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 & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ ret &= test_QueryForestTrustInformation(b, tctx, handle,
+ domains->domains[i].domain_name.string);
+ }
+ }
+
+ return ret;
+}
+
+static bool test_query_each_TrustDom(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ struct lsa_DomainList *domains)
+{
+ int i,j;
+ bool ret = true;
+
+ torture_comment(tctx, "\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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenTrustedDomain_r(b, tctx, &trust),
+ "OpenTrustedDomain failed");
+
+ if (NT_STATUS_EQUAL(trust.out.result, NT_STATUS_NO_SUCH_DOMAIN)) {
+ torture_comment(tctx, "DOMAIN(%s, %s) not a direct trust?\n",
+ domains->domains[i].name.string,
+ dom_sid_string(tctx, domains->domains[i].sid));
+ continue;
+ }
+ if (!NT_STATUS_IS_OK(trust.out.result)) {
+ torture_comment(tctx, "OpenTrustedDomain failed - %s\n", nt_errstr(trust.out.result));
+ 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 = NULL;
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = levels[j];
+ q.out.info = &info;
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfo_r(b, tctx, &q),
+ "QueryTrustedDomainInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result) && ok[j]) {
+ torture_comment(tctx, "QueryTrustedDomainInfo level %d failed - %s\n",
+ levels[j], nt_errstr(q.out.result));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(q.out.result) && !ok[j]) {
+ torture_comment(tctx, "QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(q.out.result));
+ ret = false;
+ }
+ }
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CloseTrustedDomainEx_r(b, tctx, &c_trust),
+ "CloseTrustedDomainEx failed");
+ if (!NT_STATUS_EQUAL(c_trust.out.result, NT_STATUS_NOT_IMPLEMENTED)) {
+ torture_comment(tctx, "Expected CloseTrustedDomainEx to return NT_STATUS_NOT_IMPLEMENTED, instead - %s\n", nt_errstr(c_trust.out.result));
+ return false;
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &handle2;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Close_r(b, tctx, &c),
+ "Close failed");
+ if (!NT_STATUS_IS_OK(c.out.result)) {
+ torture_comment(tctx, "Close of trusted domain failed - %s\n", nt_errstr(c.out.result));
+ return false;
+ }
+
+ for (j=0; j < ARRAY_SIZE(levels); j++) {
+ struct lsa_QueryTrustedDomainInfoBySid q;
+ union lsa_TrustedDomainInfo *info = NULL;
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfoBySid_r(b, tctx, &q),
+ "lsa_QueryTrustedDomainInfoBySid failed");
+ if (!NT_STATUS_IS_OK(q.out.result) && ok[j]) {
+ torture_comment(tctx, "QueryTrustedDomainInfoBySid level %d failed - %s\n",
+ levels[j], nt_errstr(q.out.result));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(q.out.result) && !ok[j]) {
+ torture_comment(tctx, "QueryTrustedDomainInfoBySid level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(q.out.result));
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenTrustedDomainByName_r(b, tctx, &trust_by_name),
+ "OpenTrustedDomainByName failed");
+
+ if (NT_STATUS_EQUAL(trust_by_name.out.result, NT_STATUS_NO_SUCH_DOMAIN)) {
+ torture_comment(tctx, "DOMAIN(%s, %s) not a direct trust?\n",
+ domains->domains[i].name.string,
+ dom_sid_string(tctx, domains->domains[i].sid));
+ continue;
+ }
+ if (!NT_STATUS_IS_OK(trust_by_name.out.result)) {
+ torture_comment(tctx, "OpenTrustedDomainByName failed - %s\n", nt_errstr(trust_by_name.out.result));
+ return false;
+ }
+
+ for (j=0; j < ARRAY_SIZE(levels); j++) {
+ struct lsa_QueryTrustedDomainInfo q;
+ union lsa_TrustedDomainInfo *info = NULL;
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = levels[j];
+ q.out.info = &info;
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfo_r(b, tctx, &q),
+ "QueryTrustedDomainInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result) && ok[j]) {
+ torture_comment(tctx, "QueryTrustedDomainInfo level %d failed - %s\n",
+ levels[j], nt_errstr(q.out.result));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(q.out.result) && !ok[j]) {
+ torture_comment(tctx, "QueryTrustedDomainInfo level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(q.out.result));
+ ret = false;
+ }
+ }
+
+ c.in.handle = &trustdom_handle;
+ c.out.handle = &handle2;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Close_r(b, tctx, &c),
+ "Close failed");
+ if (!NT_STATUS_IS_OK(c.out.result)) {
+ torture_comment(tctx, "Close of trusted domain failed - %s\n", nt_errstr(c.out.result));
+ return false;
+ }
+
+ for (j=0; j < ARRAY_SIZE(levels); j++) {
+ struct lsa_QueryTrustedDomainInfoByName q;
+ union lsa_TrustedDomainInfo *info = NULL;
+ struct lsa_String name;
+
+ name.string = domains->domains[i].name.string;
+
+ q.in.handle = handle;
+ q.in.trusted_domain = &name;
+ q.in.level = levels[j];
+ q.out.info = &info;
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfoByName_r(b, tctx, &q),
+ "QueryTrustedDomainInfoByName failed");
+ if (!NT_STATUS_IS_OK(q.out.result) && ok[j]) {
+ torture_comment(tctx, "QueryTrustedDomainInfoByName level %d failed - %s\n",
+ levels[j], nt_errstr(q.out.result));
+ ret = false;
+ } else if (NT_STATUS_IS_OK(q.out.result) && !ok[j]) {
+ torture_comment(tctx, "QueryTrustedDomainInfoByName level %d unexpectedly succeeded - %s\n",
+ levels[j], nt_errstr(q.out.result));
+ ret = false;
+ }
+ }
+ }
+ return ret;
+}
+
+static bool test_EnumTrustDom(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_EnumTrustDom r;
+ uint32_t in_resume_handle = 0;
+ uint32_t out_resume_handle;
+ struct lsa_DomainList domains;
+ bool ret = true;
+
+ torture_comment(tctx, "\nTesting EnumTrustDom\n");
+
+ r.in.handle = handle;
+ r.in.resume_handle = &in_resume_handle;
+ r.in.max_size = 0;
+ r.out.domains = &domains;
+ r.out.resume_handle = &out_resume_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustDom_r(b, tctx, &r),
+ "lsa_EnumTrustDom failed");
+
+ /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST
+ * always be larger than the previous input resume handle, in
+ * particular when hitting the last query it is vital to set the
+ * resume handle correctly to avoid infinite client loops, as
+ * seen e.g. with Windows XP SP3 when resume handle is 0 and
+ * status is NT_STATUS_OK - gd */
+
+ if (NT_STATUS_IS_OK(r.out.result) ||
+ NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES))
+ {
+ if (out_resume_handle <= in_resume_handle) {
+ torture_comment(tctx, "EnumTrustDom failed - should have returned output resume_handle (0x%08x) larger than input resume handle (0x%08x)\n",
+ out_resume_handle, in_resume_handle);
+ return false;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(r.out.result)) {
+ if (domains.count == 0) {
+ torture_comment(tctx, "EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+ } else if (!(NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES))) {
+ torture_comment(tctx, "EnumTrustDom of zero size failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+
+ /* Start from the bottom again */
+ in_resume_handle = 0;
+
+ do {
+ r.in.handle = handle;
+ r.in.resume_handle = &in_resume_handle;
+ r.in.max_size = LSA_ENUM_TRUST_DOMAIN_MULTIPLIER * 3;
+ r.out.domains = &domains;
+ r.out.resume_handle = &out_resume_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustDom_r(b, tctx, &r),
+ "EnumTrustDom failed");
+
+ /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST
+ * always be larger than the previous input resume handle, in
+ * particular when hitting the last query it is vital to set the
+ * resume handle correctly to avoid infinite client loops, as
+ * seen e.g. with Windows XP SP3 when resume handle is 0 and
+ * status is NT_STATUS_OK - gd */
+
+ if (NT_STATUS_IS_OK(r.out.result) ||
+ NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES))
+ {
+ if (out_resume_handle <= in_resume_handle) {
+ torture_comment(tctx, "EnumTrustDom failed - should have returned output resume_handle (0x%08x) larger than input resume handle (0x%08x)\n",
+ out_resume_handle, in_resume_handle);
+ return false;
+ }
+ }
+
+ /* NO_MORE_ENTRIES is allowed */
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NO_MORE_ENTRIES)) {
+ if (domains.count == 0) {
+ return true;
+ }
+ torture_comment(tctx, "EnumTrustDom failed - should have returned 0 trusted domains with 'NT_STATUS_NO_MORE_ENTRIES'\n");
+ return false;
+ } else if (NT_STATUS_EQUAL(r.out.result, 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) {
+ torture_comment(tctx, "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(r.out.result)) {
+ torture_comment(tctx, "EnumTrustDom failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+
+ if (domains.count == 0) {
+ torture_comment(tctx, "EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+
+ ret &= test_query_each_TrustDom(b, tctx, handle, &domains);
+
+ in_resume_handle = out_resume_handle;
+
+ } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES));
+
+ return ret;
+}
+
+static bool test_EnumTrustDomEx(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_EnumTrustedDomainsEx r_ex;
+ uint32_t in_resume_handle = 0;
+ uint32_t out_resume_handle;
+ struct lsa_DomainListEx domains_ex;
+ bool ret = true;
+
+ torture_comment(tctx, "\nTesting EnumTrustedDomainsEx\n");
+
+ r_ex.in.handle = handle;
+ r_ex.in.resume_handle = &in_resume_handle;
+ r_ex.in.max_size = 0;
+ r_ex.out.domains = &domains_ex;
+ r_ex.out.resume_handle = &out_resume_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustedDomainsEx_r(b, tctx, &r_ex),
+ "EnumTrustedDomainsEx failed");
+
+ /* according to MS-LSAD 3.1.4.7.8 output resume handle MUST
+ * always be larger than the previous input resume handle, in
+ * particular when hitting the last query it is vital to set the
+ * resume handle correctly to avoid infinite client loops, as
+ * seen e.g. with Windows XP SP3 when resume handle is 0 and
+ * status is NT_STATUS_OK - gd */
+
+ if (NT_STATUS_IS_OK(r_ex.out.result) ||
+ NT_STATUS_EQUAL(r_ex.out.result, NT_STATUS_NO_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(r_ex.out.result, STATUS_MORE_ENTRIES))
+ {
+ if (out_resume_handle <= in_resume_handle) {
+ torture_comment(tctx, "EnumTrustDomEx failed - should have returned output resume_handle (0x%08x) larger than input resume handle (0x%08x)\n",
+ out_resume_handle, in_resume_handle);
+ return false;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(r_ex.out.result)) {
+ if (domains_ex.count == 0) {
+ torture_comment(tctx, "EnumTrustDom failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+ } else if (!(NT_STATUS_EQUAL(r_ex.out.result, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_EQUAL(r_ex.out.result, NT_STATUS_NO_MORE_ENTRIES))) {
+ torture_comment(tctx, "EnumTrustDom of zero size failed - %s\n",
+ nt_errstr(r_ex.out.result));
+ return false;
+ }
+
+ in_resume_handle = 0;
+ do {
+ r_ex.in.handle = handle;
+ r_ex.in.resume_handle = &in_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 = &out_resume_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustedDomainsEx_r(b, tctx, &r_ex),
+ "EnumTrustedDomainsEx failed");
+
+ in_resume_handle = out_resume_handle;
+
+ /* NO_MORE_ENTRIES is allowed */
+ if (NT_STATUS_EQUAL(r_ex.out.result, NT_STATUS_NO_MORE_ENTRIES)) {
+ if (domains_ex.count == 0) {
+ return true;
+ }
+ torture_comment(tctx, "EnumTrustDomainsEx failed - should have returned 0 trusted domains with 'NT_STATUS_NO_MORE_ENTRIES'\n");
+ return false;
+ } else if (NT_STATUS_EQUAL(r_ex.out.result, 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) {
+ torture_comment(tctx, "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(r_ex.out.result)) {
+ torture_comment(tctx, "EnumTrustedDomainEx failed - %s\n", nt_errstr(r_ex.out.result));
+ return false;
+ }
+
+ if (domains_ex.count == 0) {
+ torture_comment(tctx, "EnumTrustDomainEx failed - should have returned 'NT_STATUS_NO_MORE_ENTRIES' for 0 trusted domains\n");
+ return false;
+ }
+
+ ret &= test_query_each_TrustDomEx(b, tctx, handle, &domains_ex);
+
+ } while (NT_STATUS_EQUAL(r_ex.out.result, STATUS_MORE_ENTRIES));
+
+ return ret;
+}
+
+
+static bool test_CreateTrustedDomain(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t num_trusts)
+{
+ bool ret = true;
+ struct lsa_CreateTrustedDomain r;
+ struct lsa_DomainInfo trustinfo;
+ struct dom_sid **domsid;
+ struct policy_handle *trustdom_handle;
+ struct lsa_QueryTrustedDomainInfo q;
+ union lsa_TrustedDomainInfo *info = NULL;
+ int i;
+
+ torture_comment(tctx, "\nTesting CreateTrustedDomain for %d domains\n", num_trusts);
+
+ if (!test_EnumTrustDom(b, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumTrustDomEx(b, tctx, handle)) {
+ ret = false;
+ }
+
+ domsid = talloc_array(tctx, struct dom_sid *, num_trusts);
+ trustdom_handle = talloc_array(tctx, struct policy_handle, num_trusts);
+
+ for (i=0; i< num_trusts; i++) {
+ char *trust_name = talloc_asprintf(tctx, "TORTURE1%02d", i);
+ char *trust_sid = talloc_asprintf(tctx, "S-1-5-21-97398-379795-1%02d", i);
+
+ domsid[i] = dom_sid_parse_talloc(tctx, 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];
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateTrustedDomain_r(b, tctx, &r),
+ "CreateTrustedDomain failed");
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ test_DeleteTrustedDomain(b, tctx, handle, trustinfo.name);
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateTrustedDomain_r(b, tctx, &r),
+ "CreateTrustedDomain failed");
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "CreateTrustedDomain failed - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ } else {
+
+ q.in.trustdom_handle = &trustdom_handle[i];
+ q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX;
+ q.out.info = &info;
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfo_r(b, tctx, &q),
+ "QueryTrustedDomainInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_comment(tctx, "QueryTrustedDomainInfo level %d failed - %s\n", q.in.level, nt_errstr(q.out.result));
+ ret = false;
+ } else if (!q.out.info) {
+ ret = false;
+ } else {
+ if (strcmp(info->info_ex.domain_name.string, trustinfo.name.string) != 0) {
+ torture_comment(tctx, "QueryTrustedDomainInfo returned inconsistent long name: %s != %s\n",
+ info->info_ex.domain_name.string, trustinfo.name.string);
+ ret = false;
+ }
+ if (strcmp(info->info_ex.netbios_name.string, trustinfo.name.string) != 0) {
+ torture_comment(tctx, "QueryTrustedDomainInfo returned inconsistent short name: %s != %s\n",
+ info->info_ex.netbios_name.string, trustinfo.name.string);
+ ret = false;
+ }
+ if (info->info_ex.trust_type != LSA_TRUST_TYPE_DOWNLEVEL) {
+ torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust type %d != %d\n",
+ trust_name, info->info_ex.trust_type, LSA_TRUST_TYPE_DOWNLEVEL);
+ ret = false;
+ }
+ if (info->info_ex.trust_attributes != 0) {
+ torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust attributes %d != %d\n",
+ trust_name, info->info_ex.trust_attributes, 0);
+ ret = false;
+ }
+ if (info->info_ex.trust_direction != LSA_TRUST_DIRECTION_OUTBOUND) {
+ torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust direction %d != %d\n",
+ trust_name, 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(b, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumTrustDomEx(b, tctx, handle)) {
+ ret = false;
+ }
+
+ for (i=0; i<num_trusts; i++) {
+ if (!test_DeleteTrustedDomainBySid(b, tctx, handle, domsid[i])) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool gen_authinfo_internal(TALLOC_CTX *mem_ctx,
+ const char *incoming_old, const char *incoming_new,
+ const char *outgoing_old, const char *outgoing_new,
+ DATA_BLOB session_key,
+ struct lsa_TrustDomainInfoAuthInfoInternal **_authinfo_internal)
+{
+ struct lsa_TrustDomainInfoAuthInfoInternal *authinfo_internal;
+ struct trustDomainPasswords auth_struct;
+ struct AuthenticationInformation in_info;
+ struct AuthenticationInformation io_info;
+ struct AuthenticationInformation on_info;
+ struct AuthenticationInformation oo_info;
+ size_t converted_size;
+ DATA_BLOB auth_blob;
+ enum ndr_err_code ndr_err;
+ bool ok;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t _session_key;
+
+ authinfo_internal = talloc_zero(mem_ctx, struct lsa_TrustDomainInfoAuthInfoInternal);
+ if (authinfo_internal == NULL) {
+ return false;
+ }
+
+ in_info.AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ok = convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16,
+ incoming_new,
+ strlen(incoming_new),
+ &in_info.AuthInfo.clear.password,
+ &converted_size);
+ if (!ok) {
+ return false;
+ }
+ in_info.AuthInfo.clear.size = converted_size;
+
+ io_info.AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ok = convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16,
+ incoming_old,
+ strlen(incoming_old),
+ &io_info.AuthInfo.clear.password,
+ &converted_size);
+ if (!ok) {
+ return false;
+ }
+ io_info.AuthInfo.clear.size = converted_size;
+
+ on_info.AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ok = convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16,
+ outgoing_new,
+ strlen(outgoing_new),
+ &on_info.AuthInfo.clear.password,
+ &converted_size);
+ if (!ok) {
+ return false;
+ }
+ on_info.AuthInfo.clear.size = converted_size;
+
+ oo_info.AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ok = convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16,
+ outgoing_old,
+ strlen(outgoing_old),
+ &oo_info.AuthInfo.clear.password,
+ &converted_size);
+ if (!ok) {
+ return false;
+ }
+ oo_info.AuthInfo.clear.size = converted_size;
+
+ generate_random_buffer(auth_struct.confounder, sizeof(auth_struct.confounder));
+ auth_struct.outgoing.count = 1;
+ auth_struct.outgoing.current.count = 1;
+ auth_struct.outgoing.current.array = &on_info;
+ auth_struct.outgoing.previous.count = 1;
+ auth_struct.outgoing.previous.array = &oo_info;
+
+ auth_struct.incoming.count = 1;
+ auth_struct.incoming.current.count = 1;
+ auth_struct.incoming.current.array = &in_info;
+ auth_struct.incoming.previous.count = 1;
+ auth_struct.incoming.previous.array = &io_info;
+
+ ndr_err = ndr_push_struct_blob(&auth_blob, mem_ctx, &auth_struct,
+ (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ _session_key = (gnutls_datum_t) {
+ .data = session_key.data,
+ .size = session_key.length,
+ };
+
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &_session_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd,
+ auth_blob.data,
+ auth_blob.length);
+ gnutls_cipher_deinit(cipher_hnd);
+
+ authinfo_internal->auth_blob.size = auth_blob.length;
+ authinfo_internal->auth_blob.data = auth_blob.data;
+
+ *_authinfo_internal = authinfo_internal;
+
+ return true;
+}
+
+static bool gen_authinfo(TALLOC_CTX *mem_ctx,
+ const char *incoming_old, const char *incoming_new,
+ const char *outgoing_old, const char *outgoing_new,
+ struct lsa_TrustDomainInfoAuthInfo **_authinfo)
+{
+ struct lsa_TrustDomainInfoAuthInfo *authinfo;
+ struct lsa_TrustDomainInfoBuffer *in_buffer;
+ struct lsa_TrustDomainInfoBuffer *io_buffer;
+ struct lsa_TrustDomainInfoBuffer *on_buffer;
+ struct lsa_TrustDomainInfoBuffer *oo_buffer;
+ size_t converted_size;
+ bool ok;
+
+ authinfo = talloc_zero(mem_ctx, struct lsa_TrustDomainInfoAuthInfo);
+ if (authinfo == NULL) {
+ return false;
+ }
+
+ in_buffer = talloc_zero(authinfo, struct lsa_TrustDomainInfoBuffer);
+ if (in_buffer == NULL) {
+ return false;
+ }
+ in_buffer->AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ok = convert_string_talloc(in_buffer, CH_UNIX, CH_UTF16,
+ incoming_new,
+ strlen(incoming_new),
+ &in_buffer->data.data,
+ &converted_size);
+ if (!ok) {
+ return false;
+ }
+ in_buffer->data.size = converted_size;
+
+ io_buffer = talloc_zero(authinfo, struct lsa_TrustDomainInfoBuffer);
+ if (io_buffer == NULL) {
+ return false;
+ }
+ io_buffer->AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ok = convert_string_talloc(io_buffer, CH_UNIX, CH_UTF16,
+ incoming_old,
+ strlen(incoming_old),
+ &io_buffer->data.data,
+ &converted_size);
+ if (!ok) {
+ return false;
+ }
+ io_buffer->data.size = converted_size;
+
+ on_buffer = talloc_zero(authinfo, struct lsa_TrustDomainInfoBuffer);
+ if (on_buffer == NULL) {
+ return false;
+ }
+ on_buffer->AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ok = convert_string_talloc(on_buffer, CH_UNIX, CH_UTF16,
+ outgoing_new,
+ strlen(outgoing_new),
+ &on_buffer->data.data,
+ &converted_size);
+ if (!ok) {
+ return false;
+ }
+ on_buffer->data.size = converted_size;
+
+ oo_buffer = talloc_zero(authinfo, struct lsa_TrustDomainInfoBuffer);
+ if (oo_buffer == NULL) {
+ return false;
+ }
+ oo_buffer->AuthType = TRUST_AUTH_TYPE_CLEAR;
+ ok = convert_string_talloc(oo_buffer, CH_UNIX, CH_UTF16,
+ outgoing_old,
+ strlen(outgoing_old),
+ &oo_buffer->data.data,
+ &converted_size);
+ if (!ok) {
+ return false;
+ }
+ oo_buffer->data.size = converted_size;
+
+ authinfo->incoming_count = 1;
+ authinfo->incoming_current_auth_info = in_buffer;
+ authinfo->incoming_previous_auth_info = io_buffer;
+ authinfo->outgoing_count = 1;
+ authinfo->outgoing_current_auth_info = on_buffer;
+ authinfo->outgoing_previous_auth_info = oo_buffer;
+
+ *_authinfo = authinfo;
+
+ return true;
+}
+
+static bool check_pw_with_ServerAuthenticate3(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ uint32_t negotiate_flags,
+ const char *server_name,
+ struct cli_credentials *machine_credentials,
+ struct netlogon_creds_CredentialState **creds_out)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ const struct samr_Password *new_password = NULL;
+ const struct samr_Password *old_password = NULL;
+ uint32_t rid;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ new_password = cli_credentials_get_nt_hash(machine_credentials, tctx);
+ old_password = cli_credentials_get_old_nt_hash(machine_credentials, tctx);
+
+ r.in.server_name = server_name;
+ r.in.computer_name = cli_credentials_get_workstation(machine_credentials);
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ a.in.server_name = server_name;
+ a.in.account_name = cli_credentials_get_username(machine_credentials);
+ a.in.secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = cli_credentials_get_workstation(machine_credentials);
+ a.in.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.out.rid = &rid;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ new_password, &credentials3,
+ negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a),
+ "ServerAuthenticate3 failed");
+ if (!NT_STATUS_IS_OK(a.out.result)) {
+ if (!NT_STATUS_EQUAL(a.out.result, NT_STATUS_ACCESS_DENIED)) {
+ torture_assert_ntstatus_ok(tctx, a.out.result,
+ "ServerAuthenticate3 failed");
+ }
+ return false;
+ }
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ if (old_password != NULL) {
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ old_password, &credentials3,
+ negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a),
+ "ServerAuthenticate3 failed");
+ if (!NT_STATUS_IS_OK(a.out.result)) {
+ if (!NT_STATUS_EQUAL(a.out.result, NT_STATUS_ACCESS_DENIED)) {
+ torture_assert_ntstatus_ok(tctx, a.out.result,
+ "ServerAuthenticate3 (old) failed");
+ }
+ return false;
+ }
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential (old) chaining failed");
+ }
+
+ /* Prove that requesting a challenge again won't break it */
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ *creds_out = creds;
+ return true;
+}
+
+#ifdef SAMBA4_USES_HEIMDAL
+
+/*
+ * This function is set in torture_krb5_init_context as krb5
+ * send_and_recv function. This allows us to override what server the
+ * test is aimed at, and to inspect the packets just before they are
+ * sent to the network, and before they are processed on the recv
+ * side.
+ *
+ * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test()
+ * functions are implement the actual tests.
+ *
+ * When this asserts, the caller will get a spurious 'cannot contact
+ * any KDC' message.
+ *
+ */
+struct check_pw_with_krb5_ctx {
+ struct addrinfo *server;
+ const char *server_nb_domain;
+ const char *server_dns_domain;
+ struct {
+ unsigned io;
+ unsigned fail;
+ unsigned errors;
+ unsigned error_io;
+ unsigned ok;
+ } counts;
+ krb5_error error;
+ struct smb_krb5_context *smb_krb5_context;
+ krb5_get_init_creds_opt *krb_options;
+ krb5_creds my_creds;
+ krb5_get_creds_opt opt_canon;
+ krb5_get_creds_opt opt_nocanon;
+ krb5_principal upn_realm;
+ krb5_principal upn_dns;
+ krb5_principal upn_netbios;
+ krb5_ccache krbtgt_ccache;
+ krb5_principal krbtgt_trust_realm;
+ krb5_creds *krbtgt_trust_realm_creds;
+ krb5_principal krbtgt_trust_dns;
+ krb5_creds *krbtgt_trust_dns_creds;
+ krb5_principal krbtgt_trust_netbios;
+ krb5_creds *krbtgt_trust_netbios_creds;
+ krb5_principal cifs_trust_dns;
+ krb5_creds *cifs_trust_dns_creds;
+ krb5_principal cifs_trust_netbios;
+ krb5_creds *cifs_trust_netbios_creds;
+ krb5_principal drs_trust_dns;
+ krb5_creds *drs_trust_dns_creds;
+ krb5_principal drs_trust_netbios;
+ krb5_creds *drs_trust_netbios_creds;
+ krb5_principal four_trust_dns;
+ krb5_creds *four_trust_dns_creds;
+ krb5_creds krbtgt_referral_creds;
+ Ticket krbtgt_referral_ticket;
+ krb5_keyblock krbtgt_referral_keyblock;
+ EncTicketPart krbtgt_referral_enc_part;
+};
+
+static krb5_error_code check_pw_with_krb5_send_to_realm(
+ struct smb_krb5_context *smb_krb5_context,
+ void *data, /* struct check_pw_with_krb5_ctx */
+ krb5_const_realm realm,
+ time_t timeout,
+ const krb5_data *send_buf,
+ krb5_data *recv_buf)
+{
+ struct check_pw_with_krb5_ctx *ctx =
+ talloc_get_type_abort(data, struct check_pw_with_krb5_ctx);
+ krb5_error_code k5ret;
+ size_t used;
+ int ret;
+
+ SMB_ASSERT(smb_krb5_context == ctx->smb_krb5_context);
+
+ if (!strequal_m(realm, ctx->server_nb_domain) &&
+ !strequal_m(realm, ctx->server_dns_domain))
+ {
+ return KRB5_KDC_UNREACH;
+ }
+
+ krb5_free_error_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->error);
+ ctx->counts.io++;
+
+ k5ret = smb_krb5_send_and_recv_func_forced_tcp(ctx->smb_krb5_context,
+ ctx->server,
+ timeout, send_buf, recv_buf);
+ if (k5ret != 0) {
+ ctx->counts.fail++;
+ return k5ret;
+ }
+
+ ret = decode_KRB_ERROR(recv_buf->data, recv_buf->length,
+ &ctx->error, &used);
+ if (ret == 0) {
+ ctx->counts.errors++;
+ ctx->counts.error_io = ctx->counts.io;
+ } else {
+ ctx->counts.ok++;
+ }
+
+ return k5ret;
+}
+
+static int check_pw_with_krb5_ctx_destructor(struct check_pw_with_krb5_ctx *ctx)
+{
+ if (ctx->server != NULL) {
+ freeaddrinfo(ctx->server);
+ ctx->server = NULL;
+ }
+
+ if (ctx->krb_options != NULL) {
+ krb5_get_init_creds_opt_free(ctx->smb_krb5_context->krb5_context,
+ ctx->krb_options);
+ ctx->krb_options = NULL;
+ }
+
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->my_creds);
+
+ if (ctx->opt_canon != NULL) {
+ krb5_get_creds_opt_free(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon);
+ ctx->opt_canon = NULL;
+ }
+
+ if (ctx->opt_nocanon != NULL) {
+ krb5_get_creds_opt_free(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_nocanon);
+ ctx->opt_nocanon = NULL;
+ }
+
+ if (ctx->krbtgt_ccache != NULL) {
+ krb5_cc_close(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache);
+ ctx->krbtgt_ccache = NULL;
+ }
+
+ if (ctx->upn_realm != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->upn_realm);
+ ctx->upn_realm = NULL;
+ }
+
+ if (ctx->upn_dns != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->upn_dns);
+ ctx->upn_dns = NULL;
+ }
+
+ if (ctx->upn_netbios != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->upn_netbios);
+ ctx->upn_netbios = NULL;
+ }
+
+ if (ctx->krbtgt_trust_realm != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_realm);
+ ctx->krbtgt_trust_realm = NULL;
+ }
+
+ if (ctx->krbtgt_trust_realm_creds != NULL) {
+ krb5_free_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_realm_creds);
+ ctx->krbtgt_trust_realm_creds = NULL;
+ }
+
+ if (ctx->krbtgt_trust_dns != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_dns);
+ ctx->krbtgt_trust_dns = NULL;
+ }
+
+ if (ctx->krbtgt_trust_dns_creds != NULL) {
+ krb5_free_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_dns_creds);
+ ctx->krbtgt_trust_dns_creds = NULL;
+ }
+
+ if (ctx->krbtgt_trust_netbios != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_netbios);
+ ctx->krbtgt_trust_netbios = NULL;
+ }
+
+ if (ctx->krbtgt_trust_netbios_creds != NULL) {
+ krb5_free_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_netbios_creds);
+ ctx->krbtgt_trust_netbios_creds = NULL;
+ }
+
+ if (ctx->cifs_trust_dns != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->cifs_trust_dns);
+ ctx->cifs_trust_dns = NULL;
+ }
+
+ if (ctx->cifs_trust_dns_creds != NULL) {
+ krb5_free_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->cifs_trust_dns_creds);
+ ctx->cifs_trust_dns_creds = NULL;
+ }
+
+ if (ctx->cifs_trust_netbios != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->cifs_trust_netbios);
+ ctx->cifs_trust_netbios = NULL;
+ }
+
+ if (ctx->cifs_trust_netbios_creds != NULL) {
+ krb5_free_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->cifs_trust_netbios_creds);
+ ctx->cifs_trust_netbios_creds = NULL;
+ }
+
+ if (ctx->drs_trust_dns != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->drs_trust_dns);
+ ctx->drs_trust_dns = NULL;
+ }
+
+ if (ctx->drs_trust_dns_creds != NULL) {
+ krb5_free_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->drs_trust_dns_creds);
+ ctx->drs_trust_dns_creds = NULL;
+ }
+
+ if (ctx->drs_trust_netbios != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->drs_trust_netbios);
+ ctx->drs_trust_netbios = NULL;
+ }
+
+ if (ctx->drs_trust_netbios_creds != NULL) {
+ krb5_free_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->drs_trust_netbios_creds);
+ ctx->drs_trust_netbios_creds = NULL;
+ }
+
+ if (ctx->four_trust_dns != NULL) {
+ krb5_free_principal(ctx->smb_krb5_context->krb5_context,
+ ctx->four_trust_dns);
+ ctx->four_trust_dns = NULL;
+ }
+
+ if (ctx->four_trust_dns_creds != NULL) {
+ krb5_free_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->four_trust_dns_creds);
+ ctx->four_trust_dns_creds = NULL;
+ }
+
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+
+ free_Ticket(&ctx->krbtgt_referral_ticket);
+
+ krb5_free_keyblock_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_keyblock);
+
+ free_EncTicketPart(&ctx->krbtgt_referral_enc_part);
+
+ krb5_free_error_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->error);
+
+ talloc_unlink(ctx, ctx->smb_krb5_context);
+ ctx->smb_krb5_context = NULL;
+ return 0;
+}
+
+static bool check_pw_with_krb5(struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ const struct lsa_TrustDomainInfoInfoEx *trusted)
+{
+ const char *trusted_dns_name = trusted->domain_name.string;
+ const char *trusted_netbios_name = trusted->netbios_name.string;
+ char *trusted_realm_name = NULL;
+ krb5_principal principal = NULL;
+ enum credentials_obtained obtained;
+ const char *error_string = NULL;
+ const char *workstation = cli_credentials_get_workstation(credentials);
+ const char *password = cli_credentials_get_password(credentials);
+#ifndef USING_EMBEDDED_HEIMDAL
+ const struct samr_Password *nthash = NULL;
+ const struct samr_Password *old_nthash = NULL;
+#endif
+ const char *old_password = cli_credentials_get_old_password(credentials);
+#ifndef USING_EMBEDDED_HEIMDAL
+ int kvno = cli_credentials_get_kvno(credentials);
+ int expected_kvno = 0;
+ krb5uint32 t_kvno = 0;
+#endif
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ krb5_error_code k5ret;
+ krb5_boolean k5ok;
+ int type;
+ bool ok;
+ struct check_pw_with_krb5_ctx *ctx = NULL;
+ char *assertion_message = NULL;
+ const char *realm = NULL;
+ char *upn_realm_string = NULL;
+ char *upn_dns_string = NULL;
+ char *upn_netbios_string = NULL;
+ char *krbtgt_cc_name = NULL;
+ char *krbtgt_trust_realm_string = NULL;
+ char *krbtgt_trust_dns_string = NULL;
+ char *krbtgt_trust_netbios_string = NULL;
+ char *cifs_trust_dns_string = NULL;
+ char *cifs_trust_netbios_string = NULL;
+ char *drs_trust_dns_string = NULL;
+ char *drs_trust_netbios_string = NULL;
+ char *four_trust_dns_string = NULL;
+
+ ctx = talloc_zero(tctx, struct check_pw_with_krb5_ctx);
+ torture_assert(tctx, ctx != NULL, "Failed to allocate");
+
+ realm = cli_credentials_get_realm(credentials);
+ trusted_realm_name = strupper_talloc(tctx, trusted_dns_name);
+
+#ifndef USING_EMBEDDED_HEIMDAL
+ nthash = cli_credentials_get_nt_hash(credentials, ctx);
+ old_nthash = cli_credentials_get_old_nt_hash(credentials, ctx);
+#endif
+
+ k5ret = smb_krb5_init_context(ctx, tctx->lp_ctx, &ctx->smb_krb5_context);
+ torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
+
+ ctx->server_nb_domain = cli_credentials_get_domain(credentials);
+ ctx->server_dns_domain = cli_credentials_get_realm(credentials);
+
+ ok = interpret_string_addr_internal(&ctx->server, host, 0);
+ torture_assert(tctx, ok, "Failed to parse target server");
+ talloc_set_destructor(ctx, check_pw_with_krb5_ctx_destructor);
+
+ set_sockaddr_port(ctx->server->ai_addr, 88);
+
+ k5ret = smb_krb5_set_send_to_kdc_func(ctx->smb_krb5_context,
+ check_pw_with_krb5_send_to_realm,
+ NULL, /* send_to_kdc */
+ ctx);
+ torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
+
+ torture_assert_int_equal(tctx,
+ krb5_get_init_creds_opt_alloc(ctx->smb_krb5_context->krb5_context,
+ &ctx->krb_options),
+ 0, "krb5_get_init_creds_opt_alloc failed");
+ torture_assert_int_equal(tctx,
+ krb5_get_init_creds_opt_set_pac_request(
+ ctx->smb_krb5_context->krb5_context,
+ ctx->krb_options, true),
+ 0, "krb5_get_init_creds_opt_set_pac_request failed");
+
+ upn_realm_string = talloc_asprintf(ctx, "user@%s",
+ trusted_realm_name);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->upn_realm,
+ realm, upn_realm_string, NULL),
+ 0, "smb_krb5_make_principal failed");
+ smb_krb5_principal_set_type(ctx->smb_krb5_context->krb5_context,
+ ctx->upn_realm, KRB5_NT_ENTERPRISE_PRINCIPAL);
+
+ upn_dns_string = talloc_asprintf(ctx, "user@%s",
+ trusted_dns_name);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->upn_dns,
+ realm, upn_dns_string, NULL),
+ 0, "smb_krb5_make_principal failed");
+ smb_krb5_principal_set_type(ctx->smb_krb5_context->krb5_context,
+ ctx->upn_dns, KRB5_NT_ENTERPRISE_PRINCIPAL);
+
+ upn_netbios_string = talloc_asprintf(ctx, "user@%s",
+ trusted_netbios_name);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->upn_netbios,
+ realm, upn_netbios_string, NULL),
+ 0, "smb_krb5_make_principal failed");
+ smb_krb5_principal_set_type(ctx->smb_krb5_context->krb5_context,
+ ctx->upn_netbios, KRB5_NT_ENTERPRISE_PRINCIPAL);
+
+ k5ret = principal_from_credentials(ctx, credentials, ctx->smb_krb5_context,
+ &principal, &obtained, &error_string);
+ torture_assert_int_equal(tctx, k5ret, 0, error_string);
+
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context,
+ &ctx->my_creds, ctx->upn_realm,
+ "_none_", NULL, NULL, 0,
+ NULL, ctx->krb_options);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_init_creds_password(%s, canon) for failed: "
+ "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u/%u,ok=%u]",
+ upn_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.error_io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.error_io, 1, assertion_message);
+ torture_assert_int_equal(tctx, KRB5_ERROR_CODE(&ctx->error), 68, assertion_message);
+ torture_assert(tctx, ctx->error.crealm != NULL, assertion_message);
+ torture_assert_str_equal(tctx, *ctx->error.crealm, trusted_realm_name, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert(tctx, ctx->error.cname != NULL, assertion_message);
+ torture_assert_int_equal(tctx, ctx->error.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, assertion_message);
+ torture_assert_int_equal(tctx, ctx->error.cname->name_string.len, 1, assertion_message);
+ torture_assert_str_equal(tctx, ctx->error.cname->name_string.val[0], upn_realm_string, assertion_message);
+#else
+ torture_assert(tctx, ctx->error.cname == NULL, assertion_message);
+#endif
+ torture_assert_str_equal(tctx, ctx->error.realm, realm, assertion_message);
+
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context,
+ &ctx->my_creds, ctx->upn_dns,
+ "_none_", NULL, NULL, 0,
+ NULL, ctx->krb_options);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_init_creds_password(%s, canon) for failed: "
+ "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u/%u,ok=%u]",
+ upn_dns_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.error_io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.error_io, 1, assertion_message);
+ torture_assert_int_equal(tctx, KRB5_ERROR_CODE(&ctx->error), 68, assertion_message);
+ torture_assert(tctx, ctx->error.crealm != NULL, assertion_message);
+ torture_assert_str_equal(tctx, *ctx->error.crealm, trusted_realm_name, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert(tctx, ctx->error.cname != NULL, assertion_message);
+ torture_assert_int_equal(tctx, ctx->error.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, assertion_message);
+ torture_assert_int_equal(tctx, ctx->error.cname->name_string.len, 1, assertion_message);
+ torture_assert_str_equal(tctx, ctx->error.cname->name_string.val[0], upn_dns_string, assertion_message);
+#else
+ torture_assert(tctx, ctx->error.cname == NULL, assertion_message);
+#endif
+ torture_assert_str_equal(tctx, ctx->error.realm, realm, assertion_message);
+
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context,
+ &ctx->my_creds, ctx->upn_netbios,
+ "_none_", NULL, NULL, 0,
+ NULL, ctx->krb_options);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_init_creds_password(%s, canon) for failed: "
+ "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u/%u,ok=%u]",
+ upn_netbios_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.error_io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.error_io, 1, assertion_message);
+ torture_assert_int_equal(tctx, KRB5_ERROR_CODE(&ctx->error), 68, assertion_message);
+ torture_assert(tctx, ctx->error.crealm != NULL, assertion_message);
+ torture_assert_str_equal(tctx, *ctx->error.crealm, trusted_realm_name, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert(tctx, ctx->error.cname != NULL, assertion_message);
+ torture_assert_int_equal(tctx, ctx->error.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, assertion_message);
+ torture_assert_int_equal(tctx, ctx->error.cname->name_string.len, 1, assertion_message);
+ torture_assert_str_equal(tctx, ctx->error.cname->name_string.val[0], upn_netbios_string, assertion_message);
+#else
+ torture_assert(tctx, ctx->error.cname == NULL, assertion_message);
+#endif
+ torture_assert_str_equal(tctx, ctx->error.realm, realm, assertion_message);
+
+ torture_comment(tctx, "(%s:%s) password[%s] old_password[%s]\n",
+ __location__, __FUNCTION__,
+ password, old_password);
+ if (old_password != NULL) {
+ k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context,
+ &ctx->my_creds, principal,
+ old_password, NULL, NULL, 0,
+ NULL, ctx->krb_options);
+ torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_PREAUTH_FAILED,
+ "preauth should fail with old password");
+ }
+
+ k5ret = krb5_get_init_creds_password(ctx->smb_krb5_context->krb5_context,
+ &ctx->my_creds, principal,
+ password, NULL, NULL, 0,
+ NULL, ctx->krb_options);
+ if (k5ret == KRB5KDC_ERR_PREAUTH_FAILED) {
+ TALLOC_FREE(ctx);
+ return false;
+ }
+
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_init_creds_password for failed: %s",
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ torture_assert_int_equal(tctx,
+ krb5_get_creds_opt_alloc(ctx->smb_krb5_context->krb5_context,
+ &ctx->opt_canon),
+ 0, "krb5_get_creds_opt_alloc");
+
+ krb5_get_creds_opt_add_options(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon,
+ KRB5_GC_CANONICALIZE);
+
+ krb5_get_creds_opt_add_options(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon,
+ KRB5_GC_NO_STORE);
+
+ torture_assert_int_equal(tctx,
+ krb5_get_creds_opt_alloc(ctx->smb_krb5_context->krb5_context,
+ &ctx->opt_nocanon),
+ 0, "krb5_get_creds_opt_alloc");
+
+ krb5_get_creds_opt_add_options(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_nocanon,
+ KRB5_GC_NO_STORE);
+
+ krbtgt_cc_name = talloc_asprintf(ctx, "MEMORY:%p.krbtgt", ctx->smb_krb5_context);
+ torture_assert_int_equal(tctx,
+ krb5_cc_resolve(ctx->smb_krb5_context->krb5_context,
+ krbtgt_cc_name,
+ &ctx->krbtgt_ccache),
+ 0, "krb5_cc_resolve failed");
+
+ torture_assert_int_equal(tctx,
+ krb5_cc_initialize(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ ctx->my_creds.client),
+ 0, "krb5_cc_initialize failed");
+
+ torture_assert_int_equal(tctx,
+ krb5_cc_store_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ &ctx->my_creds),
+ 0, "krb5_cc_store_cred failed");
+
+ krbtgt_trust_realm_string = talloc_asprintf(ctx, "krbtgt/%s@%s",
+ trusted_realm_name, realm);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_trust_realm,
+ realm, "krbtgt",
+ trusted_realm_name, NULL),
+ 0, "smb_krb5_make_principal failed");
+
+ krbtgt_trust_dns_string = talloc_asprintf(ctx, "krbtgt/%s@%s",
+ trusted_dns_name, realm);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_trust_dns,
+ realm, "krbtgt",
+ trusted_dns_name, NULL),
+ 0, "smb_krb5_make_principal failed");
+
+ krbtgt_trust_netbios_string = talloc_asprintf(ctx, "krbtgt/%s@%s",
+ trusted_netbios_name, realm);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_trust_netbios,
+ realm, "krbtgt",
+ trusted_netbios_name, NULL),
+ 0, "smb_krb5_make_principal failed");
+
+ /* Confirm if we can do a TGS for krbtgt/trusted_realm */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_nocanon,
+ ctx->krbtgt_ccache,
+ ctx->krbtgt_trust_realm,
+ &ctx->krbtgt_trust_realm_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s, canon) for failed: "
+ "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message);
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_realm_creds->server,
+ ctx->krbtgt_trust_realm);
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_realm_creds->server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Confirm if we have no referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+
+ /* Confirm if we can do a TGS for krbtgt/trusted_dns with CANON */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon,
+ ctx->krbtgt_ccache,
+ ctx->krbtgt_trust_dns,
+ &ctx->krbtgt_trust_dns_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s, canon) for failed: "
+ "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ krbtgt_trust_dns_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message);
+#endif
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message);
+
+ /* Confirm if we have the referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server,
+ ctx->krbtgt_trust_realm);
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+ k5ret = decode_Ticket(ctx->krbtgt_referral_creds.ticket.data,
+ ctx->krbtgt_referral_creds.ticket.length,
+ &ctx->krbtgt_referral_ticket, NULL);
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+ if (kvno > 0) {
+ expected_kvno = kvno - 1;
+ }
+ if (ctx->krbtgt_referral_ticket.enc_part.kvno != NULL) {
+ t_kvno = *ctx->krbtgt_referral_ticket.enc_part.kvno;
+ assertion_message = talloc_asprintf(ctx,
+ "krbtgt_referral_ticket(%s) kvno(%u) expected(%u) current(%u)",
+ krbtgt_trust_realm_string,
+ (unsigned)t_kvno, (unsigned)expected_kvno,(unsigned)kvno);
+ torture_comment(tctx, "%s\n", assertion_message);
+ torture_assert_int_not_equal(tctx, t_kvno, 0, assertion_message);
+ } else {
+ assertion_message = talloc_asprintf(ctx,
+ "krbtgt_referral_ticket(%s) kvno(NULL) exptected(%u) current(%u)",
+ krbtgt_trust_realm_string,
+ (unsigned)expected_kvno,(unsigned)kvno);
+ torture_comment(tctx, "%s\n", assertion_message);
+ }
+ torture_assert_int_equal(tctx, t_kvno, expected_kvno, assertion_message);
+
+ if (old_nthash != NULL && expected_kvno != kvno) {
+ torture_comment(tctx, "old_nthash: %s\n", assertion_message);
+ k5ret = smb_krb5_keyblock_init_contents(ctx->smb_krb5_context->krb5_context,
+ ENCTYPE_ARCFOUR_HMAC,
+ old_nthash->hash,
+ sizeof(old_nthash->hash),
+ &ctx->krbtgt_referral_keyblock);
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+ } else {
+ torture_comment(tctx, "nthash: %s\n", assertion_message);
+ k5ret = smb_krb5_keyblock_init_contents(ctx->smb_krb5_context->krb5_context,
+ ENCTYPE_ARCFOUR_HMAC,
+ nthash->hash,
+ sizeof(nthash->hash),
+ &ctx->krbtgt_referral_keyblock);
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+ }
+ k5ret = krb5_decrypt_ticket(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_ticket,
+ &ctx->krbtgt_referral_keyblock,
+ &ctx->krbtgt_referral_enc_part,
+ 0);
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ /* Delete the referral ticket from the cache */
+ k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_remove_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#endif
+
+ /* Confirm if we can do a TGS for krbtgt/trusted_dns no CANON */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_nocanon,
+ ctx->krbtgt_ccache,
+ ctx->krbtgt_trust_dns,
+ &ctx->krbtgt_trust_dns_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s, nocanon) for failed: "
+ "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ krbtgt_trust_dns_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message);
+#else
+ torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message);
+#endif
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_dns_creds->server,
+#ifdef USING_EMBEDDED_HEIMDAL
+ ctx->krbtgt_trust_dns);
+#else
+ ctx->krbtgt_trust_realm);
+#endif
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_dns_creds->server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Confirm if we have the referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server,
+ ctx->krbtgt_trust_realm);
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Delete the referral ticket from the cache */
+ k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_remove_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#endif
+
+ /* Confirm if we can do a TGS for krbtgt/NETBIOS with CANON */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon,
+ ctx->krbtgt_ccache,
+ ctx->krbtgt_trust_netbios,
+ &ctx->krbtgt_trust_netbios_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s, canon) for failed: "
+ "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ krbtgt_trust_netbios_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message);
+#endif
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message);
+
+ /* Confirm if we have the referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_netbios_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server,
+ ctx->krbtgt_trust_realm);
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Delete the referral ticket from the cache */
+ k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_remove_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#endif
+
+ /* Confirm if we can do a TGS for krbtgt/NETBIOS no CANON */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_nocanon,
+ ctx->krbtgt_ccache,
+ ctx->krbtgt_trust_netbios,
+ &ctx->krbtgt_trust_netbios_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s, nocanon) for failed: "
+ "(%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ krbtgt_trust_netbios_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message);
+#else
+ torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message);
+#endif
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_netbios_creds->server,
+#ifdef USING_EMBEDDED_HEIMDAL
+ ctx->krbtgt_trust_netbios);
+#else
+ ctx->krbtgt_trust_realm);
+#endif
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_trust_netbios_creds->server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Confirm if we have the referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server,
+ ctx->krbtgt_trust_realm);
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Delete the referral ticket from the cache */
+ k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_remove_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#endif
+
+ cifs_trust_dns_string = talloc_asprintf(ctx, "cifs/%s@%s",
+ trusted_dns_name, realm);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->cifs_trust_dns,
+ realm, "cifs",
+ trusted_dns_name, NULL),
+ 0, "smb_krb5_make_principal failed");
+
+ /* Confirm if we get krbtgt/trusted_realm back when asking for cifs/trusted_realm */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon,
+ ctx->krbtgt_ccache,
+ ctx->cifs_trust_dns,
+ &ctx->cifs_trust_dns_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ cifs_trust_dns_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message);
+#else
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message);
+#endif
+
+ /* Confirm if we have the referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server,
+ ctx->krbtgt_trust_realm);
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Delete the referral ticket from the cache */
+ k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_remove_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#endif
+
+ cifs_trust_netbios_string = talloc_asprintf(ctx, "cifs/%s@%s",
+ trusted_netbios_name, realm);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->cifs_trust_netbios,
+ realm, "cifs",
+ trusted_netbios_name, NULL),
+ 0, "smb_krb5_make_principal failed");
+
+ /* Confirm if we get krbtgt/trusted_realm back when asking for cifs/trusted_realm */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon,
+ ctx->krbtgt_ccache,
+ ctx->cifs_trust_netbios,
+ &ctx->cifs_trust_netbios_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ cifs_trust_netbios_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message);
+#else
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message);
+#endif
+
+ /* Confirm if we have the referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server,
+ ctx->krbtgt_trust_realm);
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Delete the referral ticket from the cache */
+ k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_remove_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#endif
+
+ drs_trust_dns_string = talloc_asprintf(ctx,
+ "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s@%s",
+ workstation, trusted_dns_name, realm);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->drs_trust_dns,
+ realm, "E3514235-4B06-11D1-AB04-00C04FC2DCD2",
+ workstation, trusted_dns_name, NULL),
+ 0, "smb_krb5_make_principal failed");
+
+ /* Confirm if we get krbtgt/trusted_realm back when asking for a 3 part principal */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon,
+ ctx->krbtgt_ccache,
+ ctx->drs_trust_dns,
+ &ctx->drs_trust_dns_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ drs_trust_dns_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message);
+#else
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message);
+#endif
+
+ /* Confirm if we have the referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server,
+ ctx->krbtgt_trust_realm);
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Delete the referral ticket from the cache */
+ k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_remove_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#endif
+
+ drs_trust_netbios_string = talloc_asprintf(ctx,
+ "E3514235-4B06-11D1-AB04-00C04FC2DCD2/%s/%s@%s",
+ workstation, trusted_netbios_name, realm);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->drs_trust_netbios,
+ realm, "E3514235-4B06-11D1-AB04-00C04FC2DCD2",
+ workstation, trusted_netbios_name, NULL),
+ 0, "smb_krb5_make_principal failed");
+
+ /* Confirm if we get krbtgt/trusted_realm back when asking for a 3 part principal */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon,
+ ctx->krbtgt_ccache,
+ ctx->drs_trust_netbios,
+ &ctx->drs_trust_netbios_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ drs_trust_netbios_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, KRB5_KDC_UNREACH, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 2, assertion_message);
+#else
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.ok, 1, assertion_message);
+#endif
+
+ /* Confirm if we have the referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+#else
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+
+ k5ok = krb5_principal_compare(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server,
+ ctx->krbtgt_trust_realm);
+ torture_assert(tctx, k5ok, assertion_message);
+ type = smb_krb5_principal_get_type(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_referral_creds.server);
+ torture_assert_int_equal(tctx, type, KRB5_NT_SRV_INST, assertion_message);
+
+ /* Delete the referral ticket from the cache */
+ k5ret = krb5_cc_remove_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_remove_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
+#endif
+
+ four_trust_dns_string = talloc_asprintf(ctx, "four/tree/two/%s@%s",
+ trusted_dns_name, realm);
+ torture_assert_int_equal(tctx,
+ smb_krb5_make_principal(ctx->smb_krb5_context->krb5_context,
+ &ctx->four_trust_dns,
+ realm, "four", "tree", "two",
+ trusted_dns_name, NULL),
+ 0, "smb_krb5_make_principal failed");
+
+ /* Confirm if we get an error back for a 4 part principal */
+ ZERO_STRUCT(ctx->counts);
+ k5ret = krb5_get_creds(ctx->smb_krb5_context->krb5_context,
+ ctx->opt_canon,
+ ctx->krbtgt_ccache,
+ ctx->four_trust_dns,
+ &ctx->four_trust_dns_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_get_creds(%s) for failed: (%d) %s; t[d=0x%x,t=0x%x,a=0x%x] [io=%u,error=%u,ok=%u]",
+ four_trust_dns_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx),
+ trusted->trust_direction,
+ trusted->trust_type,
+ trusted->trust_attributes,
+ ctx->counts.io, ctx->counts.errors, ctx->counts.ok);
+ torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, assertion_message);
+#ifdef USING_EMBEDDED_HEIMDAL
+ torture_assert_int_equal(tctx, ctx->counts.io, 2, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.error_io, 2, assertion_message);
+#else
+ torture_assert_int_equal(tctx, ctx->counts.io, 1, assertion_message);
+ torture_assert_int_equal(tctx, ctx->counts.error_io, 1, assertion_message);
+#endif
+ torture_assert_int_equal(tctx, KRB5_ERROR_CODE(&ctx->error), 7, assertion_message);
+
+ /* Confirm if we have no referral ticket in the cache */
+ krb5_free_cred_contents(ctx->smb_krb5_context->krb5_context,
+ &ctx->krbtgt_referral_creds);
+ k5ret = krb5_cc_retrieve_cred(ctx->smb_krb5_context->krb5_context,
+ ctx->krbtgt_ccache,
+ 0,
+ ctx->krbtgt_trust_realm_creds,
+ &ctx->krbtgt_referral_creds);
+ assertion_message = talloc_asprintf(ctx,
+ "krb5_cc_retrieve_cred(%s) for failed: (%d) %s",
+ krbtgt_trust_realm_string,
+ k5ret,
+ smb_get_krb5_error_message(ctx->smb_krb5_context->krb5_context,
+ k5ret, ctx));
+ torture_assert_int_equal(tctx, k5ret, KRB5_CC_END, assertion_message);
+
+ TALLOC_FREE(ctx);
+ return true;
+}
+#endif
+
+static bool check_dom_trust_pw(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ const char *our_netbios_name,
+ const char *our_dns_name,
+ enum netr_SchannelType secure_channel_type,
+ const struct lsa_TrustDomainInfoInfoEx *trusted,
+ const char *previous_password,
+ const char *current_password,
+ uint32_t current_version,
+ const char *next_password,
+ uint32_t next_version,
+ bool expected_result)
+{
+ struct cli_credentials *incoming_creds;
+ char *server_name = NULL;
+ char *account = NULL;
+ char *principal = NULL;
+ char *workstation = NULL;
+ const char *binding = torture_setting_string(tctx, "binding", NULL);
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ const char *ip;
+ struct nbt_name nbt_name;
+ struct dcerpc_binding *b2;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_CryptPassword samr_crypt_password;
+ struct netr_CryptPassword netr_crypt_password;
+ struct netr_Authenticator req_auth;
+ struct netr_Authenticator rep_auth;
+ struct netr_ServerPasswordSet2 s;
+ struct dcerpc_pipe *p1 = NULL;
+ struct dcerpc_pipe *p2 = NULL;
+ NTSTATUS status;
+ bool ok;
+ int rc;
+ const char *trusted_netbios_name = trusted->netbios_name.string;
+ const char *trusted_dns_name = trusted->domain_name.string;
+ struct tsocket_address *dest_addr;
+ struct cldap_socket *cldap;
+ struct cldap_netlogon cldap1;
+
+ incoming_creds = cli_credentials_init(tctx);
+ torture_assert(tctx, incoming_creds, "cli_credentials_init");
+
+ cli_credentials_set_domain(incoming_creds, our_netbios_name, CRED_SPECIFIED);
+ cli_credentials_set_realm(incoming_creds, our_dns_name, CRED_SPECIFIED);
+
+ if (secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+ account = talloc_asprintf(tctx, "%s.", trusted_dns_name);
+ torture_assert(tctx, account, __location__);
+
+ principal = talloc_asprintf(tctx, "%s$@%s",
+ trusted_netbios_name,
+ cli_credentials_get_realm(incoming_creds));
+ torture_assert(tctx, principal, __location__);
+
+ workstation = talloc_asprintf(tctx, "%sUP",
+ trusted_netbios_name);
+ torture_assert(tctx, workstation, __location__);
+ } else {
+ account = talloc_asprintf(tctx, "%s$", trusted_netbios_name);
+ torture_assert(tctx, account, __location__);
+
+ workstation = talloc_asprintf(tctx, "%sDOWN",
+ trusted_netbios_name);
+ torture_assert(tctx, workstation, __location__);
+ }
+
+ cli_credentials_set_username(incoming_creds, account, CRED_SPECIFIED);
+ if (principal != NULL) {
+ cli_credentials_set_principal(incoming_creds, principal,
+ CRED_SPECIFIED);
+ }
+ cli_credentials_set_kvno(incoming_creds, current_version);
+ cli_credentials_set_password(incoming_creds, current_password, CRED_SPECIFIED);
+ cli_credentials_set_old_password(incoming_creds, previous_password, CRED_SPECIFIED);
+ cli_credentials_set_workstation(incoming_creds, workstation, CRED_SPECIFIED);
+ cli_credentials_set_secure_channel_type(incoming_creds, secure_channel_type);
+
+ make_nbt_name_server(&nbt_name, host);
+
+ status = resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx),
+ 0, 0, &nbt_name, tctx, &ip, tctx->ev);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx,"Failed to resolve %s: %s",
+ nbt_name.name, nt_errstr(status)));
+
+ rc = tsocket_address_inet_from_strings(tctx, "ip",
+ ip,
+ lpcfg_cldap_port(tctx->lp_ctx),
+ &dest_addr);
+ torture_assert_int_equal(tctx, rc, 0,
+ talloc_asprintf(tctx,
+ "tsocket_address_inet_from_strings failed parsing %s:%d",
+ host, lpcfg_cldap_port(tctx->lp_ctx)));
+
+ /* cldap_socket_init should now know about the dest. address */
+ status = cldap_socket_init(tctx, NULL, dest_addr, &cldap);
+ torture_assert_ntstatus_ok(tctx, status, "cldap_socket_init");
+
+ ZERO_STRUCT(cldap1);
+ cldap1.in.dest_address = NULL;
+ cldap1.in.dest_port = 0;
+ cldap1.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ cldap1.in.user = account;
+ if (secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+ cldap1.in.acct_control = ACB_AUTOLOCK;
+ } else {
+ cldap1.in.acct_control = ACB_DOMTRUST;
+ }
+ status = cldap_netlogon(cldap, tctx, &cldap1);
+ torture_assert_ntstatus_ok(tctx, status, "cldap_netlogon");
+ torture_assert_int_equal(tctx, cldap1.out.netlogon.ntver,
+ NETLOGON_NT_VERSION_5EX,
+ "ntver");
+ torture_assert_int_equal(tctx, cldap1.out.netlogon.data.nt5_ex.nt_version,
+ NETLOGON_NT_VERSION_1 | NETLOGON_NT_VERSION_5EX,
+ "nt_version");
+ torture_assert_int_equal(tctx, cldap1.out.netlogon.data.nt5_ex.command,
+ LOGON_SAM_LOGON_RESPONSE_EX,
+ "command");
+ torture_assert_str_equal(tctx, cldap1.out.netlogon.data.nt5_ex.user_name,
+ cldap1.in.user,
+ "user_name");
+ server_name = talloc_asprintf(tctx, "\\\\%s",
+ cldap1.out.netlogon.data.nt5_ex.pdc_dns_name);
+ torture_assert(tctx, server_name, __location__);
+
+ status = dcerpc_parse_binding(tctx, binding, &b2);
+ torture_assert_ntstatus_ok(tctx, status, "Bad binding string");
+
+ status = dcerpc_pipe_connect_b(tctx, &p1, b2,
+ &ndr_table_netlogon,
+ cli_credentials_init_anon(tctx),
+ tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_pipe_connect_b");
+
+ ok = check_pw_with_ServerAuthenticate3(p1, tctx,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES,
+ server_name,
+ incoming_creds, &creds);
+ torture_assert_int_equal(tctx, ok, expected_result,
+ "check_pw_with_ServerAuthenticate3");
+ if (expected_result == true) {
+ ok = test_SetupCredentialsPipe(p1, tctx, incoming_creds, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p2);
+ torture_assert_int_equal(tctx, ok, true,
+ "test_SetupCredentialsPipe");
+ }
+ TALLOC_FREE(p1);
+
+ if (trusted->trust_type != LSA_TRUST_TYPE_DOWNLEVEL) {
+#ifdef SAMBA4_USES_HEIMDAL
+ ok = check_pw_with_krb5(tctx, incoming_creds, trusted);
+ torture_assert_int_equal(tctx, ok, expected_result,
+ "check_pw_with_krb5");
+#else
+ torture_comment(tctx, "skipping check_pw_with_krb5 for MIT Kerberos build");
+#endif
+ }
+
+ if (expected_result != true || next_password == NULL) {
+ TALLOC_FREE(p2);
+ return true;
+ }
+
+ /*
+ * netr_ServerPasswordSet2
+ */
+ ok = encode_pw_buffer(samr_crypt_password.data,
+ next_password, STR_UNICODE);
+ torture_assert(tctx, ok, "encode_pw_buffer");
+
+ if (next_version != 0) {
+ struct NL_PASSWORD_VERSION version;
+ uint32_t len = IVAL(samr_crypt_password.data, 512);
+ uint32_t ofs = 512 - len;
+ uint8_t *ptr;
+
+ ofs -= 12;
+
+ version.ReservedField = 0;
+ version.PasswordVersionNumber = next_version;
+ version.PasswordVersionPresent =
+ NETLOGON_PASSWORD_VERSION_NUMBER_PRESENT;
+
+ ptr = samr_crypt_password.data + ofs;
+ SIVAL(ptr, 0, version.ReservedField);
+ SIVAL(ptr, 4, version.PasswordVersionNumber);
+ SIVAL(ptr, 8, version.PasswordVersionPresent);
+ }
+
+ netlogon_creds_client_authenticator(creds, &req_auth);
+ ZERO_STRUCT(rep_auth);
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds,
+ samr_crypt_password.data,
+ 516);
+ } else {
+ netlogon_creds_arcfour_crypt(creds,
+ samr_crypt_password.data,
+ 516);
+ }
+
+ memcpy(netr_crypt_password.data,
+ samr_crypt_password.data, 512);
+ netr_crypt_password.length = IVAL(samr_crypt_password.data, 512);
+
+
+ s.in.server_name = server_name;
+ s.in.account_name = cli_credentials_get_username(incoming_creds);
+ s.in.secure_channel_type = cli_credentials_get_secure_channel_type(incoming_creds);
+ s.in.computer_name = cli_credentials_get_workstation(incoming_creds);
+ s.in.credential = &req_auth;
+ s.in.new_password = &netr_crypt_password;
+ s.out.return_authenticator = &rep_auth;
+ status = dcerpc_netr_ServerPasswordSet2_r(p2->binding_handle, tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "failed to set password");
+
+ ok = netlogon_creds_client_check(creds, &rep_auth.cred);
+ torture_assert(tctx, ok, "netlogon_creds_client_check");
+
+ cli_credentials_set_kvno(incoming_creds, next_version);
+ cli_credentials_set_password(incoming_creds, next_password, CRED_SPECIFIED);
+ cli_credentials_set_old_password(incoming_creds, current_password, CRED_SPECIFIED);
+
+ TALLOC_FREE(p2);
+ status = dcerpc_pipe_connect_b(tctx, &p2, b2,
+ &ndr_table_netlogon,
+ cli_credentials_init_anon(tctx),
+ tctx->ev, tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_pipe_connect_b");
+
+ ok = check_pw_with_ServerAuthenticate3(p2, tctx,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES,
+ server_name,
+ incoming_creds, &creds);
+ torture_assert(tctx, ok, "check_pw_with_ServerAuthenticate3 with changed password");
+
+ if (trusted->trust_type != LSA_TRUST_TYPE_DOWNLEVEL) {
+#if SAMBA4_USES_HEIMDAL
+ ok = check_pw_with_krb5(tctx, incoming_creds, trusted);
+ torture_assert(tctx, ok, "check_pw_with_krb5 with changed password");
+#else
+ torture_comment(tctx, "skipping check_pw_with_krb5 for MIT Kerberos build");
+#endif
+ }
+
+ TALLOC_FREE(p2);
+ return true;
+}
+
+static bool test_CreateTrustedDomainEx_common(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t num_trusts,
+ bool ex2_call)
+{
+ NTSTATUS status;
+ bool ret = true;
+ struct lsa_QueryInfoPolicy2 p2;
+ union lsa_PolicyInformation *our_info = NULL;
+ struct lsa_CreateTrustedDomainEx r;
+ struct lsa_CreateTrustedDomainEx2 r2;
+ struct lsa_TrustDomainInfoInfoEx trustinfo;
+ struct lsa_TrustDomainInfoAuthInfoInternal *authinfo_internal = NULL;
+ struct lsa_TrustDomainInfoAuthInfo *authinfo = NULL;
+ struct dom_sid **domsid;
+ struct policy_handle *trustdom_handle;
+ struct lsa_QueryTrustedDomainInfo q;
+ union lsa_TrustedDomainInfo *info = NULL;
+ DATA_BLOB session_key;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *id;
+ const char *incoming_v00 = TRUSTPW "InV00";
+ const char *incoming_v0 = TRUSTPW "InV0";
+ const char *incoming_v1 = TRUSTPW "InV1";
+ const char *incoming_v2 = TRUSTPW "InV2";
+ const char *incoming_v40 = TRUSTPW "InV40";
+ const char *outgoing_v00 = TRUSTPW "OutV00";
+ const char *outgoing_v0 = TRUSTPW "OutV0";
+
+ if (ex2_call) {
+ torture_comment(tctx, "\nTesting CreateTrustedDomainEx2 for %d domains\n", num_trusts);
+ id = "3";
+ } else {
+ torture_comment(tctx, "\nTesting CreateTrustedDomainEx for %d domains\n", num_trusts);
+ id = "2";
+ }
+
+ domsid = talloc_array(tctx, struct dom_sid *, num_trusts);
+ trustdom_handle = talloc_array(tctx, struct policy_handle, num_trusts);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_fetch_session_key failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ ZERO_STRUCT(p2);
+ p2.in.handle = handle;
+ p2.in.level = LSA_POLICY_INFO_DNS;
+ p2.out.info = &our_info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_QueryInfoPolicy2_r(b, tctx, &p2),
+ "lsa_QueryInfoPolicy2 failed");
+ torture_assert_ntstatus_ok(tctx, p2.out.result,
+ "lsa_QueryInfoPolicy2 failed");
+ torture_assert(tctx, our_info != NULL, "lsa_QueryInfoPolicy2 our_info");
+
+ for (i=0; i< num_trusts; i++) {
+ char *trust_name = talloc_asprintf(tctx, "TORTURE%s%02d", id, i);
+ char *trust_name_dns = talloc_asprintf(tctx, "torturedom%s%02d.samba._none_.example.com", id, i);
+ char *trust_sid = talloc_asprintf(tctx, "S-1-5-21-97398-379795-%s%02d", id, i);
+ bool ok;
+
+ domsid[i] = dom_sid_parse_talloc(tctx, 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 == downlevel (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;
+
+ ok = gen_authinfo_internal(tctx, incoming_v00, incoming_v0,
+ outgoing_v00, outgoing_v0,
+ session_key, &authinfo_internal);
+ if (!ok) {
+ torture_comment(tctx, "gen_authinfo_internal failed");
+ ret = false;
+ }
+
+ ok = gen_authinfo(tctx, incoming_v00, incoming_v0,
+ outgoing_v00, outgoing_v0,
+ &authinfo);
+ if (!ok) {
+ torture_comment(tctx, "gen_authinfonfo failed");
+ ret = false;
+ }
+
+ if (ex2_call) {
+
+ r2.in.policy_handle = handle;
+ r2.in.info = &trustinfo;
+ r2.in.auth_info_internal = authinfo_internal;
+ r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r2.out.trustdom_handle = &trustdom_handle[i];
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_CreateTrustedDomainEx2_r(b, tctx, &r2),
+ "CreateTrustedDomainEx2 failed");
+
+ status = r2.out.result;
+ } else {
+
+ 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];
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_CreateTrustedDomainEx_r(b, tctx, &r),
+ "CreateTrustedDomainEx failed");
+
+ status = r.out.result;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ test_DeleteTrustedDomain(b, tctx, handle, trustinfo.netbios_name);
+ if (ex2_call) {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_CreateTrustedDomainEx2_r(b, tctx, &r2),
+ "CreateTrustedDomainEx2 failed");
+ status = r2.out.result;
+ } else {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_CreateTrustedDomainEx_r(b, tctx, &r),
+ "CreateTrustedDomainEx2 failed");
+ status = r.out.result;
+ }
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "CreateTrustedDomainEx failed2 - %s\n", nt_errstr(status));
+ ret = false;
+ } else {
+ /* For outbound and MIT trusts there is no trust account */
+ if (trustinfo.trust_direction != 2 &&
+ trustinfo.trust_type != 3) {
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_comment(tctx, "skipping trusted domain auth tests against samba3\n");
+ } else if (ex2_call == false &&
+ torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "skipping CreateTrustedDomainEx trusted domain auth tests against samba4\n");
+
+ } else {
+ ok = check_dom_trust_pw(p, tctx,
+ our_info->dns.name.string,
+ our_info->dns.dns_domain.string,
+ SEC_CHAN_DOMAIN,
+ &trustinfo,
+ NULL,
+ "x" TRUSTPW "x", 0,
+ NULL, 0,
+ false);
+ if (!ok) {
+ torture_comment(tctx, "Password check passed unexpectedly\n");
+ ret = false;
+ }
+ ok = check_dom_trust_pw(p, tctx,
+ our_info->dns.name.string,
+ our_info->dns.dns_domain.string,
+ SEC_CHAN_DOMAIN,
+ &trustinfo,
+ incoming_v00,
+ incoming_v0, 0,
+ incoming_v1, 1,
+ true);
+ if (!ok) {
+ torture_comment(tctx, "Password check failed (SEC_CHAN_DOMAIN)\n");
+ ret = false;
+ }
+ ok = check_dom_trust_pw(p, tctx,
+ our_info->dns.name.string,
+ our_info->dns.dns_domain.string,
+ SEC_CHAN_DNS_DOMAIN,
+ &trustinfo,
+ incoming_v0,
+ incoming_v1, 1,
+ incoming_v2, 2,
+ true);
+ if (!ok) {
+ torture_comment(tctx, "Password check failed v2 (SEC_CHAN_DNS_DOMAIN)\n");
+ ret = false;
+ }
+ ok = check_dom_trust_pw(p, tctx,
+ our_info->dns.name.string,
+ our_info->dns.dns_domain.string,
+ SEC_CHAN_DNS_DOMAIN,
+ &trustinfo,
+ incoming_v1,
+ incoming_v2, 2,
+ incoming_v40, 40,
+ true);
+ if (!ok) {
+ torture_comment(tctx, "Password check failed v4 (SEC_CHAN_DNS_DOMAIN)\n");
+ ret = false;
+ }
+ }
+ }
+
+ q.in.trustdom_handle = &trustdom_handle[i];
+ q.in.level = LSA_TRUSTED_DOMAIN_INFO_INFO_EX;
+ q.out.info = &info;
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfo_r(b, tctx, &q),
+ "QueryTrustedDomainInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_comment(tctx, "QueryTrustedDomainInfo level 1 failed - %s\n", nt_errstr(q.out.result));
+ ret = false;
+ } else if (!q.out.info) {
+ torture_comment(tctx, "QueryTrustedDomainInfo level 1 failed to return an info pointer\n");
+ ret = false;
+ } else {
+ if (strcmp(info->info_ex.domain_name.string, trustinfo.domain_name.string) != 0) {
+ torture_comment(tctx, "QueryTrustedDomainInfo returned inconsistent long name: %s != %s\n",
+ info->info_ex.domain_name.string, trustinfo.domain_name.string);
+ ret = false;
+ }
+ if (strcmp(info->info_ex.netbios_name.string, trustinfo.netbios_name.string) != 0) {
+ torture_comment(tctx, "QueryTrustedDomainInfo returned inconsistent short name: %s != %s\n",
+ info->info_ex.netbios_name.string, trustinfo.netbios_name.string);
+ ret = false;
+ }
+ if (info->info_ex.trust_type != trustinfo.trust_type) {
+ torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust type %d != %d\n",
+ trust_name, info->info_ex.trust_type, trustinfo.trust_type);
+ ret = false;
+ }
+ if (info->info_ex.trust_attributes != LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION) {
+ torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust attributes %d != %d\n",
+ trust_name, info->info_ex.trust_attributes, LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION);
+ ret = false;
+ }
+ if (info->info_ex.trust_direction != trustinfo.trust_direction) {
+ torture_comment(tctx, "QueryTrustedDomainInfo of %s returned incorrect trust direction %d != %d\n",
+ trust_name, 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(b, tctx, handle)) {
+ torture_comment(tctx, "test_EnumTrustDom failed\n");
+ ret = false;
+ }
+
+ if (!test_EnumTrustDomEx(b, tctx, handle)) {
+ torture_comment(tctx, "test_EnumTrustDomEx failed\n");
+ ret = false;
+ }
+
+ for (i=0; i<num_trusts; i++) {
+ if (!test_DeleteTrustedDomainBySid(b, tctx, handle, domsid[i])) {
+ torture_comment(tctx, "test_DeleteTrustedDomainBySid failed\n");
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_CreateTrustedDomainEx2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t num_trusts)
+{
+ return test_CreateTrustedDomainEx_common(p, tctx, handle, num_trusts, true);
+}
+
+static bool test_CreateTrustedDomainEx(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t num_trusts)
+{
+ return test_CreateTrustedDomainEx_common(p, tctx, handle, num_trusts, false);
+}
+
+static bool test_QueryDomainInfoPolicy(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_QueryDomainInformationPolicy r;
+ union lsa_DomainInformationPolicy *info = NULL;
+ int i;
+ bool ret = true;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping QueryDomainInformationPolicy test\n");
+ }
+
+ torture_comment(tctx, "\nTesting QueryDomainInformationPolicy\n");
+
+ for (i=2;i<4;i++) {
+ r.in.handle = handle;
+ r.in.level = i;
+ r.out.info = &info;
+
+ torture_comment(tctx, "\nTrying QueryDomainInformationPolicy level %d\n", i);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryDomainInformationPolicy_r(b, tctx, &r),
+ "QueryDomainInformationPolicy failed");
+
+ /* If the server does not support EFS, then this is the correct return */
+ if (i == LSA_DOMAIN_INFO_POLICY_EFS && NT_STATUS_EQUAL(r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ continue;
+ } else if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "QueryDomainInformationPolicy failed - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_QueryInfoPolicyCalls( bool version2,
+ struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_QueryInfoPolicy r;
+ union lsa_PolicyInformation *info = NULL;
+ int i;
+ bool ret = true;
+ const char *call = talloc_asprintf(tctx, "QueryInfoPolicy%s", version2 ? "2":"");
+
+ torture_comment(tctx, "\nTesting %s\n", call);
+
+ if (version2 && torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping QueryInfoPolicy2 tests\n");
+ }
+
+ for (i=1;i<=14;i++) {
+ r.in.handle = handle;
+ r.in.level = i;
+ r.out.info = &info;
+
+ torture_comment(tctx, "\nTrying %s level %d\n", call, i);
+
+ if (version2)
+ /* We can perform the cast, because both types are
+ structurally equal */
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryInfoPolicy2_r(b, tctx,
+ (struct lsa_QueryInfoPolicy2*) &r),
+ "QueryInfoPolicy2 failed");
+ else
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryInfoPolicy_r(b, tctx, &r),
+ "QueryInfoPolicy2 failed");
+
+ switch (i) {
+ case LSA_POLICY_INFO_MOD:
+ case LSA_POLICY_INFO_AUDIT_FULL_SET:
+ case LSA_POLICY_INFO_AUDIT_FULL_QUERY:
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ torture_comment(tctx, "Server should have failed level %u: %s\n", i, nt_errstr(r.out.result));
+ ret = false;
+ }
+ break;
+ case LSA_POLICY_INFO_DOMAIN:
+ case LSA_POLICY_INFO_ACCOUNT_DOMAIN:
+ case LSA_POLICY_INFO_REPLICA:
+ case LSA_POLICY_INFO_QUOTA:
+ case LSA_POLICY_INFO_ROLE:
+ case LSA_POLICY_INFO_AUDIT_LOG:
+ case LSA_POLICY_INFO_AUDIT_EVENTS:
+ case LSA_POLICY_INFO_PD:
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "%s failed - %s\n", call, nt_errstr(r.out.result));
+ ret = false;
+ }
+ break;
+ case LSA_POLICY_INFO_L_ACCOUNT_DOMAIN:
+ case LSA_POLICY_INFO_DNS_INT:
+ case LSA_POLICY_INFO_DNS:
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ /* Other levels not implemented yet */
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) {
+ torture_comment(tctx, "%s failed - %s\n", call, nt_errstr(r.out.result));
+ ret = false;
+ }
+ } else if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "%s failed - %s\n", call, nt_errstr(r.out.result));
+ ret = false;
+ }
+ break;
+ default:
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ /* Other levels not implemented yet */
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) {
+ torture_comment(tctx, "%s failed - %s\n", call, nt_errstr(r.out.result));
+ ret = false;
+ }
+ } else if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "%s failed - %s\n", call, nt_errstr(r.out.result));
+ ret = false;
+ }
+ break;
+ }
+
+ if (NT_STATUS_IS_OK(r.out.result) && (i == LSA_POLICY_INFO_DNS
+ || i == LSA_POLICY_INFO_DNS_INT)) {
+ /* Let's look up some of these names */
+
+ struct lsa_TransNameArray tnames, dnames;
+ tnames.count = 14;
+ tnames.names = talloc_zero_array(tctx, struct lsa_TranslatedName, tnames.count);
+ tnames.names[0].name.string = info->dns.name.string;
+ tnames.names[0].sid_type = SID_NAME_DOMAIN;
+ tnames.names[1].name.string = info->dns.dns_domain.string;
+ tnames.names[1].sid_type = SID_NAME_DOMAIN;
+ tnames.names[2].name.string = talloc_asprintf(tctx, "%s\\", info->dns.name.string);
+ tnames.names[2].sid_type = SID_NAME_DOMAIN;
+ tnames.names[3].name.string = talloc_asprintf(tctx, "%s\\", info->dns.dns_domain.string);
+ tnames.names[3].sid_type = SID_NAME_DOMAIN;
+ tnames.names[4].name.string = talloc_asprintf(tctx, "%s\\guest", info->dns.name.string);
+ tnames.names[4].sid_type = SID_NAME_USER;
+ tnames.names[5].name.string = talloc_asprintf(tctx, "%s\\krbtgt", info->dns.name.string);
+ tnames.names[5].sid_type = SID_NAME_USER;
+ tnames.names[6].name.string = talloc_asprintf(tctx, "%s\\guest", info->dns.dns_domain.string);
+ tnames.names[6].sid_type = SID_NAME_USER;
+ tnames.names[7].name.string = talloc_asprintf(tctx, "%s\\krbtgt", info->dns.dns_domain.string);
+ tnames.names[7].sid_type = SID_NAME_USER;
+ tnames.names[8].name.string = talloc_asprintf(tctx, "krbtgt@%s", info->dns.name.string);
+ tnames.names[8].sid_type = SID_NAME_USER;
+ tnames.names[9].name.string = talloc_asprintf(tctx, "krbtgt@%s", info->dns.dns_domain.string);
+ tnames.names[9].sid_type = SID_NAME_USER;
+ tnames.names[10].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", info->dns.name.string);
+ tnames.names[10].sid_type = SID_NAME_USER;
+ tnames.names[11].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", info->dns.dns_domain.string);
+ tnames.names[11].sid_type = SID_NAME_USER;
+ tnames.names[12].name.string = talloc_asprintf(tctx, TEST_MACHINENAME "$@%s", info->dns.name.string);
+ tnames.names[12].sid_type = SID_NAME_USER;
+ tnames.names[13].name.string = talloc_asprintf(tctx, TEST_MACHINENAME "$@%s", info->dns.dns_domain.string);
+ tnames.names[13].sid_type = SID_NAME_USER;
+ ret &= test_LookupNames(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &tnames);
+
+ /* Try to use in-forest search for the test machine */
+ dnames.count = 1;
+ dnames.names = talloc_zero_array(tctx, struct lsa_TranslatedName, dnames.count);
+ dnames.names[0].name.string = talloc_asprintf(tctx, "%s\\"TEST_MACHINENAME "$", info->dns.name.string);
+ dnames.names[0].sid_type = SID_NAME_USER;
+ ret &= test_LookupNames(b, tctx, handle, LSA_LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2, &dnames);
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryInfoPolicy(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ return test_QueryInfoPolicyCalls(false, b, tctx, handle);
+}
+
+static bool test_QueryInfoPolicy2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ return test_QueryInfoPolicyCalls(true, b, tctx, handle);
+}
+
+static bool test_GetUserName(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx)
+{
+ struct lsa_GetUserName r;
+ struct lsa_String *authority_name_p = NULL;
+ struct lsa_String *account_name_p = NULL;
+
+ torture_comment(tctx, "\nTesting GetUserName\n");
+
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = NULL;
+ r.out.account_name = &account_name_p;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetUserName_r(b, tctx, &r),
+ "GetUserName failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "GetUserName result failed");
+ torture_assert_not_null(tctx, r.out.account_name, "r.out.account_name");
+ torture_assert_not_null(tctx, *r.out.account_name, "*r.out.account_name");
+ torture_assert(tctx, r.out.authority_name == NULL, "r.out.authority_name");
+
+ account_name_p = NULL;
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetUserName_r(b, tctx, &r),
+ "GetUserName failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "GetUserName result failed");
+ torture_assert_not_null(tctx, r.out.account_name, "r.out.account_name");
+ torture_assert_not_null(tctx, *r.out.account_name, "*r.out.account_name");
+ torture_assert_not_null(tctx, r.out.authority_name, "r.out.authority_name");
+ torture_assert_not_null(tctx, *r.out.authority_name, "*r.out.authority_name");
+
+ torture_comment(tctx,
+ "Account Name: %s, Authority Name: %s\n",
+ (*r.out.account_name)->string,
+ (*r.out.authority_name)->string);
+
+ return true;
+}
+
+static bool test_GetUserName_fail(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx)
+{
+ struct lsa_GetUserName r;
+ struct lsa_String *account_name_p = NULL;
+ NTSTATUS status;
+
+ torture_comment(tctx, "\nTesting GetUserName_fail\n");
+
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = NULL;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx,
+ "GetUserName correctly returned with "
+ "status: %s\n",
+ nt_errstr(status));
+ return true;
+ }
+
+ torture_assert_ntstatus_equal(tctx,
+ status,
+ NT_STATUS_ACCESS_DENIED,
+ "GetUserName return value should "
+ "be ACCESS_DENIED");
+ return true;
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) ||
+ NT_STATUS_EQUAL(r.out.result, NT_STATUS_RPC_PROTSEQ_NOT_SUPPORTED)) {
+ torture_comment(tctx,
+ "GetUserName correctly returned with "
+ "result: %s\n",
+ nt_errstr(r.out.result));
+ return true;
+ }
+ }
+
+ torture_assert_ntstatus_equal(tctx,
+ r.out.result,
+ NT_STATUS_OK,
+ "GetUserName return value should be "
+ "ACCESS_DENIED");
+
+ return false;
+}
+
+bool test_lsa_Close(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct lsa_Close r;
+ struct policy_handle handle2;
+
+ torture_comment(tctx, "\nTesting Close\n");
+
+ r.in.handle = handle;
+ r.out.handle = &handle2;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Close_r(b, tctx, &r),
+ "Close failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Close failed");
+
+ torture_assert_ntstatus_equal(tctx, dcerpc_lsa_Close_r(b, tctx, &r),
+ NT_STATUS_RPC_SS_CONTEXT_MISMATCH, "Close should failed");
+
+ torture_comment(tctx, "\n");
+
+ return true;
+}
+
+bool torture_rpc_lsa(struct torture_context *tctx)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct policy_handle *handle = NULL;
+ struct test_join *join = NULL;
+ struct cli_credentials *machine_creds;
+ struct dcerpc_binding_handle *b;
+ enum dcerpc_transport_t transport;
+
+ status = torture_rpc_connection(tctx, &p, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ b = p->binding_handle;
+ transport = dcerpc_binding_get_transport(p->binding);
+
+ /* Test lsaLookupSids3 and lsaLookupNames4 over tcpip */
+ if (transport == NCACN_IP_TCP) {
+ if (!test_OpenPolicy_fail(b, tctx)) {
+ ret = false;
+ }
+
+ if (!test_OpenPolicy2_fail(b, tctx)) {
+ ret = false;
+ }
+
+ if (!test_many_LookupSids(p, tctx, handle, LSA_LOOKUP_NAMES_ALL)) {
+ ret = false;
+ }
+
+ return ret;
+ }
+
+ if (!test_OpenPolicy(b, tctx)) {
+ ret = false;
+ }
+
+ if (!test_lsa_OpenPolicy2(b, tctx, &handle)) {
+ ret = false;
+ }
+
+ if (handle) {
+ join = torture_join_domain(tctx, TEST_MACHINENAME, ACB_WSTRUST, &machine_creds);
+ if (!join) {
+ ret = false;
+ }
+
+ if (!test_LookupSids_async(b, tctx, handle, LSA_LOOKUP_NAMES_ALL)) {
+ ret = false;
+ }
+
+ if (!test_QueryDomainInfoPolicy(b, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_CreateSecret(p, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryInfoPolicy(b, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryInfoPolicy2(b, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_Delete(b, tctx, handle)) {
+ ret = false;
+ }
+
+ if (!test_many_LookupSids(p, tctx, handle, LSA_LOOKUP_NAMES_ALL)) {
+ ret = false;
+ }
+
+ if (!test_lsa_Close(b, tctx, handle)) {
+ ret = false;
+ }
+
+ torture_leave_domain(tctx, join);
+
+ } else {
+ if (!test_many_LookupSids(p, tctx, handle, LSA_LOOKUP_NAMES_ALL)) {
+ ret = false;
+ }
+ }
+
+ if (!test_GetUserName(b, tctx)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool torture_rpc_lsa_get_user(struct torture_context *tctx)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct dcerpc_binding_handle *b;
+ enum dcerpc_transport_t transport;
+
+ status = torture_rpc_connection(tctx, &p, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ b = p->binding_handle;
+ transport = dcerpc_binding_get_transport(p->binding);
+
+ if (transport == NCACN_IP_TCP) {
+ if (!test_GetUserName_fail(b, tctx)) {
+ ret = false;
+ }
+ return ret;
+ }
+
+ if (!test_GetUserName(b, tctx)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool testcase_LookupNames(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ bool ret = true;
+ struct policy_handle *handle;
+ struct lsa_TransNameArray tnames;
+ struct lsa_TransNameArray2 tnames2;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding);
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ torture_comment(tctx, "testcase_LookupNames is only available "
+ "over NCACN_NP or NCALRPC");
+ return true;
+ }
+
+ if (!test_OpenPolicy(b, tctx)) {
+ ret = false;
+ }
+
+ if (!test_lsa_OpenPolicy2(b, tctx, &handle)) {
+ ret = false;
+ }
+
+ if (!handle) {
+ ret = false;
+ }
+
+ tnames.count = 1;
+ tnames.names = talloc_array(tctx, struct lsa_TranslatedName, tnames.count);
+ ZERO_STRUCT(tnames.names[0]);
+ tnames.names[0].name.string = "BUILTIN";
+ tnames.names[0].sid_type = SID_NAME_DOMAIN;
+
+ if (!test_LookupNames(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &tnames)) {
+ ret = false;
+ }
+
+ tnames2.count = 1;
+ tnames2.names = talloc_array(tctx, struct lsa_TranslatedName2, tnames2.count);
+ ZERO_STRUCT(tnames2.names[0]);
+ tnames2.names[0].name.string = "BUILTIN";
+ tnames2.names[0].sid_type = SID_NAME_DOMAIN;
+
+ if (!test_LookupNames2(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &tnames2, true)) {
+ ret = false;
+ }
+
+ if (!test_LookupNames3(b, tctx, handle, LSA_LOOKUP_NAMES_ALL, &tnames2, true)) {
+ ret = false;
+ }
+
+ if (!test_LookupNames_wellknown(b, tctx, handle, LSA_LOOKUP_NAMES_ALL)) {
+ ret = false;
+ }
+
+ if (!test_LookupNames_NULL(b, tctx, handle, LSA_LOOKUP_NAMES_ALL)) {
+ ret = false;
+ }
+
+ if (!test_LookupNames_bogus(b, tctx, handle, LSA_LOOKUP_NAMES_ALL)) {
+ ret = false;
+ }
+
+ if (!test_lsa_Close(b, tctx, handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_lsa_lookup_names(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+ struct torture_rpc_tcase *tcase;
+
+ suite = torture_suite_create(mem_ctx, "lsa.lookupnames");
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa",
+ &ndr_table_lsarpc);
+ torture_rpc_tcase_add_test(tcase, "LookupNames",
+ testcase_LookupNames);
+
+ return suite;
+}
+
+struct lsa_trustdom_state {
+ uint32_t num_trusts;
+};
+
+static bool testcase_TrustedDomains(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ void *data)
+{
+ bool ret = true;
+ struct policy_handle *handle;
+ struct lsa_trustdom_state *state =
+ talloc_get_type_abort(data, struct lsa_trustdom_state);
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding);
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ torture_comment(tctx, "testcase_TrustedDomains is only available "
+ "over NCACN_NP or NCALRPC");
+ return true;
+ }
+
+ torture_comment(tctx, "Testing %d domains\n", state->num_trusts);
+
+ if (!test_OpenPolicy(b, tctx)) {
+ ret = false;
+ }
+
+ if (!test_lsa_OpenPolicy2(b, tctx, &handle)) {
+ ret = false;
+ }
+
+ if (!handle) {
+ ret = false;
+ }
+
+ if (!test_CreateTrustedDomain(b, tctx, handle, state->num_trusts)) {
+ ret = false;
+ }
+
+ if (!test_CreateTrustedDomainEx(p, tctx, handle, state->num_trusts)) {
+ ret = false;
+ }
+
+ if (!test_CreateTrustedDomainEx2(p, tctx, handle, state->num_trusts)) {
+ ret = false;
+ }
+
+ if (!test_lsa_Close(b, tctx, handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_lsa_trusted_domains(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+ struct torture_rpc_tcase *tcase;
+ struct lsa_trustdom_state *state;
+
+ state = talloc(mem_ctx, struct lsa_trustdom_state);
+
+ state->num_trusts = 12;
+
+ suite = torture_suite_create(mem_ctx, "lsa.trusted.domains");
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa",
+ &ndr_table_lsarpc);
+ torture_rpc_tcase_add_test_ex(tcase, "TrustedDomains",
+ testcase_TrustedDomains,
+ state);
+
+ return suite;
+}
+
+static bool testcase_Privileges(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct policy_handle *handle;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding);
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ torture_skip(tctx, "testcase_Privileges is only available "
+ "over NCACN_NP or NCALRPC");
+ }
+
+ if (!test_OpenPolicy(b, tctx)) {
+ return false;
+ }
+
+ if (!test_lsa_OpenPolicy2(b, tctx, &handle)) {
+ return false;
+ }
+
+ if (!handle) {
+ return false;
+ }
+
+ if (!test_CreateAccount(b, tctx, handle)) {
+ return false;
+ }
+
+ if (!test_EnumAccounts(b, tctx, handle)) {
+ return false;
+ }
+
+ if (!test_EnumPrivs(b, tctx, handle)) {
+ return false;
+ }
+
+ if (!test_lsa_Close(b, tctx, handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+struct torture_suite *torture_rpc_lsa_privileges(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+ struct torture_rpc_tcase *tcase;
+
+ suite = torture_suite_create(mem_ctx, "lsa.privileges");
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa",
+ &ndr_table_lsarpc);
+ torture_rpc_tcase_add_test(tcase, "Privileges",
+ testcase_Privileges);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/lsa_lookup.c b/source4/torture/rpc/lsa_lookup.c
new file mode 100644
index 0000000..f641827
--- /dev/null
+++ b/source4/torture/rpc/lsa_lookup.c
@@ -0,0 +1,428 @@
+/*
+ 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/rpc/torture_rpc.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "libcli/security/security.h"
+
+static bool open_policy(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle **handle)
+{
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+
+ *handle = talloc(tctx, 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenPolicy2_r(b, tctx, &r),
+ "OpenPolicy2 failed");
+
+ return NT_STATUS_IS_OK(r.out.result);
+}
+
+static bool get_domainsid(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ struct dom_sid **sid)
+{
+ struct lsa_QueryInfoPolicy r;
+ union lsa_PolicyInformation *info = NULL;
+
+ r.in.level = LSA_POLICY_INFO_DOMAIN;
+ r.in.handle = handle;
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryInfoPolicy_r(b, tctx, &r),
+ "QueryInfoPolicy failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "QueryInfoPolicy failed");
+
+ *sid = info->domain.sid;
+ return true;
+}
+
+static NTSTATUS lookup_sids(struct torture_context *tctx,
+ uint16_t level,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ struct dom_sid **sids, uint32_t num_sids,
+ struct lsa_TransNameArray *names)
+{
+ struct lsa_LookupSids r;
+ struct lsa_SidArray sidarray;
+ struct lsa_RefDomainList *domains;
+ uint32_t count = 0;
+ uint32_t i;
+ NTSTATUS status;
+
+ names->count = 0;
+ names->names = NULL;
+
+ sidarray.num_sids = num_sids;
+ sidarray.sids = talloc_array(tctx, 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;
+ r.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupSids_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return r.out.result;
+}
+
+static bool test_lookupsids(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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(tctx, level, b, 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(tctx, 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_binding_handle *b,
+ struct policy_handle *handle,
+ struct dom_sid **sid)
+{
+ struct lsa_EnumTrustDom r;
+ uint32_t resume_handle = 0;
+ struct lsa_DomainList domains;
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustDom_r(b, tctx, &r),
+ "EnumTrustDom failed");
+
+ if (NT_STATUS_EQUAL(r.out.result, 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;
+ union lsa_TrustedDomainInfo *info = NULL;
+
+ if (domains.domains[i].sid == NULL)
+ continue;
+
+ q.in.handle = handle;
+ q.in.dom_sid = domains.domains[i].sid;
+ q.in.level = 6;
+ q.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QueryTrustedDomainInfoBySid_r(b, tctx, &q),
+ "QueryTrustedDomainInfoBySid failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) continue;
+
+ if ((info->info_ex.trust_direction & 2) &&
+ (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 = NULL;
+ struct dom_sid *trusted_sid = NULL;
+ struct dom_sid *sids[NUM_SIDS];
+ struct dcerpc_binding_handle *b;
+ enum dcerpc_transport_t transport;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_fail(torture, "unable to connect to table");
+ }
+ b = p->binding_handle;
+ transport = dcerpc_binding_get_transport(p->binding);
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ torture_comment(torture,
+ "torture_rpc_lsa_lookup is only available "
+ "over NCACN_NP or NCALRPC");
+ return true;
+ }
+
+ ret &= open_policy(torture, b, &handle);
+ if (!ret) return false;
+
+ ret &= get_domainsid(torture, b, handle, &dom_sid);
+ if (!ret) return false;
+
+ ret &= get_downleveltrust(torture, b, 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, b, 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, b, 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, b, 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, b, 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, b, handle, sids, NUM_SIDS, 4,
+ STATUS_SOME_UNMAPPED, types);
+ }
+
+ ret &= test_lookupsids(torture, b, 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, b, handle, sids, NUM_SIDS, 6,
+ STATUS_SOME_UNMAPPED, types);
+ }
+
+ ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 7,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+ ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 8,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+ ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 9,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+ ret &= test_lookupsids(torture, b, handle, sids, NUM_SIDS, 10,
+ NT_STATUS_INVALID_PARAMETER, NULL);
+
+ return ret;
+}
+
+static bool test_LookupSidsReply(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct policy_handle *handle = NULL;
+
+ struct dom_sid **sids = NULL;
+ uint32_t num_sids = 1;
+
+ struct lsa_LookupSids r;
+ struct lsa_SidArray sidarray;
+ struct lsa_RefDomainList *domains = NULL;
+ struct lsa_TransNameArray names;
+ uint32_t count = 0;
+
+ uint32_t i;
+ const char *dom_sid = "S-1-5-21-1111111111-2222222222-3333333333";
+ const char *dom_admin_sid;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ enum dcerpc_transport_t transport = dcerpc_binding_get_transport(p->binding);
+
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(sidarray);
+ ZERO_STRUCT(names);
+
+ if (transport != NCACN_NP && transport != NCALRPC) {
+ torture_comment(tctx,
+ "test_LookupSidsReply is only available "
+ "over NCACN_NP or NCALRPC");
+ return true;
+ }
+
+ if (!open_policy(tctx, b, &handle)) {
+ return false;
+ }
+
+ dom_admin_sid = talloc_asprintf(tctx, "%s-%d", dom_sid, 512);
+
+ sids = talloc_zero_array(tctx, struct dom_sid *, num_sids);
+
+ sids[0] = dom_sid_parse_talloc(tctx, dom_admin_sid);
+
+ names.count = 0;
+ names.names = NULL;
+
+ sidarray.num_sids = num_sids;
+ sidarray.sids = talloc_zero_array(tctx, 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 = LSA_LOOKUP_NAMES_ALL;
+ r.in.count = &count;
+ r.out.names = &names;
+ r.out.count = &count;
+ r.out.domains = &domains;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_LookupSids_r(b, tctx, &r),
+ "LookupSids failed");
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NONE_MAPPED,
+ "unexpected error code");
+
+ torture_assert_int_equal(tctx, names.count, num_sids,
+ "unexpected names count");
+ torture_assert(tctx, names.names,
+ "unexpected names pointer");
+ torture_assert_str_equal(tctx, names.names[0].name.string, dom_admin_sid,
+ "unexpected names[0].string");
+
+#if 0
+ /* vista sp1 passes, w2k3 sp2 fails */
+ torture_assert_int_equal(tctx, domains->count, num_sids,
+ "unexpected domains count");
+ torture_assert(tctx, domains->domains,
+ "unexpected domains pointer");
+ torture_assert_str_equal(tctx, dom_sid_string(tctx, domains->domains[0].sid), dom_sid,
+ "unexpected domain sid");
+#endif
+
+ return true;
+}
+
+/* check for lookup sids results */
+struct torture_suite *torture_rpc_lsa_lookup_sids(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+ struct torture_rpc_tcase *tcase;
+
+ suite = torture_suite_create(mem_ctx, "lsa.lookupsids");
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "lsa",
+ &ndr_table_lsarpc);
+
+ torture_rpc_tcase_add_test(tcase, "LookupSidsReply", test_LookupSidsReply);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/mdssvc.c b/source4/torture/rpc/mdssvc.c
new file mode 100644
index 0000000..afe7068
--- /dev/null
+++ b/source4/torture/rpc/mdssvc.c
@@ -0,0 +1,1040 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for the mdssvc RPC serice
+
+ Copyright (C) Ralph Boehme 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "torture/rpc/torture_rpc.h"
+#include "librpc/gen_ndr/ndr_mdssvc_c.h"
+#include "param/param.h"
+#include "lib/cmdline/cmdline.h"
+#include "rpc_server/mdssvc/dalloc.h"
+#include "rpc_server/mdssvc/marshalling.h"
+
+struct torture_mdsscv_state {
+ struct dcerpc_pipe *p;
+ struct policy_handle ph;
+
+ /* Known fields used across multiple commands */
+ uint32_t dev;
+ uint32_t flags;
+
+ /* cmd specific or unknown fields */
+ struct {
+ const char share_path[1025];
+ uint32_t unkn2;
+ uint32_t unkn3;
+ } mdscmd_open;
+ struct {
+ uint32_t status;
+ uint32_t unkn7;
+ } mdscmd_unknown1;
+ struct {
+ uint32_t fragment;
+ uint32_t unkn9;
+ } mdscmd_cmd;
+ struct {
+ uint32_t status;
+ } mdscmd_close;
+};
+
+static bool torture_rpc_mdssvc_setup(struct torture_context *tctx,
+ void **data)
+{
+ struct torture_mdsscv_state *state = NULL;
+ NTSTATUS status;
+
+ state = talloc_zero(tctx, struct torture_mdsscv_state);
+ if (state == NULL) {
+ return false;
+ }
+ *data = state;
+
+ status = torture_rpc_connection(tctx, &state->p, &ndr_table_mdssvc);
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ return true;
+}
+
+static bool torture_rpc_mdssvc_teardown(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+
+ TALLOC_FREE(state->p);
+ TALLOC_FREE(state);
+ return true;
+}
+
+static bool torture_rpc_mdssvc_open(struct torture_context *tctx,
+ void **data)
+{
+ struct torture_mdsscv_state *state = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ const char *share_name = NULL;
+ const char *share_mount_path = NULL;
+ NTSTATUS status;
+ bool ok = true;
+
+ state = talloc_zero(tctx, struct torture_mdsscv_state);
+ if (state == NULL) {
+ return false;
+ }
+ *data = state;
+
+ status = torture_rpc_connection(tctx, &state->p, &ndr_table_mdssvc);
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+ b = state->p->binding_handle;
+
+ share_name = torture_setting_string(
+ tctx, "spotlight_share", "spotlight");
+ share_mount_path = torture_setting_string(
+ tctx, "share_mount_path", "/foo/bar");
+
+ state->dev = generate_random();
+ state->mdscmd_open.unkn2 = 23;
+ state->mdscmd_open.unkn3 = 0;
+
+ ZERO_STRUCT(state->ph);
+
+ status = dcerpc_mdssvc_open(b,
+ state,
+ &state->dev,
+ &state->mdscmd_open.unkn2,
+ &state->mdscmd_open.unkn3,
+ share_mount_path,
+ share_name,
+ state->mdscmd_open.share_path,
+ &state->ph);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "dcerpc_mdssvc_open failed\n");
+
+ status = dcerpc_mdssvc_unknown1(b,
+ state,
+ &state->ph,
+ 0,
+ state->dev,
+ state->mdscmd_open.unkn2,
+ 0,
+ geteuid(),
+ getegid(),
+ &state->mdscmd_unknown1.status,
+ &state->flags,
+ &state->mdscmd_unknown1.unkn7);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "dcerpc_mdssvc_unknown1 failed\n");
+
+done:
+ if (!ok) {
+ (void)dcerpc_mdssvc_close(b,
+ state,
+ &state->ph,
+ 0,
+ state->dev,
+ state->mdscmd_open.unkn2,
+ 0,
+ &state->ph,
+ &state->mdscmd_close.status);
+ ZERO_STRUCT(state);
+ }
+ return ok;
+}
+
+static bool torture_rpc_mdssvc_close(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ NTSTATUS status;
+ bool ok = true;
+
+ torture_comment(tctx, "test_teardown_mdssvc_disconnect\n");
+
+ status = dcerpc_mdssvc_close(b,
+ state,
+ &state->ph,
+ 0,
+ state->dev,
+ state->mdscmd_open.unkn2,
+ 0,
+ &state->ph,
+ &state->mdscmd_close.status);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "dcerpc_mdssvc_close failed\n");
+
+ ZERO_STRUCT(state);
+
+done:
+ return ok;
+}
+
+/*
+ * Test unknown share name
+ */
+static bool test_mdssvc_open_unknown_share(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ struct policy_handle ph;
+ struct policy_handle nullh;
+ uint32_t device_id;
+ uint32_t unkn2;
+ uint32_t unkn3;
+ uint32_t device_id_out;
+ uint32_t unkn2_out;
+ uint32_t unkn3_out;
+ const char *share_mount_path = NULL;
+ const char *share_name = NULL;
+ const char share_path[1025] = "X";
+ NTSTATUS status;
+ bool ok = true;
+
+ share_name = torture_setting_string(
+ tctx, "unknown_share", "choukawoohoo");
+ share_mount_path = torture_setting_string(
+ tctx, "share_mount_path", "/foo/bar");
+
+ device_id_out = device_id = generate_random();
+ unkn2_out = unkn2 = generate_random();
+ unkn3_out = unkn3 = generate_random();
+
+ ZERO_STRUCT(ph);
+ ZERO_STRUCT(nullh);
+
+ status = dcerpc_mdssvc_open(b,
+ tctx,
+ &device_id_out,
+ &unkn2_out,
+ &unkn3_out,
+ share_mount_path,
+ share_name,
+ share_path,
+ &ph);
+
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "dcerpc_mdssvc_open failed\n");
+
+ torture_assert_u32_equal_goto(tctx, device_id_out, device_id, ok, done,
+ "Bad device_id\n");
+
+ torture_assert_u32_equal_goto(tctx, unkn2_out, unkn2, ok, done,
+ "Bad unkn2\n");
+
+ torture_assert_u32_equal_goto(tctx, unkn3_out, unkn3, ok, done,
+ "Bad unkn3\n");
+
+ torture_assert_goto(tctx, share_path[0] == '\0', ok, done,
+ "Expected empty string as share path\n");
+
+ torture_assert_mem_equal_goto(tctx, &ph, &nullh,
+ sizeof(ph), ok, done,
+ "Expected all-zero policy handle\n");
+
+done:
+ return ok;
+}
+
+/*
+ * Test on a share where Spotlight is not enabled
+ */
+static bool test_mdssvc_open_spotlight_disabled(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ struct policy_handle ph;
+ struct policy_handle nullh;
+ uint32_t device_id;
+ uint32_t unkn2;
+ uint32_t unkn3;
+ uint32_t device_id_out;
+ uint32_t unkn2_out;
+ uint32_t unkn3_out;
+ const char *share_mount_path = NULL;
+ const char *share_name = NULL;
+ const char share_path[1025] = "";
+ NTSTATUS status;
+ bool ok = true;
+
+ share_name = torture_setting_string(
+ tctx, "no_spotlight_share", "no_spotlight");
+ share_mount_path = torture_setting_string(
+ tctx, "share_mount_path", "/foo/bar");
+
+ device_id_out = device_id = generate_random();
+ unkn2_out = unkn2 = 23;
+ unkn3_out = unkn3 = 0;
+
+ ZERO_STRUCT(ph);
+ ZERO_STRUCT(nullh);
+
+ status = dcerpc_mdssvc_open(b,
+ tctx,
+ &device_id_out,
+ &unkn2_out,
+ &unkn3_out,
+ share_mount_path,
+ share_name,
+ share_path,
+ &ph);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "dcerpc_mdssvc_open failed\n");
+
+ torture_assert_u32_equal_goto(tctx, device_id, device_id_out, ok, done,
+ "Bad device_id\n");
+
+ torture_assert_u32_equal_goto(tctx, unkn2, unkn2_out,
+ ok, done, "Bad unkn2\n");
+
+ torture_assert_u32_equal_goto(tctx, unkn3, unkn3_out,
+ ok, done, "Bad unkn3\n");
+
+ torture_assert_goto(tctx, share_path[0] == '\0', ok, done,
+ "Expected empty string as share path\n");
+
+ torture_assert_mem_equal_goto(tctx, &ph, &nullh,
+ sizeof(ph), ok, done,
+ "Expected all-zero policy handle\n");
+
+done:
+ return ok;
+}
+
+static bool test_mdssvc_close(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ struct policy_handle ph;
+ struct policy_handle close_ph;
+ uint32_t device_id;
+ uint32_t unkn2;
+ uint32_t unkn3;
+ const char *share_mount_path = NULL;
+ const char *share_name = NULL;
+ const char share_path[1025] = "";
+ uint32_t close_status;
+ DATA_BLOB ph_blob;
+ DATA_BLOB close_ph_blob;
+ NTSTATUS status;
+ bool ok = true;
+
+ share_name = torture_setting_string(
+ tctx, "spotlight_share", "spotlight");
+ share_mount_path = torture_setting_string(
+ tctx, "share_mount_path", "/foo/bar");
+
+ device_id = generate_random();
+ unkn2 = 23;
+ unkn3 = 0;
+
+ ZERO_STRUCT(ph);
+ ZERO_STRUCT(close_ph);
+
+ status = dcerpc_mdssvc_open(b,
+ tctx,
+ &device_id,
+ &unkn2,
+ &unkn3,
+ share_mount_path,
+ share_name,
+ share_path,
+ &ph);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "dcerpc_mdssvc_open failed\n");
+
+ status = dcerpc_mdssvc_close(b,
+ tctx,
+ &ph,
+ 0,
+ device_id,
+ unkn2,
+ 0,
+ &close_ph,
+ &close_status);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "dcerpc_mdssvc_open failed\n");
+
+ ph_blob = (DATA_BLOB) {
+ .data = (uint8_t *)&ph,
+ .length = sizeof(struct policy_handle)
+ };
+ close_ph_blob = (DATA_BLOB) {
+ .data = (uint8_t *)&close_ph,
+ .length = sizeof(struct policy_handle),
+ };
+
+ torture_assert_data_blob_equal(tctx, close_ph_blob, ph_blob,
+ "bad blob");
+
+ torture_comment(tctx, "Test close with a all-zero handle\n");
+
+ ZERO_STRUCT(ph);
+ status = dcerpc_mdssvc_close(b,
+ tctx,
+ &ph,
+ 0,
+ device_id,
+ unkn2,
+ 0,
+ &close_ph,
+ &close_status);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "dcerpc_mdssvc_close failed\n");
+
+ torture_assert_data_blob_equal(tctx, close_ph_blob, ph_blob,
+ "bad blob");
+
+done:
+ return ok;
+}
+
+static bool test_mdssvc_null_ph(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ struct policy_handle nullh;
+ struct policy_handle ph;
+ uint32_t device_id;
+ uint32_t unkn2;
+ uint32_t unkn7;
+ uint32_t cmd_status;
+ uint32_t flags;
+ NTSTATUS status;
+ bool ok = true;
+
+ device_id = generate_random();
+ unkn2 = 23;
+ unkn7 = 0;
+ cmd_status = 0;
+
+ ZERO_STRUCT(nullh);
+ ZERO_STRUCT(ph);
+
+ status = dcerpc_mdssvc_unknown1(b,
+ tctx,
+ &ph,
+ 0,
+ device_id,
+ unkn2,
+ 0,
+ geteuid(),
+ getegid(),
+ &cmd_status,
+ &flags,
+ &unkn7);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "dcerpc_mdssvc_unknown1 failed\n");
+
+ torture_assert_mem_equal_goto(tctx, &ph, &nullh,
+ sizeof(ph), ok, done,
+ "Expected all-zero policy handle\n");
+
+done:
+ return ok;
+}
+
+static bool test_mdssvc_invalid_ph_unknown1(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ struct policy_handle ph;
+ uint32_t device_id;
+ uint32_t unkn2;
+ uint32_t unkn7;
+ uint32_t cmd_status;
+ uint32_t flags;
+ NTSTATUS status;
+ bool ok = true;
+
+ device_id = generate_random();
+ unkn2 = 23;
+ unkn7 = 0;
+ cmd_status = 0;
+
+ ZERO_STRUCT(ph);
+ ph.uuid = GUID_random();
+
+ status = dcerpc_mdssvc_unknown1(b,
+ tctx,
+ &ph,
+ 0,
+ device_id,
+ unkn2,
+ 0,
+ geteuid(),
+ getegid(),
+ &cmd_status,
+ &flags,
+ &unkn7);
+ torture_assert_ntstatus_equal_goto(
+ tctx, status, NT_STATUS_RPC_PROTOCOL_ERROR, ok, done,
+ "dcerpc_mdssvc_unknown1 failed\n");
+
+done:
+ return ok;
+}
+
+static bool test_mdssvc_invalid_ph_cmd(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ struct policy_handle ph;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+ uint32_t device_id;
+ uint32_t unkn2;
+ uint32_t unkn9;
+ uint32_t fragment;
+ uint32_t flags;
+ NTSTATUS status;
+ bool ok = true;
+
+ device_id = generate_random();
+ unkn2 = 23;
+ unkn9 = 0;
+ fragment = 0;
+ flags = UINT32_C(0x6b000001);
+
+ ZERO_STRUCT(ph);
+ ph.uuid = GUID_random();
+
+ request_blob.spotlight_blob = talloc_array(state,
+ uint8_t,
+ 0);
+ torture_assert_not_null_goto(tctx, request_blob.spotlight_blob,
+ ok, done, "dalloc_zero failed\n");
+ request_blob.size = 0;
+ request_blob.length = 0;
+ request_blob.size = 0;
+
+ status = dcerpc_mdssvc_cmd(b,
+ state,
+ &ph,
+ 0,
+ device_id,
+ unkn2,
+ 0,
+ flags,
+ request_blob,
+ 0,
+ 64 * 1024,
+ 1,
+ 64 * 1024,
+ 0,
+ 0,
+ &fragment,
+ &response_blob,
+ &unkn9);
+ torture_assert_ntstatus_equal_goto(
+ tctx, status, NT_STATUS_RPC_PROTOCOL_ERROR, ok, done,
+ "dcerpc_mdssvc_unknown1 failed\n");
+
+done:
+ return ok;
+}
+
+static uint8_t test_sl_unpack_loop_buf[] = {
+ 0x34, 0x33, 0x32, 0x31, 0x33, 0x30, 0x64, 0x6d,
+ 0x1d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x07, 0x04, 0x00, 0x00, 0x00,
+ 0x66, 0x65, 0x74, 0x63, 0x68, 0x41, 0x74, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x3a,
+ 0x66, 0x6f, 0x72, 0x4f, 0x49, 0x44, 0x41, 0x72,
+ 0x72, 0x61, 0x79, 0x3a, 0x63, 0x6f, 0x6e, 0x74,
+ 0x65, 0x78, 0x74, 0x3a, 0x00, 0x00, 0x00, 0xea,
+ 0x02, 0x00, 0x00, 0x84, 0x02, 0x00, 0x00, 0x00,
+ 0x0a, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x02, 0x05, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x00,
+ 0x6b, 0x4d, 0x44, 0x49, 0x74, 0x65, 0x6d, 0x50,
+ 0x61, 0x74, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x87, 0x08, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0xdd, 0x0a, 0x20, 0x00, 0x00, 0x6b,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x0a, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x0c, 0x04, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static bool test_mdssvc_sl_unpack_loop(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+ uint32_t device_id;
+ uint32_t unkn2;
+ uint32_t unkn9;
+ uint32_t fragment;
+ uint32_t flags;
+ NTSTATUS status;
+ bool ok = true;
+
+ device_id = UINT32_C(0x2f000045);
+ unkn2 = 23;
+ unkn9 = 0;
+ fragment = 0;
+ flags = UINT32_C(0x6b000001);
+
+ request_blob.spotlight_blob = test_sl_unpack_loop_buf;
+ request_blob.size = sizeof(test_sl_unpack_loop_buf);
+ request_blob.length = sizeof(test_sl_unpack_loop_buf);
+
+ status = dcerpc_mdssvc_cmd(b,
+ state,
+ &state->ph,
+ 0,
+ device_id,
+ unkn2,
+ 0,
+ flags,
+ request_blob,
+ 0,
+ 64 * 1024,
+ 1,
+ 64 * 1024,
+ 0,
+ 0,
+ &fragment,
+ &response_blob,
+ &unkn9);
+ torture_assert_ntstatus_ok_goto(
+ tctx, status, ok, done,
+ "dcerpc_mdssvc_unknown1 failed\n");
+
+done:
+ return ok;
+}
+
+static bool test_sl_dict_type_safety(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+ uint64_t ctx1 = 0xdeadbeef;
+ uint64_t ctx2 = 0xcafebabe;
+ uint32_t device_id;
+ uint32_t unkn2;
+ uint32_t unkn9;
+ uint32_t fragment;
+ uint32_t flags;
+ DALLOC_CTX *d = NULL;
+ sl_array_t *array1 = NULL, *array2 = NULL;
+ sl_dict_t *arg = NULL;
+ int result;
+ NTSTATUS status;
+ bool ok = true;
+
+ device_id = UINT32_C(0x2f000045);
+ unkn2 = 23;
+ unkn9 = 0;
+ fragment = 0;
+ flags = UINT32_C(0x6b000001);
+
+ d = dalloc_new(tctx);
+ torture_assert_not_null_goto(tctx, d,
+ ok, done, "dalloc_new failed\n");
+
+ array1 = dalloc_zero(d, sl_array_t);
+ torture_assert_not_null_goto(tctx, array1,
+ ok, done, "dalloc_zero failed\n");
+
+ array2 = dalloc_zero(d, sl_array_t);
+ torture_assert_not_null_goto(tctx, array2,
+ ok, done, "dalloc_new failed\n");
+
+ result = dalloc_stradd(array2, "openQueryWithParams:forContext:");
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_stradd failed\n");
+
+ result = dalloc_add_copy(array2, &ctx1, uint64_t);
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_stradd failed\n");
+
+ result = dalloc_add_copy(array2, &ctx2, uint64_t);
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_stradd failed\n");
+
+ arg = dalloc_zero(array1, sl_dict_t);
+ torture_assert_not_null_goto(tctx, d,
+ ok, done, "dalloc_zero failed\n");
+
+ result = dalloc_stradd(arg, "kMDQueryString");
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_stradd failed\n");
+
+ result = dalloc_stradd(arg, "*");
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_stradd failed\n");
+
+ result = dalloc_stradd(arg, "kMDScopeArray");
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_stradd failed\n");
+
+ result = dalloc_stradd(arg, "AAAABBBB");
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_stradd failed\n");
+
+ result = dalloc_add(array1, array2, sl_array_t);
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_add failed\n");
+
+ result = dalloc_add(array1, arg, sl_dict_t);
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_add failed\n");
+
+ result = dalloc_add(d, array1, sl_array_t);
+ torture_assert_goto(tctx, result == 0,
+ ok, done, "dalloc_add failed\n");
+
+ torture_comment(tctx, "%s", dalloc_dump(d, 0));
+
+ request_blob.spotlight_blob = talloc_array(tctx,
+ uint8_t,
+ 64 * 1024);
+ torture_assert_not_null_goto(tctx, request_blob.spotlight_blob,
+ ok, done, "dalloc_new failed\n");
+ request_blob.size = 64 * 1024;
+
+ status = sl_pack_alloc(tctx, d, &request_blob, 64 * 1024);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "sl_pack_alloc() failed\n");
+
+ status = dcerpc_mdssvc_cmd(b,
+ state,
+ &state->ph,
+ 0,
+ device_id,
+ unkn2,
+ 0,
+ flags,
+ request_blob,
+ 0,
+ 64 * 1024,
+ 1,
+ 64 * 1024,
+ 0,
+ 0,
+ &fragment,
+ &response_blob,
+ &unkn9);
+ torture_assert_ntstatus_ok_goto(
+ tctx, status, ok, done,
+ "dcerpc_mdssvc_cmd failed\n");
+
+done:
+ return ok;
+}
+
+static bool test_mdssvc_invalid_ph_close(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ struct policy_handle ph;
+ uint32_t device_id;
+ uint32_t unkn2;
+ uint32_t close_status;
+ NTSTATUS status;
+ bool ok = true;
+
+ device_id = generate_random();
+ unkn2 = 23;
+ close_status = 0;
+
+ ZERO_STRUCT(ph);
+ ph.uuid = GUID_random();
+
+ status = dcerpc_mdssvc_close(b,
+ state,
+ &ph,
+ 0,
+ device_id,
+ unkn2,
+ 0,
+ &ph,
+ &close_status);
+ torture_assert_ntstatus_equal_goto(
+ tctx, status, NT_STATUS_RPC_PROTOCOL_ERROR, ok, done,
+ "dcerpc_mdssvc_unknown1 failed\n");
+
+done:
+ return ok;
+}
+
+/*
+ * Test fetchAttributes with unknown CNID
+ */
+static bool test_mdssvc_fetch_attr_unknown_cnid(struct torture_context *tctx,
+ void *data)
+{
+ struct torture_mdsscv_state *state = talloc_get_type_abort(
+ data, struct torture_mdsscv_state);
+ struct dcerpc_binding_handle *b = state->p->binding_handle;
+ uint32_t max_fragment_size = 64 * 1024;
+ struct mdssvc_blob request_blob;
+ struct mdssvc_blob response_blob;
+ DALLOC_CTX *d = NULL, *mds_reply = NULL;
+ uint64_t *uint64var = NULL;
+ sl_array_t *array = NULL;
+ sl_array_t *cmd_array = NULL;
+ sl_array_t *attr_array = NULL;
+ sl_cnids_t *cnids = NULL;
+ void *path = NULL;
+ const char *path_type = NULL;
+ uint64_t ino64;
+ NTSTATUS status;
+ int ret;
+ bool ok = true;
+
+ d = dalloc_new(state);
+ torture_assert_not_null_goto(tctx, d, ret, done, "dalloc_new failed\n");
+
+ array = dalloc_zero(d, sl_array_t);
+ torture_assert_not_null_goto(tctx, array, ret, done,
+ "dalloc_zero failed\n");
+
+ ret = dalloc_add(d, array, sl_array_t);
+ torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n");
+
+ cmd_array = dalloc_zero(d, sl_array_t);
+ torture_assert_not_null_goto(tctx, cmd_array, ret, done,
+ "dalloc_zero failed\n");
+
+ ret = dalloc_add(array, cmd_array, sl_array_t);
+ torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n");
+
+ ret = dalloc_stradd(cmd_array, "fetchAttributes:forOIDArray:context:");
+ torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_stradd failed\n");
+
+ uint64var = talloc_zero_array(cmd_array, uint64_t, 2);
+ torture_assert_not_null_goto(tctx, uint64var, ret, done,
+ "talloc_zero_array failed\n");
+ talloc_set_name(uint64var, "uint64_t *");
+
+ uint64var[0] = 0x500a;
+ uint64var[1] = 0;
+
+ ret = dalloc_add(cmd_array, &uint64var[0], uint64_t *);
+ torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n");
+
+ attr_array = dalloc_zero(d, sl_array_t);
+ torture_assert_not_null_goto(tctx, attr_array, ret, done,
+ "dalloc_zero failed\n");
+
+ ret = dalloc_add(array, attr_array, sl_array_t);
+ torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n");
+
+ ret = dalloc_stradd(attr_array, "kMDItemPath");
+ torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_stradd failed\n");
+
+ /* CNIDs */
+ cnids = talloc_zero(array, sl_cnids_t);
+ torture_assert_not_null_goto(tctx, cnids, ret, done,
+ "talloc_zero failed\n");
+
+ cnids->ca_cnids = dalloc_new(cnids);
+ torture_assert_not_null_goto(tctx, cnids->ca_cnids, ret, done,
+ "dalloc_new failed\n");
+
+ cnids->ca_unkn1 = 0xadd;
+ cnids->ca_context = 0x6b000020;
+
+ ino64 = UINT64_C(64382947389618974);
+ ret = dalloc_add_copy(cnids->ca_cnids, &ino64, uint64_t);
+ torture_assert_goto(tctx, ret == 0, ret, done,
+ "dalloc_add_copy failed\n");
+
+ ret = dalloc_add(array, cnids, sl_cnids_t);
+ torture_assert_goto(tctx, ret == 0, ret, done, "dalloc_add failed\n");
+
+ status = sl_pack_alloc(tctx, d, &request_blob, max_fragment_size);
+ torture_assert_ntstatus_ok_goto(tctx, status, ok, done,
+ "sl_pack_alloc() failed\n");
+
+ status = dcerpc_mdssvc_cmd(b,
+ state,
+ &state->ph,
+ 0,
+ state->dev,
+ state->mdscmd_open.unkn2,
+ 0,
+ state->flags,
+ request_blob,
+ 0,
+ max_fragment_size,
+ 1,
+ max_fragment_size,
+ 0,
+ 0,
+ &state->mdscmd_cmd.fragment,
+ &response_blob,
+ &state->mdscmd_cmd.unkn9);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "dcerpc_mdssvc_cmd failed\n");
+
+ mds_reply = dalloc_new(state);
+ torture_assert_not_null_goto(tctx, mds_reply, ret, done,
+ "dalloc_zero failed\n");
+
+ ok = sl_unpack(mds_reply,
+ (char *)response_blob.spotlight_blob,
+ response_blob.length);
+ torture_assert_goto(tctx, ok, ret, done, "dalloc_add failed\n");
+
+ torture_comment(tctx, "%s", dalloc_dump(mds_reply, 0));
+
+ path = dalloc_get(mds_reply,
+ "DALLOC_CTX", 0,
+ "DALLOC_CTX", 2,
+ "DALLOC_CTX", 0,
+ "sl_nil_t", 1);
+ torture_assert_not_null_goto(tctx, path, ret, done,
+ "dalloc_get path failed\n");
+
+ path_type = talloc_get_name(path);
+
+ torture_assert_str_equal_goto(tctx, path_type, "sl_nil_t", ret, done,
+ "Wrong dalloc object type\n");
+
+done:
+ return ok;
+}
+
+struct torture_suite *torture_rpc_mdssvc(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(
+ mem_ctx, "mdssvc");
+ struct torture_tcase *tcase = NULL;
+
+ tcase = torture_suite_add_tcase(suite, "rpccmd");
+ if (tcase == NULL) {
+ return NULL;
+ }
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_mdssvc_setup,
+ torture_rpc_mdssvc_teardown);
+
+ torture_tcase_add_simple_test(tcase,
+ "open_unknown_share",
+ test_mdssvc_open_unknown_share);
+
+ torture_tcase_add_simple_test(tcase,
+ "open_spotlight_disabled",
+ test_mdssvc_open_spotlight_disabled);
+
+ torture_tcase_add_simple_test(tcase,
+ "close",
+ test_mdssvc_close);
+
+ torture_tcase_add_simple_test(tcase,
+ "null_ph",
+ test_mdssvc_null_ph);
+
+ tcase = torture_suite_add_tcase(suite, "disconnect1");
+ if (tcase == NULL) {
+ return NULL;
+ }
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_mdssvc_open,
+ torture_rpc_mdssvc_close);
+
+ torture_tcase_add_simple_test(tcase,
+ "invalid_ph_unknown1",
+ test_mdssvc_invalid_ph_unknown1);
+
+ tcase = torture_suite_add_tcase(suite, "disconnect2");
+ if (tcase == NULL) {
+ return NULL;
+ }
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_mdssvc_open,
+ torture_rpc_mdssvc_close);
+
+ torture_tcase_add_simple_test(tcase,
+ "invalid_ph_cmd",
+ test_mdssvc_invalid_ph_cmd);
+
+ tcase = torture_suite_add_tcase(suite, "disconnect3");
+ if (tcase == NULL) {
+ return NULL;
+ }
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_mdssvc_open,
+ torture_rpc_mdssvc_close);
+
+ torture_tcase_add_simple_test(tcase,
+ "invalid_ph_close",
+ test_mdssvc_invalid_ph_close);
+
+ tcase = torture_suite_add_tcase(suite, "mdscmd");
+ if (tcase == NULL) {
+ return NULL;
+ }
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_mdssvc_open,
+ torture_rpc_mdssvc_close);
+
+ torture_tcase_add_simple_test(tcase,
+ "fetch_unknown_cnid",
+ test_mdssvc_fetch_attr_unknown_cnid);
+
+ torture_tcase_add_simple_test(tcase,
+ "mdssvc_sl_unpack_loop",
+ test_mdssvc_sl_unpack_loop);
+
+ torture_tcase_add_simple_test(tcase,
+ "sl_dict_type_safety",
+ test_sl_dict_type_safety);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/mgmt.c b/source4/torture/rpc/mgmt.c
new file mode 100644
index 0000000..f3344c9
--- /dev/null
+++ b/source4/torture/rpc/mgmt.c
@@ -0,0 +1,322 @@
+/*
+ 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 "librpc/gen_ndr/ndr_mgmt_c.h"
+#include "auth/gensec/gensec.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+
+
+/*
+ ask the server what interface IDs are available on this endpoint
+*/
+bool test_inq_if_ids(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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)
+{
+ 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_mgmt_inq_if_ids_r(b, mem_ctx, &r),
+ "inq_if_ids failed");
+
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "inq_if_ids gave unexpected error code");
+
+ if (!vector) {
+ torture_comment(tctx, "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;
+
+ torture_comment(tctx, "\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 torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx)
+{
+ 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_mgmt_inq_stats_r(b, mem_ctx, &r),
+ "inq_stats failed");
+
+ if (statistics.count != MGMT_STATS_ARRAY_MAX_SIZE) {
+ torture_comment(tctx, "Unexpected array size %d\n", statistics.count);
+ return false;
+ }
+
+ torture_comment(tctx, "\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_size(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t authn_proto,
+ const char *expected_princ_name)
+{
+ struct mgmt_inq_princ_name r;
+ uint32_t len, i;
+
+ len = strlen(expected_princ_name);
+
+ r.in.authn_proto = authn_proto;
+
+ /*
+ * 0 gives NT_STATUS_RPC_BAD_STUB_DATA
+ */
+
+ for (i=1; i <= len; i++) {
+ r.in.princ_name_size = i;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_mgmt_inq_princ_name_r(b, tctx, &r),
+ "mgmt_inq_princ_name failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INSUFFICIENT_BUFFER,
+ "mgmt_inq_princ_name failed");
+ }
+
+ r.in.princ_name_size = len + 1;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_mgmt_inq_princ_name_r(b, tctx, &r),
+ "mgmt_inq_princ_name failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "mgmt_inq_princ_name failed");
+
+ return true;
+}
+
+static bool test_inq_princ_name(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx)
+{
+ NTSTATUS status;
+ struct mgmt_inq_princ_name r;
+ int i;
+ bool ret = false;
+
+ for (i=0;i<256;i++) {
+ r.in.authn_proto = i; /* DCERPC_AUTH_TYPE_* */
+ r.in.princ_name_size = 100;
+
+ status = dcerpc_mgmt_inq_princ_name_r(b, 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(NULL, i);
+ ret = true;
+ if (name) {
+ torture_comment(tctx, "\tprinciple name for proto %u (%s) is '%s'\n",
+ i, name, r.out.princ_name);
+ } else {
+ torture_comment(tctx, "\tprinciple name for proto %u is '%s'\n",
+ i, r.out.princ_name);
+ }
+
+ switch (i) {
+ case DCERPC_AUTH_TYPE_KRB5:
+ case DCERPC_AUTH_TYPE_NTLMSSP:
+ case DCERPC_AUTH_TYPE_SPNEGO:
+ torture_assert(tctx,
+ test_inq_princ_name_size(tctx, b, i, r.out.princ_name),
+ "failed");
+ break;
+ case DCERPC_AUTH_TYPE_SCHANNEL:
+ /*
+ * for some reason schannel behaves differently
+ *
+ */
+ default:
+ break;
+ }
+ }
+ }
+
+ if (!ret) {
+ torture_comment(tctx, "\tno principle names?\n");
+ }
+
+ return true;
+}
+
+static bool test_is_server_listening(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx)
+{
+ struct mgmt_is_server_listening r;
+ r.out.status = talloc(mem_ctx, uint32_t);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_mgmt_is_server_listening_r(b, mem_ctx, &r),
+ "is_server_listening failed");
+
+ if (*r.out.status != 0 || r.out.result == 0) {
+ torture_comment(tctx, "\tserver is NOT listening\n");
+ } else {
+ torture_comment(tctx, "\tserver is listening\n");
+ }
+
+ return true;
+}
+
+static bool test_stop_server_listening(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx)
+{
+ struct mgmt_stop_server_listening r;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_mgmt_stop_server_listening_r(b, mem_ctx, &r),
+ "stop_server_listening failed");
+
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "\tserver refused to stop listening - %s\n", win_errstr(r.out.result));
+ } else {
+ torture_comment(tctx, "\tserver allowed a stop_server_listening request\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+bool torture_rpc_mgmt(struct torture_context *tctx)
+{
+ 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(tctx, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return false;
+ }
+
+ for (l=ndr_table_list();l;l=l->next) {
+ struct dcerpc_binding_handle *bh;
+
+ 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;
+ }
+
+ torture_comment(tctx, "\nTesting pipe '%s'\n", l->table->name);
+
+ status = dcerpc_epm_map_binding(loop_ctx, b, l->table,
+ tctx->ev, tctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "Failed to map port for uuid %s\n",
+ GUID_string(loop_ctx, &l->table->syntax_id.uuid));
+ talloc_free(loop_ctx);
+ continue;
+ }
+
+ lpcfg_set_cmdline(tctx->lp_ctx, "torture:binding", dcerpc_binding_string(loop_ctx, b));
+
+ status = torture_rpc_connection(tctx, &p, &ndr_table_mgmt);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ torture_comment(tctx, "Interface not available - skipping\n");
+ talloc_free(loop_ctx);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(loop_ctx);
+ torture_comment(tctx, "Interface not available (%s) - skipping\n", nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ bh = p->binding_handle;
+
+ if (!test_is_server_listening(tctx, bh, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_stop_server_listening(tctx, bh, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_inq_stats(tctx, bh, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_inq_princ_name(tctx, bh, loop_ctx)) {
+ ret = false;
+ }
+
+ if (!test_inq_if_ids(tctx, bh, 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 0000000..a3d190f
--- /dev/null
+++ b/source4/torture/rpc/netlogon.c
@@ -0,0 +1,6038 @@
+/*
+ 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
+ Copyright (C) Matthias Dieter Wallnöfer 2009-2010
+
+ 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/events/events.h"
+#include "lib/cmdline/cmdline.h"
+#include "torture/rpc/torture_rpc.h"
+#include "../lib/crypto/crypto.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"
+#include "libcli/security/security.h"
+#include <ldb.h>
+#include "lib/util/util_ldb.h"
+#include "ldb_wrap.h"
+#include "lib/replace/system/network.h"
+#include "dsdb/samdb/samdb.h"
+
+#undef strcasecmp
+
+#define TEST_MACHINE_NAME "torturetest"
+
+static bool test_netr_broken_binding_handle(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_DsRGetSiteName r;
+ const char *site = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.computer_name = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.out.site = &site;
+
+ torture_comment(tctx,
+ "Testing netlogon request with correct binding handle: %s\n",
+ r.in.computer_name);
+
+ status = dcerpc_netr_DsRGetSiteName_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Netlogon request with broken binding handle");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "Netlogon request with broken binding handle");
+
+ if (torture_setting_bool(tctx, "samba3", false) ||
+ torture_setting_bool(tctx, "samba4", false)) {
+ torture_skip(tctx,
+ "Skipping broken binding handle check against Samba");
+ }
+
+ r.in.computer_name = talloc_asprintf(tctx, "\\\\\\\\%s",
+ dcerpc_server_name(p));
+
+ torture_comment(tctx,
+ "Testing netlogon request with broken binding handle: %s\n",
+ r.in.computer_name);
+
+ status = dcerpc_netr_DsRGetSiteName_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Netlogon request with broken binding handle");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_INVALID_COMPUTERNAME,
+ "Netlogon request with broken binding handle");
+
+ r.in.computer_name = "\\\\\\\\THIS_IS_NOT_VALID";
+
+ torture_comment(tctx,
+ "Testing netlogon request with broken binding handle: %s\n",
+ r.in.computer_name);
+
+ status = dcerpc_netr_DsRGetSiteName_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Netlogon request with broken binding handle");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_INVALID_COMPUTERNAME,
+ "Netlogon request with broken binding handle");
+
+ return true;
+}
+
+static bool test_LogonUasLogon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct netr_LogonUasLogon r;
+ struct netr_UasInfo *info = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_name = NULL;
+ r.in.account_name = cli_credentials_get_username(
+ samba_cmdline_get_creds());
+ r.in.workstation = TEST_MACHINE_NAME;
+ r.out.info = &info;
+
+ status = dcerpc_netr_LogonUasLogon_r(b, 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;
+ struct netr_UasLogoffInfo info;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_name = NULL;
+ r.in.account_name = cli_credentials_get_username(
+ samba_cmdline_get_creds());
+ r.in.workstation = TEST_MACHINE_NAME;
+ r.out.info = &info;
+
+ status = dcerpc_netr_LogonUasLogoff_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonUasLogoff");
+
+ return true;
+}
+
+bool test_SetupCredentials(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct netlogon_creds_CredentialState **creds_out)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ const struct samr_Password *mach_password;
+ const char *machine_name;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ mach_password = cli_credentials_get_nt_hash(credentials, tctx);
+ machine_name = cli_credentials_get_workstation(credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ a.in.server_name = NULL;
+ a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name);
+ a.in.secure_channel_type = cli_credentials_get_secure_channel_type(credentials);
+ a.in.computer_name = machine_name;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ mach_password, &credentials3,
+ 0);
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+
+ torture_comment(tctx, "Testing ServerAuthenticate\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate_r(b, tctx, &a),
+ "ServerAuthenticate failed");
+
+ /* This allows the tests to continue against the more fussy windows 2008 */
+ if (NT_STATUS_EQUAL(a.out.result, NT_STATUS_DOWNGRADE_DETECTED)) {
+ return test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES,
+ credentials,
+ cli_credentials_get_secure_channel_type(credentials),
+ creds_out);
+ }
+
+ torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3),
+ "Credential chaining failed");
+
+ *creds_out = creds;
+ return true;
+}
+
+bool test_SetupCredentials2ex(struct dcerpc_pipe *p, struct torture_context *tctx,
+ uint32_t negotiate_flags,
+ struct cli_credentials *machine_credentials,
+ const char *computer_name,
+ enum netr_SchannelType sec_chan_type,
+ NTSTATUS expected_result,
+ struct netlogon_creds_CredentialState **creds_out)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate2 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ const struct samr_Password *mach_password;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *account_name = cli_credentials_get_username(machine_credentials);
+
+ mach_password = cli_credentials_get_nt_hash(machine_credentials, tctx);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = computer_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ a.in.server_name = NULL;
+ a.in.account_name = account_name;
+ a.in.secure_channel_type = sec_chan_type;
+ a.in.computer_name = computer_name;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ mach_password, &credentials3,
+ negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate2\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a),
+ "ServerAuthenticate2 failed");
+ torture_assert_ntstatus_equal(tctx, a.out.result, expected_result,
+ "ServerAuthenticate2 unexpected");
+
+ if (NT_STATUS_IS_OK(expected_result)) {
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3),
+ "Credential chaining failed");
+ } else {
+ torture_assert(tctx, !netlogon_creds_client_check(creds, &credentials3),
+ "Credential chaining passed unexptected");
+ }
+
+ torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags);
+
+ *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,
+ enum netr_SchannelType sec_chan_type,
+ struct netlogon_creds_CredentialState **creds_out)
+{
+ const char *computer_name =
+ cli_credentials_get_workstation(machine_credentials);
+
+ return test_SetupCredentials2ex(p, tctx, negotiate_flags,
+ machine_credentials,
+ computer_name,
+ sec_chan_type,
+ NT_STATUS_OK,
+ creds_out);
+}
+
+bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx,
+ uint32_t negotiate_flags,
+ struct cli_credentials *machine_credentials,
+ struct netlogon_creds_CredentialState **creds_out)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+ struct dcerpc_binding_handle *b = NULL;
+
+ if (p == NULL) {
+ return false;
+ }
+
+ b = p->binding_handle;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ torture_assert(tctx, machine_name != NULL, "machine_name");
+ plain_pass = cli_credentials_get_password(machine_credentials);
+ torture_assert(tctx, plain_pass != NULL, "plain_pass");
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.out.rid = &rid;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a),
+ "ServerAuthenticate3 failed");
+ torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags);
+
+ /* Prove that requesting a challenge again won't break it */
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ *creds_out = creds;
+ return true;
+}
+
+bool test_SetupCredentialsDowngrade(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t negotiate_flags = 0;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ torture_assert(tctx, machine_name != NULL, "machine_name");
+ plain_pass = cli_credentials_get_password(machine_credentials);
+ torture_assert(tctx, plain_pass != NULL, "plain_pass");
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.out.rid = &rid;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a),
+ "ServerAuthenticate3 failed");
+ torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_DOWNGRADE_DETECTED, "ServerAuthenticate3 should have failed");
+
+ negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a),
+ "ServerAuthenticate3 failed");
+ torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 should succeed");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ torture_comment(tctx, "negotiate_flags=0x%08x\n", negotiate_flags);
+
+ /* Prove that requesting a challenge again won't break it */
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ return true;
+}
+
+bool test_SetupCredentialsPipe(const struct dcerpc_pipe *p1,
+ struct torture_context *tctx,
+ struct cli_credentials *machine_credentials,
+ struct netlogon_creds_CredentialState *creds,
+ uint32_t additional_flags,
+ struct dcerpc_pipe **_p2)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *b2 = NULL;
+ struct dcerpc_pipe *p2 = NULL;
+
+ b2 = dcerpc_binding_dup(tctx, p1->binding);
+ torture_assert(tctx, b2 != NULL, "dcerpc_binding_dup");
+ dcerpc_binding_set_flags(b2,
+ DCERPC_SCHANNEL | additional_flags,
+ DCERPC_AUTH_OPTIONS);
+
+ cli_credentials_set_netlogon_creds(machine_credentials, creds);
+ status = dcerpc_pipe_connect_b(tctx, &p2, b2,
+ &ndr_table_netlogon,
+ machine_credentials,
+ tctx->ev, tctx->lp_ctx);
+ cli_credentials_set_netlogon_creds(machine_credentials, NULL);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_pipe_connect_b schannel");
+
+ *_p2 = p2;
+ return true;
+}
+
+static bool test_ServerReqChallenge(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ const char *machine_name;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct netr_ServerAuthenticate2 a;
+ uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ uint32_t out_negotiate_flags = 0;
+ const struct samr_Password *mach_password = NULL;
+ enum netr_SchannelType sec_chan_type = 0;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ const char *account_name = NULL;
+
+ machine_name = cli_credentials_get_workstation(credentials);
+ mach_password = cli_credentials_get_nt_hash(credentials, tctx);
+ account_name = cli_credentials_get_username(credentials);
+ sec_chan_type = cli_credentials_get_secure_channel_type(credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(
+ tctx,
+ r.out.result,
+ "ServerReqChallenge failed");
+ a.in.server_name = NULL;
+ a.in.account_name = account_name;
+ a.in.secure_channel_type = sec_chan_type;
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &in_negotiate_flags;
+ a.out.negotiate_flags = &out_negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ mach_password, &credentials3,
+ in_negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate2\n");
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a),
+ "ServerAuthenticate2 failed");
+ torture_assert_ntstatus_equal(
+ tctx,
+ a.out.result,
+ NT_STATUS_OK,
+ "ServerAuthenticate2 unexpected");
+
+ return true;
+}
+
+static bool test_ServerReqChallenge_zero_challenge(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ const char *machine_name;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct netr_ServerAuthenticate2 a;
+ uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ uint32_t out_negotiate_flags = 0;
+ const struct samr_Password *mach_password = NULL;
+ enum netr_SchannelType sec_chan_type = 0;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ const char *account_name = NULL;
+
+ machine_name = cli_credentials_get_workstation(credentials);
+ mach_password = cli_credentials_get_nt_hash(credentials, tctx);
+ account_name = cli_credentials_get_username(credentials);
+ sec_chan_type = cli_credentials_get_secure_channel_type(credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ /*
+ * Set the client challenge to zero, this should fail
+ * CVE-2020-1472(ZeroLogon)
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497
+ */
+ ZERO_STRUCT(credentials1);
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(
+ tctx,
+ r.out.result,
+ "ServerReqChallenge failed");
+ a.in.server_name = NULL;
+ a.in.account_name = account_name;
+ a.in.secure_channel_type = sec_chan_type;
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &in_negotiate_flags;
+ a.out.negotiate_flags = &out_negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ mach_password, &credentials3,
+ in_negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate2\n");
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a),
+ "ServerAuthenticate2 failed");
+ torture_assert_ntstatus_equal(
+ tctx,
+ a.out.result,
+ NT_STATUS_ACCESS_DENIED,
+ "ServerAuthenticate2 unexpected");
+
+ return true;
+}
+
+static bool test_ServerReqChallenge_5_repeats(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ const char *machine_name;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct netr_ServerAuthenticate2 a;
+ uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ uint32_t out_negotiate_flags = 0;
+ const struct samr_Password *mach_password = NULL;
+ enum netr_SchannelType sec_chan_type = 0;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ const char *account_name = NULL;
+
+ machine_name = cli_credentials_get_workstation(credentials);
+ mach_password = cli_credentials_get_nt_hash(credentials, tctx);
+ account_name = cli_credentials_get_username(credentials);
+ sec_chan_type = cli_credentials_get_secure_channel_type(credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ /*
+ * Set the first 5 bytes of the client challenge to the same value,
+ * this should fail CVE-2020-1472(ZeroLogon)
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497
+ */
+ credentials1.data[0] = 'A';
+ credentials1.data[1] = 'A';
+ credentials1.data[2] = 'A';
+ credentials1.data[3] = 'A';
+ credentials1.data[4] = 'A';
+ credentials1.data[5] = 'B';
+ credentials1.data[6] = 'C';
+ credentials1.data[7] = 'D';
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(
+ tctx,
+ r.out.result,
+ "ServerReqChallenge failed");
+ a.in.server_name = NULL;
+ a.in.account_name = account_name;
+ a.in.secure_channel_type = sec_chan_type;
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &in_negotiate_flags;
+ a.out.negotiate_flags = &out_negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ mach_password, &credentials3,
+ in_negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate2\n");
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a),
+ "ServerAuthenticate2 failed");
+ torture_assert_ntstatus_equal(
+ tctx,
+ a.out.result,
+ NT_STATUS_ACCESS_DENIED,
+ "ServerAuthenticate2 unexpected");
+
+ return true;
+}
+
+static bool test_ServerReqChallenge_4_repeats(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ const char *machine_name;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct netr_ServerAuthenticate2 a;
+ uint32_t in_negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ uint32_t out_negotiate_flags = 0;
+ const struct samr_Password *mach_password = NULL;
+ enum netr_SchannelType sec_chan_type = 0;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ const char *account_name = NULL;
+
+ machine_name = cli_credentials_get_workstation(credentials);
+ mach_password = cli_credentials_get_nt_hash(credentials, tctx);
+ account_name = cli_credentials_get_username(credentials);
+ sec_chan_type = cli_credentials_get_secure_channel_type(credentials);
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ /*
+ * Set the first 4 bytes of the client challenge to the same
+ * value, this should pass as 5 bytes identical are needed to
+ * fail for CVE-2020-1472(ZeroLogon)
+ *
+ * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14497
+ */
+ credentials1.data[0] = 'A';
+ credentials1.data[1] = 'A';
+ credentials1.data[2] = 'A';
+ credentials1.data[3] = 'A';
+ credentials1.data[4] = 'B';
+ credentials1.data[5] = 'C';
+ credentials1.data[6] = 'D';
+ credentials1.data[7] = 'E';
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(
+ tctx,
+ r.out.result,
+ "ServerReqChallenge failed");
+ a.in.server_name = NULL;
+ a.in.account_name = account_name;
+ a.in.secure_channel_type = sec_chan_type;
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &in_negotiate_flags;
+ a.out.negotiate_flags = &out_negotiate_flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ mach_password, &credentials3,
+ in_negotiate_flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate2\n");
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a),
+ "ServerAuthenticate2 failed");
+ torture_assert_ntstatus_equal(
+ tctx,
+ a.out.result,
+ NT_STATUS_OK,
+ "ServerAuthenticate2 unexpected");
+
+ return true;
+}
+
+/*
+ * Establish a NetLogon session, using a session key that encrypts the
+ * target character to zero
+ */
+static bool test_ServerAuthenticate2_encrypts_to_zero(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials,
+ const char target,
+ struct netlogon_creds_CredentialState **creds_out)
+{
+ const char *computer_name =
+ cli_credentials_get_workstation(machine_credentials);
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate2 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ const struct samr_Password *mach_password;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *account_name = cli_credentials_get_username(
+ machine_credentials);
+ uint32_t flags =
+ NETLOGON_NEG_AUTH2_ADS_FLAGS |
+ NETLOGON_NEG_SUPPORTS_AES;
+ enum netr_SchannelType sec_chan_type =
+ cli_credentials_get_secure_channel_type(machine_credentials);
+ /*
+ * Limit the number of attempts to generate a suitable session key.
+ */
+ const unsigned MAX_ITER = 4096;
+ unsigned i = 0;
+
+ mach_password = cli_credentials_get_nt_hash(machine_credentials, tctx);
+
+ r.in.server_name = NULL;
+ r.in.computer_name = computer_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+ credentials1.data[0] = target;
+ i = 0;
+ torture_comment(tctx, "Generating candidate session keys\n");
+ do {
+ TALLOC_FREE(creds);
+ i++;
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(
+ tctx,
+ r.out.result,
+ "ServerReqChallenge failed");
+
+ a.in.server_name = NULL;
+ a.in.account_name = account_name;
+ a.in.secure_channel_type = sec_chan_type;
+ a.in.computer_name = computer_name;
+ a.in.negotiate_flags = &flags;
+ a.out.negotiate_flags = &flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+
+ creds = netlogon_creds_client_init(
+ tctx,
+ a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1,
+ &credentials2,
+ mach_password,
+ &credentials3,
+ flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+ } while (credentials3.data[0] != 0 && i < MAX_ITER);
+
+ if (i >= MAX_ITER) {
+ torture_comment(
+ tctx,
+ "Unable to obtain a suitable session key, "
+ "after [%u] attempts\n",
+ i);
+ torture_fail(tctx, "Unable obtain suitable session key");
+ }
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerAuthenticate2_r(b, tctx, &a),
+ "ServerAuthenticate2 failed");
+ torture_assert_ntstatus_equal(
+ tctx,
+ a.out.result,
+ NT_STATUS_OK,
+ "ServerAuthenticate2 unexpected result code");
+
+ *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)
+{
+ struct netr_ServerPasswordSet r;
+ const char *password;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Authenticator credential, return_authenticator;
+ struct samr_Password new_password;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ password = generate_random_password(tctx, 8, 255);
+ E_md4hash(password, new_password.hash);
+
+ netlogon_creds_des_encrypt(creds, &new_password);
+
+ torture_comment(tctx, "Testing ServerPasswordSet on machine account\n");
+ torture_comment(tctx, "Changing machine account password to '%s'\n",
+ password);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet_r(b, tctx, &r),
+ "ServerPasswordSet failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet failed");
+
+ if (!netlogon_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);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet_r(b, tctx, &r),
+ "ServerPasswordSet (2) failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet (2) failed");
+
+ if (!netlogon_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_SetPassword_flags(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials,
+ uint32_t negotiate_flags)
+{
+ struct netr_ServerPasswordSet r;
+ const char *password;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Authenticator credential, return_authenticator;
+ struct samr_Password new_password;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+
+ if (!test_SetupCredentials2(p1, tctx, negotiate_flags,
+ machine_credentials,
+ cli_credentials_get_secure_channel_type(machine_credentials),
+ &creds)) {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ password = generate_random_password(tctx, 8, 255);
+ E_md4hash(password, new_password.hash);
+
+ netlogon_creds_des_encrypt(creds, &new_password);
+
+ torture_comment(tctx, "Testing ServerPasswordSet on machine account\n");
+ torture_comment(tctx, "Changing machine account password to '%s'\n",
+ password);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet_r(b, tctx, &r),
+ "ServerPasswordSet failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet failed");
+
+ if (!netlogon_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);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet_r(b, tctx, &r),
+ "ServerPasswordSet (2) failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet (2) failed");
+
+ if (!netlogon_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;
+}
+
+
+/*
+ generate a random password for password change tests
+*/
+static DATA_BLOB netlogon_very_rand_pass(TALLOC_CTX *mem_ctx, int len)
+{
+ int i;
+ DATA_BLOB password = data_blob_talloc(mem_ctx, NULL, len * 2 /* number of unicode chars */);
+ generate_random_buffer(password.data, password.length);
+
+ for (i=0; i < len; i++) {
+ if (((uint16_t *)password.data)[i] == 0) {
+ ((uint16_t *)password.data)[i] = 1;
+ }
+ }
+
+ return password;
+}
+
+/*
+ try a change password for our machine account
+*/
+static bool test_SetPassword2_with_flags(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials,
+ uint32_t flags)
+{
+ struct netr_ServerPasswordSet2 r;
+ const char *password;
+ DATA_BLOB new_random_pass;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_CryptPassword password_buf;
+ struct samr_Password nt_hash;
+ struct netr_Authenticator credential, return_authenticator;
+ struct netr_CryptPassword new_password;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+
+ if (!test_SetupCredentials2(p1, tctx, flags, machine_credentials,
+ cli_credentials_get_secure_channel_type(machine_credentials),
+ &creds)) {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ password = generate_random_password(tctx, 8, 255);
+ encode_pw_buffer(password_buf.data, password, STR_UNICODE);
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds, password_buf.data, 516);
+ } else {
+ netlogon_creds_arcfour_crypt(creds, password_buf.data, 516);
+ }
+
+ memcpy(new_password.data, password_buf.data, 512);
+ 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);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet2 failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet2 failed");
+
+ if (!netlogon_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);
+
+ /*
+ * As a consequence of CVE-2020-1472(ZeroLogon)
+ * Samba explicitly disallows the setting of an empty machine account
+ * password.
+ *
+ * Note that this may fail against Windows, and leave a machine account
+ * with an empty password.
+ */
+ password = "";
+ encode_pw_buffer(password_buf.data, password, STR_UNICODE);
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds, password_buf.data, 516);
+ } else {
+ netlogon_creds_arcfour_crypt(creds, password_buf.data, 516);
+ }
+ memcpy(new_password.data, password_buf.data, 512);
+ 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);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(
+ tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet2 failed");
+ torture_assert_ntstatus_equal(
+ tctx,
+ r.out.result,
+ NT_STATUS_WRONG_PASSWORD,
+ "ServerPasswordSet2 did not return NT_STATUS_WRONG_PASSWORD");
+
+ /* now try a random password */
+ password = generate_random_password(tctx, 8, 255);
+ encode_pw_buffer(password_buf.data, password, STR_UNICODE);
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds, password_buf.data, 516);
+ } else {
+ netlogon_creds_arcfour_crypt(creds, password_buf.data, 516);
+ }
+ memcpy(new_password.data, password_buf.data, 512);
+ 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);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet2 (2) failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet2 (2) failed");
+
+ if (!netlogon_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);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet (3) failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet (3) failed");
+
+ if (!netlogon_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");
+
+ new_random_pass = netlogon_very_rand_pass(tctx, 128);
+
+ /* now try a random stream of bytes for a password */
+ set_pw_in_buffer(password_buf.data, &new_random_pass);
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds, password_buf.data, 516);
+ } else {
+ netlogon_creds_arcfour_crypt(creds, password_buf.data, 516);
+ }
+
+ memcpy(new_password.data, password_buf.data, 512);
+ new_password.length = IVAL(password_buf.data, 512);
+
+ torture_comment(tctx,
+ "Testing a third ServerPasswordSet2 on machine account, with a completely random password\n");
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet (3) failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerPasswordSet (3) failed");
+
+ if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ mdfour(nt_hash.hash, new_random_pass.data, new_random_pass.length);
+
+ cli_credentials_set_password(machine_credentials, NULL, CRED_UNINITIALISED);
+ cli_credentials_set_nt_hash(machine_credentials, &nt_hash, CRED_SPECIFIED);
+
+ torture_assert (tctx,
+ test_SetupCredentials(p, tctx, machine_credentials, &creds),
+ "ServerPasswordSet failed to actually change the password");
+
+ return true;
+}
+
+/*
+ try to change the password of our machine account using a buffer of all zeros,
+ and a session key that encrypts that to all zeros.
+
+Note: The test does use sign and seal, it's purpose is to exercise
+ the detection code in dcesrv_netr_ServerPasswordSet2
+*/
+static bool test_SetPassword2_encrypted_to_all_zeros(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerPasswordSet2 r;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_CryptPassword password_buf;
+ struct netr_Authenticator credential, return_authenticator;
+ struct netr_CryptPassword new_password;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+
+ if (!test_ServerAuthenticate2_encrypts_to_zero(
+ tctx,
+ p1,
+ machine_credentials,
+ '\0',
+ &creds)) {
+
+ return false;
+ }
+
+ if (!test_SetupCredentialsPipe(
+ p1,
+ tctx,
+ machine_credentials,
+ creds,
+ DCERPC_SIGN | DCERPC_SEAL,
+ &p))
+ {
+ return false;
+ }
+ b = p->binding_handle;
+
+ 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 =
+ cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ ZERO_STRUCT(password_buf);
+
+ if (!(creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES)) {
+ torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES not set");
+ }
+ netlogon_creds_aes_encrypt(creds, password_buf.data, 516);
+ if(!all_zero(password_buf.data, 516)) {
+ torture_fail(tctx, "Password did not encrypt to all zeros\n");
+ }
+
+ memcpy(new_password.data, password_buf.data, 512);
+ new_password.length = IVAL(password_buf.data, 512);
+ torture_assert_int_equal(
+ tctx,
+ new_password.length,
+ 0,
+ "Length should have encrypted to 0");
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet2 zero length check failed");
+ torture_assert_ntstatus_equal(
+ tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, "");
+
+ return true;
+}
+
+/*
+ * Choose a session key that encrypts a password of all zeros to all zeros.
+ * Then try to set the password, using a zeroed buffer, with a non zero
+ * length.
+ *
+ * This exercises the password self encryption check.
+ *
+ * Note: The test does use sign and seal, it's purpose is to exercise
+ * the detection code in dcesrv_netr_ServerPasswordSet2
+*/
+static bool test_SetPassword2_password_encrypts_to_zero(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerPasswordSet2 r;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_CryptPassword password_buf;
+ struct netr_Authenticator credential, return_authenticator;
+ struct netr_CryptPassword new_password;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+
+ if (!test_ServerAuthenticate2_encrypts_to_zero(
+ tctx,
+ p1,
+ machine_credentials,
+ 0x00,
+ &creds)) {
+
+ return false;
+ }
+
+ if (!test_SetupCredentialsPipe(
+ p1,
+ tctx,
+ machine_credentials,
+ creds,
+ DCERPC_SIGN | DCERPC_SEAL,
+ &p))
+ {
+ return false;
+ }
+ b = p->binding_handle;
+
+ 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 =
+ cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ ZERO_STRUCT(password_buf);
+ SIVAL(password_buf.data, 512, 512);
+
+ if (!(creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES)) {
+ torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES not set");
+ }
+ netlogon_creds_aes_encrypt(creds, password_buf.data, 516);
+
+ memcpy(new_password.data, password_buf.data, 512);
+ new_password.length = IVAL(password_buf.data, 512);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet2 password encrypts to zero check failed");
+ torture_assert_ntstatus_equal(
+ tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, "");
+
+ return true;
+}
+
+/*
+ * Check that an all zero confounder, that encrypts to all zeros is
+ * rejected.
+ *
+ * Note: The test does use sign and seal, it's purpose is to exercise
+ * the detection code in dcesrv_netr_ServerPasswordSet2
+ */
+static bool test_SetPassword2_confounder(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerPasswordSet2 r;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_CryptPassword password_buf;
+ struct netr_Authenticator credential, return_authenticator;
+ struct netr_CryptPassword new_password;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+
+ if (!test_ServerAuthenticate2_encrypts_to_zero(
+ tctx,
+ p1,
+ machine_credentials,
+ '\0',
+ &creds)) {
+
+ return false;
+ }
+
+ if (!test_SetupCredentialsPipe(
+ p1,
+ tctx,
+ machine_credentials,
+ creds,
+ DCERPC_SIGN | DCERPC_SEAL,
+ &p))
+ {
+ return false;
+ }
+ b = p->binding_handle;
+
+ 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 =
+ cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ ZERO_STRUCT(password_buf);
+ password_buf.data[511] = 'A';
+ SIVAL(password_buf.data, 512, 2);
+
+ if (!(creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES)) {
+ torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES not set");
+ }
+ netlogon_creds_aes_encrypt(creds, password_buf.data, 516);
+
+ memcpy(new_password.data, password_buf.data, 512);
+ new_password.length = IVAL(password_buf.data, 512);
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet2 confounder check failed");
+ torture_assert_ntstatus_equal(
+ tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, "");
+
+ return true;
+}
+
+/*
+ * try a change password for our machine account, using an all zero
+ * request. This should fail on the zero length check.
+ *
+ * Note: This test uses ARC4 encryption to exercise the desired check.
+ */
+static bool test_SetPassword2_all_zeros(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerPasswordSet2 r;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_CryptPassword password_buf;
+ struct netr_Authenticator credential, return_authenticator;
+ struct netr_CryptPassword new_password;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ uint32_t flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; /* no AES desired here */
+
+ if (!test_SetupCredentials2(
+ p1,
+ tctx,
+ flags,
+ machine_credentials,
+ cli_credentials_get_secure_channel_type(machine_credentials),
+ &creds))
+ {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(
+ p1,
+ tctx,
+ machine_credentials,
+ creds,
+ DCERPC_SIGN | DCERPC_SEAL,
+ &p))
+ {
+ return false;
+ }
+ b = p->binding_handle;
+
+ 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 =
+ cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ ZERO_STRUCT(password_buf.data);
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES enabled\n");
+ }
+ netlogon_creds_arcfour_crypt(creds, password_buf.data, 516);
+
+ memcpy(new_password.data, password_buf.data, 512);
+ new_password.length = IVAL(password_buf.data, 512);
+
+ torture_comment(
+ tctx,
+ "Testing ServerPasswordSet2 on machine account\n");
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet2 zero length check failed");
+ torture_assert_ntstatus_equal(
+ tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, "");
+
+ return true;
+}
+
+/*
+ try a change password for our machine account, using a maximum length
+ password
+*/
+static bool test_SetPassword2_maximum_length_password(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerPasswordSet2 r;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_CryptPassword password_buf;
+ struct netr_Authenticator credential, return_authenticator;
+ struct netr_CryptPassword new_password;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ uint32_t flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ DATA_BLOB new_random_pass = data_blob_null;
+
+ if (!test_SetupCredentials2(
+ p1,
+ tctx,
+ flags,
+ machine_credentials,
+ cli_credentials_get_secure_channel_type(machine_credentials),
+ &creds))
+ {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(
+ p1,
+ tctx,
+ machine_credentials,
+ creds,
+ DCERPC_SIGN | DCERPC_SEAL,
+ &p))
+ {
+ return false;
+ }
+ b = p->binding_handle;
+
+ 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 =
+ cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ new_random_pass = netlogon_very_rand_pass(tctx, 256);
+ set_pw_in_buffer(password_buf.data, &new_random_pass);
+ SIVAL(password_buf.data, 512, 512);
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds, password_buf.data, 516);
+ } else {
+ netlogon_creds_arcfour_crypt(creds, password_buf.data, 516);
+ }
+
+ memcpy(new_password.data, password_buf.data, 512);
+ new_password.length = IVAL(password_buf.data, 512);
+
+ torture_comment(
+ tctx,
+ "Testing ServerPasswordSet2 on machine account\n");
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet2 zero length check failed");
+ torture_assert_ntstatus_equal(
+ tctx, r.out.result, NT_STATUS_OK, "");
+
+ return true;
+}
+
+/*
+ try a change password for our machine account, using a password of
+ all zeros, and a non zero password length.
+
+ This test relies on the buffer being encrypted with ARC4, to
+ trigger the appropriate check in the rpc server code
+*/
+static bool test_SetPassword2_all_zero_password(
+ struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerPasswordSet2 r;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_CryptPassword password_buf;
+ struct netr_Authenticator credential, return_authenticator;
+ struct netr_CryptPassword new_password;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ uint32_t flags = NETLOGON_NEG_AUTH2_ADS_FLAGS; /* no AES desired here */
+
+ if (!test_SetupCredentials2(
+ p1,
+ tctx,
+ flags,
+ machine_credentials,
+ cli_credentials_get_secure_channel_type(machine_credentials),
+ &creds))
+ {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(
+ p1,
+ tctx,
+ machine_credentials,
+ creds,
+ DCERPC_SIGN | DCERPC_SEAL,
+ &p))
+ {
+ return false;
+ }
+ b = p->binding_handle;
+
+ 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 =
+ cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ ZERO_STRUCT(password_buf.data);
+ SIVAL(password_buf.data, 512, 128);
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ torture_fail(tctx, "NETLOGON_NEG_SUPPORTS_AES set");
+ }
+ netlogon_creds_arcfour_crypt(creds, password_buf.data, 516);
+
+ memcpy(new_password.data, password_buf.data, 512);
+ new_password.length = IVAL(password_buf.data, 512);
+
+ torture_comment(
+ tctx,
+ "Testing ServerPasswordSet2 on machine account\n");
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(
+ tctx,
+ dcerpc_netr_ServerPasswordSet2_r(b, tctx, &r),
+ "ServerPasswordSet2 all zero password check failed");
+ torture_assert_ntstatus_equal(
+ tctx, r.out.result, NT_STATUS_WRONG_PASSWORD, "");
+
+ return true;
+}
+
+
+static bool test_SetPassword2(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ return test_SetPassword2_with_flags(tctx, p, machine_credentials, NETLOGON_NEG_AUTH2_ADS_FLAGS);
+}
+
+static bool test_SetPassword2_AES(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ return test_SetPassword2_with_flags(tctx, p, machine_credentials, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES);
+}
+
+static bool test_GetPassword(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_ServerPasswordGet r;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Authenticator credential;
+ NTSTATUS status;
+ struct netr_Authenticator return_authenticator;
+ struct samr_Password password;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ netlogon_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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ 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_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordGet");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "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 netlogon_creds_CredentialState *creds;
+ struct netr_Authenticator credential;
+ struct netr_Authenticator return_authenticator;
+ struct samr_Password password, password2;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ netlogon_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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.new_owf_password = &password;
+ r.out.old_owf_password = &password2;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerTrustPasswordsGet_r(b, tctx, &r),
+ "ServerTrustPasswordsGet failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerTrustPasswordsGet failed");
+
+ return true;
+}
+
+/*
+ try a netlogon SamLogon
+*/
+static bool test_netlogon_ops_args(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct netlogon_creds_CredentialState *creds,
+ bool null_domain)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogon r;
+ struct netr_Authenticator auth, auth2;
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative;
+ struct netr_NetworkInfo ninfo;
+ DATA_BLOB names_blob, chal, lm_resp, nt_resp;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ int flags = CLI_CRED_NTLM_AUTH;
+ if (lpcfg_client_lanman_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lpcfg_client_ntlmv2_auth(tctx->lp_ctx) && !null_domain) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ cli_credentials_get_ntlm_username_domain(samba_cmdline_get_creds(),
+ tctx,
+ &ninfo.identity_info.account_name.string,
+ &ninfo.identity_info.domain_name.string);
+
+ if (null_domain) {
+ ninfo.identity_info.domain_name.string = NULL;
+ }
+
+ generate_random_buffer(ninfo.challenge,
+ sizeof(ninfo.challenge));
+ chal = data_blob_const(ninfo.challenge,
+ sizeof(ninfo.challenge));
+
+ names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(credentials),
+ cli_credentials_get_domain(credentials));
+
+ status = cli_credentials_get_ntlm_response(
+ samba_cmdline_get_creds(), tctx,
+ &flags,
+ chal,
+ NULL, /* server_timestamp */
+ 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 = 0;
+ ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials);
+
+ logon.network = &ninfo;
+
+ 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 = NetlogonNetworkInformation;
+ r.in.logon = &logon;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+
+ d_printf("Testing LogonSamLogon with name %s\n", ninfo.identity_info.account_name.string);
+
+ for (i=2;i<=3;i++) {
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = i;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds,
+ &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+ torture_assert_int_equal(tctx, *r.out.authoritative, 1,
+ "LogonSamLogon invalid *r.out.authoritative");
+ }
+
+ /* this makes sure we get the unmarshalling right for invalid levels */
+ for (i=52;i<53;i++) {
+ ZERO_STRUCT(auth2);
+ /* the authenticator should be ignored by the server */
+ generate_random_buffer((uint8_t *) &auth, sizeof(auth));
+
+ r.in.validation_level = i;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result,
+ NT_STATUS_INVALID_INFO_CLASS,
+ "LogonSamLogon failed");
+
+ torture_assert_int_equal(tctx, *r.out.authoritative, 1,
+ "LogonSamLogon invalid *r.out.authoritative");
+ torture_assert(tctx,
+ all_zero((uint8_t *)&auth2, sizeof(auth2)),
+ "Return authenticator non zero");
+ }
+
+ for (i=2;i<=3;i++) {
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = i;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds,
+ &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+ torture_assert_int_equal(tctx, *r.out.authoritative, 1,
+ "LogonSamLogon invalid *r.out.authoritative");
+ }
+
+ r.in.logon_level = 52;
+
+ for (i=2;i<=3;i++) {
+ ZERO_STRUCT(auth2);
+ /* the authenticator should be ignored by the server */
+ generate_random_buffer((uint8_t *) &auth, sizeof(auth));
+
+ r.in.validation_level = i;
+
+ torture_comment(tctx, "Testing SamLogon with validation level %d and a NULL credential\n", i);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER,
+ "LogonSamLogon expected INVALID_PARAMETER");
+
+ torture_assert(tctx,
+ all_zero((uint8_t *)&auth2, sizeof(auth2)),
+ "Return authenticator non zero");
+ torture_assert_int_equal(tctx, *r.out.authoritative, 1,
+ "LogonSamLogon invalid *r.out.authoritative");
+ }
+
+ r.in.credential = NULL;
+
+ for (i=2;i<=3;i++) {
+ ZERO_STRUCT(auth2);
+
+ r.in.validation_level = i;
+
+ torture_comment(tctx, "Testing SamLogon with validation level %d and a NULL credential\n", i);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER,
+ "LogonSamLogon expected INVALID_PARAMETER");
+
+ torture_assert(tctx,
+ all_zero((uint8_t *)&auth2, sizeof(auth2)),
+ "Return authenticator non zero");
+ torture_assert_int_equal(tctx, *r.out.authoritative, 1,
+ "LogonSamLogon invalid *r.out.authoritative");
+ }
+
+ r.in.logon_level = NetlogonNetworkInformation;
+ r.in.credential = &auth;
+
+ for (i=2;i<=3;i++) {
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = i;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds,
+ &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+ torture_assert_int_equal(tctx, *r.out.authoritative, 1,
+ "LogonSamLogon invalid *r.out.authoritative");
+ }
+
+ return true;
+}
+
+bool test_netlogon_ops(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct netlogon_creds_CredentialState *creds)
+{
+ return test_netlogon_ops_args(p, tctx, credentials, creds, false);
+}
+
+/*
+ try a netlogon GetCapabilities
+*/
+bool test_netlogon_capabilities(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct netlogon_creds_CredentialState *creds)
+{
+ NTSTATUS status;
+ struct netr_LogonGetCapabilities r;
+ union netr_Capabilities capabilities;
+ struct netr_Authenticator auth, return_auth;
+ struct netlogon_creds_CredentialState tmp_creds;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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 = &return_auth;
+ r.in.query_level = 1;
+ r.out.capabilities = &capabilities;
+ r.out.return_authenticator = &return_auth;
+
+ torture_comment(tctx, "Testing LogonGetCapabilities with query_level=0\n");
+
+ r.in.query_level = 0;
+ ZERO_STRUCT(return_auth);
+
+ /*
+ * we need to operate on a temporary copy of creds
+ * because dcerpc_netr_LogonGetCapabilities with
+ * an unknown query level returns DCERPC_NCA_S_FAULT_INVALID_TAG
+ * => NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE
+ * without looking a the authenticator.
+ */
+ tmp_creds = *creds;
+ netlogon_creds_client_authenticator(&tmp_creds, &auth);
+
+ status = dcerpc_netr_LogonGetCapabilities_r(b, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE,
+ "LogonGetCapabilities query_level=0 failed");
+
+ torture_comment(tctx, "Testing LogonGetCapabilities with query_level=3\n");
+
+ r.in.query_level = 3;
+ ZERO_STRUCT(return_auth);
+
+ /*
+ * we need to operate on a temporary copy of creds
+ * because dcerpc_netr_LogonGetCapabilities with
+ * an unknown query level returns DCERPC_NCA_S_FAULT_INVALID_TAG
+ * => NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE
+ * without looking a the authenticator.
+ */
+ tmp_creds = *creds;
+ netlogon_creds_client_authenticator(&tmp_creds, &auth);
+
+ status = dcerpc_netr_LogonGetCapabilities_r(b, tctx, &r);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE,
+ "LogonGetCapabilities query_level=0 failed");
+
+ torture_comment(tctx, "Testing LogonGetCapabilities with query_level=1\n");
+
+ r.in.query_level = 1;
+ ZERO_STRUCT(return_auth);
+
+ /*
+ * we need to operate on a temporary copy of creds
+ * because dcerpc_netr_LogonGetCapabilities was
+ * dcerpc_netr_DummyFunction and returns NT_STATUS_NOT_IMPLEMENTED
+ * without looking a the authenticator.
+ */
+ tmp_creds = *creds;
+ netlogon_creds_client_authenticator(&tmp_creds, &auth);
+
+ status = dcerpc_netr_LogonGetCapabilities_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonGetCapabilities failed");
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_IMPLEMENTED)) {
+ return true;
+ }
+
+ *creds = tmp_creds;
+
+ torture_assert(tctx, netlogon_creds_client_check(creds,
+ &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ torture_assert_int_equal(tctx, creds->negotiate_flags,
+ capabilities.server_capabilities,
+ "negotiate flags");
+
+ torture_comment(tctx, "Testing LogonGetCapabilities with query_level=2\n");
+
+ r.in.query_level = 2;
+ ZERO_STRUCT(return_auth);
+
+ /*
+ * we need to operate on a temporary copy of creds
+ * because dcerpc_netr_LogonGetCapabilities with
+ * an query level 2 may returns DCERPC_NCA_S_FAULT_INVALID_TAG
+ * => NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE
+ * without looking a the authenticator.
+ */
+ tmp_creds = *creds;
+ netlogon_creds_client_authenticator(&tmp_creds, &auth);
+
+ status = dcerpc_netr_LogonGetCapabilities_r(b, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE)) {
+ /*
+ * an server without KB5028166 returns
+ * DCERPC_NCA_S_FAULT_INVALID_TAG =>
+ * NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE
+ */
+ return true;
+ }
+ torture_assert_ntstatus_ok(tctx, status, "LogonGetCapabilities query_level=2 failed");
+
+ *creds = tmp_creds;
+
+ torture_assert(tctx, netlogon_creds_client_check(creds,
+ &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ torture_assert_int_equal(tctx, creds->negotiate_flags,
+ capabilities.server_capabilities,
+ "negotiate flags");
+
+ return true;
+}
+
+/*
+ try a netlogon SamLogon
+*/
+static bool test_SamLogon(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ struct netlogon_creds_CredentialState *creds;
+
+ if (!test_SetupCredentials(p, tctx, credentials, &creds)) {
+ return false;
+ }
+
+ return test_netlogon_ops(p, tctx, credentials, creds);
+}
+
+static bool test_invalidAuthenticate2(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ struct netlogon_creds_CredentialState *creds;
+ uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+
+ torture_comment(tctx, "Testing invalidAuthenticate2\n");
+
+ if (!test_SetupCredentials2(p, tctx, flags,
+ credentials,
+ cli_credentials_get_secure_channel_type(credentials),
+ &creds)) {
+ return false;
+ }
+
+ if (!test_SetupCredentials2ex(p, tctx, flags,
+ credentials,
+ "1234567890123456",
+ cli_credentials_get_secure_channel_type(credentials),
+ STATUS_BUFFER_OVERFLOW,
+ &creds)) {
+ return false;
+ }
+
+ if (!test_SetupCredentials2ex(p, tctx, flags,
+ credentials,
+ "123456789012345",
+ cli_credentials_get_secure_channel_type(credentials),
+ NT_STATUS_OK,
+ &creds)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_ServerReqChallengeGlobal(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+ struct dcerpc_binding_handle *b1 = p1->binding_handle;
+ struct dcerpc_pipe *p2 = NULL;
+ struct dcerpc_binding_handle *b2 = NULL;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ torture_assert(tctx, machine_name != NULL, "machine_name");
+ plain_pass = cli_credentials_get_password(machine_credentials);
+ torture_assert(tctx, plain_pass != NULL, "plain_pass");
+
+ torture_comment(tctx, "Testing ServerReqChallenge on b1\n");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p2, p1->binding,
+ &ndr_table_netlogon,
+ machine_credentials,
+ tctx->ev, tctx->lp_ctx),
+ "dcerpc_pipe_connect_b failed");
+ b2 = p2->binding_handle;
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r),
+ "ServerReqChallenge failed on b1");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1");
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+ a.out.negotiate_flags = &flags;
+ a.out.rid = &rid;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a),
+ "ServerAuthenticate3 failed on b2");
+ torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b2");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ return true;
+}
+
+/*
+ * Test the re-use of the challenge is not possible on a third
+ * connection, after first useing it second one.
+ */
+
+static bool test_ServerReqChallengeReuseGlobal(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+ struct dcerpc_binding_handle *b1 = p1->binding_handle;
+ struct dcerpc_pipe *p2 = NULL;
+ struct dcerpc_binding_handle *b2 = NULL;
+ struct dcerpc_pipe *p3 = NULL;
+ struct dcerpc_binding_handle *b3 = NULL;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ torture_assert(tctx, machine_name != NULL, "machine_name");
+ plain_pass = cli_credentials_get_password(machine_credentials);
+ torture_assert(tctx, plain_pass != NULL, "plain_pass");
+
+ torture_comment(tctx, "Testing ServerReqChallenge on b1\n");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p2, p1->binding,
+ &ndr_table_netlogon,
+ machine_credentials,
+ tctx->ev, tctx->lp_ctx),
+ "dcerpc_pipe_connect_b failed");
+ b2 = p2->binding_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p3, p1->binding,
+ &ndr_table_netlogon,
+ machine_credentials,
+ tctx->ev, tctx->lp_ctx),
+ "dcerpc_pipe_connect_b failed");
+ b3 = p3->binding_handle;
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r),
+ "ServerReqChallenge failed on b1");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1");
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+ a.out.negotiate_flags = &flags;
+ a.out.rid = &rid;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a),
+ "ServerAuthenticate3 failed on b2");
+ torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b2");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ /* We have to re-run this part */
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b3, tctx, &a),
+ "ServerAuthenticate3 failed on b3");
+ torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED,
+ "ServerAuthenticate3 should have failed on b3, due to credential reuse");
+ return true;
+}
+
+/*
+ * Test if use of the per-pipe challenge will wipe out the globally cached challenge
+ */
+static bool test_ServerReqChallengeReuseGlobal2(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+ struct dcerpc_binding_handle *b1 = p1->binding_handle;
+ struct dcerpc_pipe *p2 = NULL;
+ struct dcerpc_binding_handle *b2 = NULL;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ torture_assert(tctx, machine_name != NULL, "machine_name");
+ plain_pass = cli_credentials_get_password(machine_credentials);
+ torture_assert(tctx, plain_pass != NULL, "plain_pass");
+
+ torture_comment(tctx, "Testing ServerReqChallenge on b1\n");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p2, p1->binding,
+ &ndr_table_netlogon,
+ machine_credentials,
+ tctx->ev, tctx->lp_ctx),
+ "dcerpc_pipe_connect_b failed");
+ b2 = p2->binding_handle;
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r),
+ "ServerReqChallenge failed on b1");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1");
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+ a.out.negotiate_flags = &flags;
+ a.out.rid = &rid;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b1, tctx, &a),
+ "ServerAuthenticate3 failed on b");
+ torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ /* We have to re-run this part */
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a),
+ "ServerAuthenticate3 failed on b2");
+ torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED,
+ "ServerAuthenticate3 should have failed on b2, due to credential reuse");
+ return true;
+}
+
+/*
+ * Test if use of the globally cached challenge will wipe out the
+ * per-pipe challenge
+ */
+static bool test_ServerReqChallengeReuseGlobal3(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+ struct dcerpc_binding_handle *b1 = p1->binding_handle;
+ struct dcerpc_pipe *p2 = NULL;
+ struct dcerpc_binding_handle *b2 = NULL;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ torture_assert(tctx, machine_name != NULL, "machine_name");
+ plain_pass = cli_credentials_get_password(machine_credentials);
+ torture_assert(tctx, plain_pass != NULL, "plain_pass");
+
+ torture_comment(tctx, "Testing ServerReqChallenge on b1\n");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p2, p1->binding,
+ &ndr_table_netlogon,
+ machine_credentials,
+ tctx->ev, tctx->lp_ctx),
+ "dcerpc_pipe_connect_b failed");
+ b2 = p2->binding_handle;
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r),
+ "ServerReqChallenge failed on b1");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1");
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+ a.out.negotiate_flags = &flags;
+ a.out.rid = &rid;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3 on b2\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a),
+ "ServerAuthenticate3 failed on b2");
+ torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ /* We have to re-run this part */
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b1, tctx, &a),
+ "ServerAuthenticate3 failed on b1");
+ torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED,
+ "ServerAuthenticate3 should have failed on b1, due to credential reuse");
+ return true;
+}
+
+/*
+ * Test if more than one globally cached challenge works
+ */
+static bool test_ServerReqChallengeReuseGlobal4(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials1_random,
+ credentials2, credentials3, credentials_discard;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+ struct dcerpc_binding_handle *b1 = p1->binding_handle;
+ struct dcerpc_pipe *p2 = NULL;
+ struct dcerpc_binding_handle *b2 = NULL;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ torture_assert(tctx, machine_name != NULL, "machine_name");
+ plain_pass = cli_credentials_get_password(machine_credentials);
+ torture_assert(tctx, plain_pass != NULL, "plain_pass");
+
+ torture_comment(tctx, "Testing ServerReqChallenge on b1\n");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p2, p1->binding,
+ &ndr_table_netlogon,
+ machine_credentials,
+ tctx->ev, tctx->lp_ctx),
+ "dcerpc_pipe_connect_b failed");
+ b2 = p2->binding_handle;
+
+ r.in.server_name = NULL;
+ r.in.computer_name = "CHALTEST1";
+ r.in.credentials = &credentials1_random;
+ r.out.return_credentials = &credentials_discard;
+
+ netlogon_creds_random_challenge(&credentials1_random);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r),
+ "ServerReqChallenge failed on b1");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1");
+
+ /* Now ask for the actual client name */
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r),
+ "ServerReqChallenge failed on b1");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = "CHALTEST2";
+ r.in.credentials = &credentials1_random;
+ r.out.return_credentials = &credentials_discard;
+
+ netlogon_creds_random_challenge(&credentials1_random);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b1, tctx, &r),
+ "ServerReqChallenge failed on b1");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1");
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+ a.out.negotiate_flags = &flags;
+ a.out.rid = &rid;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3 on b2 (must use global credentials)\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b2, tctx, &a),
+ "ServerAuthenticate3 failed on b2");
+ torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed on b2");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ /* We have to re-run this part */
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b1, tctx, &a),
+ "ServerAuthenticate3 failed on b1");
+ torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED,
+ "ServerAuthenticate3 should have failed on b1, due to credential reuse");
+ return true;
+}
+
+static bool test_ServerReqChallengeReuse(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t flags = NETLOGON_NEG_AUTH2_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential credentials1, credentials2, credentials3;
+ struct netlogon_creds_CredentialState *creds;
+ struct samr_Password mach_password;
+ uint32_t rid;
+ const char *machine_name;
+ const char *plain_pass;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ torture_assert(tctx, machine_name != NULL, "machine_name");
+ plain_pass = cli_credentials_get_password(machine_credentials);
+ torture_assert(tctx, plain_pass != NULL, "plain_pass");
+
+ torture_comment(tctx, "Testing ServerReqChallenge on b1\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &credentials1;
+ r.out.return_credentials = &credentials2;
+
+ netlogon_creds_random_challenge(&credentials1);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed on b1");
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &flags;
+ a.in.credentials = &credentials3;
+ a.out.return_credentials = &credentials3;
+ a.out.negotiate_flags = &flags;
+ a.out.rid = &rid;
+
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a),
+ "ServerAuthenticate3 failed");
+ torture_assert_ntstatus_ok(tctx, a.out.result, "ServerAuthenticate3 failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &credentials3), "Credential chaining failed");
+
+ /* We have to re-run this part */
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a),
+ "ServerAuthenticate3 failed");
+ torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED,
+ "ServerAuthenticate3 should have failed on b3, due to credential reuse");
+
+ ZERO_STRUCT(credentials1.data);
+ ZERO_STRUCT(credentials2.data);
+ creds = netlogon_creds_client_init(tctx, a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &credentials1, &credentials2,
+ &mach_password, &credentials3,
+ flags);
+
+ torture_assert(tctx, creds != NULL, "memory allocation");
+
+ torture_comment(tctx, "Testing ServerAuthenticate3 with zero'ed challenge\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a),
+ "ServerAuthenticate3 failed");
+ torture_assert_ntstatus_equal(tctx, a.out.result, NT_STATUS_ACCESS_DENIED,
+ "ServerAuthenticate3 should have failed on b3, due to credential reuse");
+ return true;
+}
+
+static bool test_SamLogon_NULL_domain(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ struct netlogon_creds_CredentialState *creds;
+
+ if (!test_SetupCredentials(p, tctx, credentials, &creds)) {
+ return false;
+ }
+
+ return test_netlogon_ops_args(p, tctx, credentials, creds, true);
+}
+
+/* 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)
+{
+ struct netr_DatabaseSync r;
+ struct netlogon_creds_CredentialState *creds;
+ const uint32_t database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS};
+ int i;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ struct netr_Authenticator credential, return_authenticator;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ ZERO_STRUCT(return_authenticator);
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.preferredmaximumlength = (uint32_t)-1;
+ r.in.return_authenticator = &return_authenticator;
+ r.out.delta_enum_array = &delta_enum_array;
+ r.out.return_authenticator = &return_authenticator;
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint32_t sync_context = 0;
+
+ r.in.database_id = database_ids[i];
+ r.in.sync_context = &sync_context;
+ r.out.sync_context = &sync_context;
+
+ torture_comment(tctx, "Testing DatabaseSync of id %d\n", r.in.database_id);
+
+ do {
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ r.in.credential = &credential;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_DatabaseSync_r(b, tctx, &r),
+ "DatabaseSync failed");
+ if (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES))
+ break;
+
+ /* Native mode servers don't do this */
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_SUPPORTED)) {
+ return true;
+ }
+ torture_assert_ntstatus_ok(tctx, r.out.result, "DatabaseSync");
+
+ if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ if (delta_enum_array &&
+ delta_enum_array->num_deltas > 0 &&
+ delta_enum_array->delta_enum[0].delta_type == NETR_DELTA_DOMAIN &&
+ delta_enum_array->delta_enum[0].delta_union.domain) {
+ sequence_nums[r.in.database_id] =
+ 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(r.out.result, 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)
+{
+ struct netr_DatabaseDeltas r;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Authenticator credential;
+ struct netr_Authenticator return_authenticator;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ const uint32_t database_ids[] = {0, 1, 2};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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);
+ r.out.return_authenticator = &return_authenticator;
+ r.out.delta_enum_array = &delta_enum_array;
+
+ 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 {
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_DatabaseDeltas_r(b, tctx, &r),
+ "DatabaseDeltas failed");
+ if (NT_STATUS_EQUAL(r.out.result,
+ NT_STATUS_SYNCHRONIZATION_REQUIRED)) {
+ torture_comment(tctx, "not considering %s to be an error\n",
+ nt_errstr(r.out.result));
+ return true;
+ }
+ if (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES))
+ break;
+
+ torture_assert_ntstatus_ok(tctx, r.out.result, "DatabaseDeltas");
+
+ if (!netlogon_creds_client_check(creds, &return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ (*r.in.sequence_num)++;
+ } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+static bool test_DatabaseRedo(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_DatabaseRedo r;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Authenticator credential;
+ struct netr_Authenticator return_authenticator;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ struct netr_ChangeLogEntry e;
+ struct dom_sid null_sid, *sid;
+ int i,d;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(null_sid);
+
+ sid = dom_sid_parse_talloc(tctx, "S-1-5-21-1111111111-2222222222-333333333-500");
+
+ {
+
+ struct {
+ uint32_t rid;
+ uint16_t flags;
+ uint8_t db_index;
+ uint8_t delta_type;
+ struct dom_sid sid;
+ const char *name;
+ NTSTATUS expected_error;
+ uint32_t expected_num_results;
+ uint8_t expected_delta_type_1;
+ uint8_t expected_delta_type_2;
+ const char *comment;
+ } changes[] = {
+
+ /* SAM_DATABASE_DOMAIN */
+
+ {
+ .rid = 0,
+ .flags = 0,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = NETR_DELTA_MODIFY_COUNT,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED,
+ .expected_num_results = 0,
+ .comment = "NETR_DELTA_MODIFY_COUNT"
+ },
+ {
+ .rid = 0,
+ .flags = 0,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = 0,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DOMAIN,
+ .comment = "NULL DELTA"
+ },
+ {
+ .rid = 0,
+ .flags = 0,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = NETR_DELTA_DOMAIN,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DOMAIN,
+ .comment = "NETR_DELTA_DOMAIN"
+ },
+ {
+ .rid = DOMAIN_RID_ADMINISTRATOR,
+ .flags = 0,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = NETR_DELTA_USER,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_USER,
+ .comment = "NETR_DELTA_USER by rid 500"
+ },
+ {
+ .rid = DOMAIN_RID_GUEST,
+ .flags = 0,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = NETR_DELTA_USER,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_USER,
+ .comment = "NETR_DELTA_USER by rid 501"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_SID_INCLUDED,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = NETR_DELTA_USER,
+ .sid = *sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DELETE_USER,
+ .comment = "NETR_DELTA_USER by sid and flags"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_SID_INCLUDED,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = NETR_DELTA_USER,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DELETE_USER,
+ .comment = "NETR_DELTA_USER by null_sid and flags"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_NAME_INCLUDED,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = NETR_DELTA_USER,
+ .sid = null_sid,
+ .name = "administrator",
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DELETE_USER,
+ .comment = "NETR_DELTA_USER by name 'administrator'"
+ },
+ {
+ .rid = DOMAIN_RID_ADMINS,
+ .flags = 0,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = NETR_DELTA_GROUP,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 2,
+ .expected_delta_type_1 = NETR_DELTA_GROUP,
+ .expected_delta_type_2 = NETR_DELTA_GROUP_MEMBER,
+ .comment = "NETR_DELTA_GROUP by rid 512"
+ },
+ {
+ .rid = DOMAIN_RID_ADMINS,
+ .flags = 0,
+ .db_index = SAM_DATABASE_DOMAIN,
+ .delta_type = NETR_DELTA_GROUP_MEMBER,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 2,
+ .expected_delta_type_1 = NETR_DELTA_GROUP,
+ .expected_delta_type_2 = NETR_DELTA_GROUP_MEMBER,
+ .comment = "NETR_DELTA_GROUP_MEMBER by rid 512"
+ },
+
+
+ /* SAM_DATABASE_BUILTIN */
+
+ {
+ .rid = 0,
+ .flags = 0,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = NETR_DELTA_MODIFY_COUNT,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED,
+ .expected_num_results = 0,
+ .comment = "NETR_DELTA_MODIFY_COUNT"
+ },
+ {
+ .rid = 0,
+ .flags = 0,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = NETR_DELTA_DOMAIN,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DOMAIN,
+ .comment = "NETR_DELTA_DOMAIN"
+ },
+ {
+ .rid = DOMAIN_RID_ADMINISTRATOR,
+ .flags = 0,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = NETR_DELTA_USER,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DELETE_USER,
+ .comment = "NETR_DELTA_USER by rid 500"
+ },
+ {
+ .rid = 0,
+ .flags = 0,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = NETR_DELTA_USER,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DELETE_USER,
+ .comment = "NETR_DELTA_USER"
+ },
+ {
+ .rid = 544,
+ .flags = 0,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = NETR_DELTA_ALIAS,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 2,
+ .expected_delta_type_1 = NETR_DELTA_ALIAS,
+ .expected_delta_type_2 = NETR_DELTA_ALIAS_MEMBER,
+ .comment = "NETR_DELTA_ALIAS by rid 544"
+ },
+ {
+ .rid = 544,
+ .flags = 0,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = NETR_DELTA_ALIAS_MEMBER,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 2,
+ .expected_delta_type_1 = NETR_DELTA_ALIAS,
+ .expected_delta_type_2 = NETR_DELTA_ALIAS_MEMBER,
+ .comment = "NETR_DELTA_ALIAS_MEMBER by rid 544"
+ },
+ {
+ .rid = 544,
+ .flags = 0,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = 0,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DOMAIN,
+ .comment = "NULL DELTA by rid 544"
+ },
+ {
+ .rid = 544,
+ .flags = NETR_CHANGELOG_SID_INCLUDED,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = 0,
+ .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32-544"),
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DOMAIN,
+ .comment = "NULL DELTA by rid 544 sid S-1-5-32-544 and flags"
+ },
+ {
+ .rid = 544,
+ .flags = NETR_CHANGELOG_SID_INCLUDED,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = NETR_DELTA_ALIAS,
+ .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32-544"),
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 2,
+ .expected_delta_type_1 = NETR_DELTA_ALIAS,
+ .expected_delta_type_2 = NETR_DELTA_ALIAS_MEMBER,
+ .comment = "NETR_DELTA_ALIAS by rid 544 and sid S-1-5-32-544 and flags"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_SID_INCLUDED,
+ .db_index = SAM_DATABASE_BUILTIN,
+ .delta_type = NETR_DELTA_ALIAS,
+ .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32-544"),
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DELETE_ALIAS,
+ .comment = "NETR_DELTA_ALIAS by sid S-1-5-32-544 and flags"
+ },
+
+ /* SAM_DATABASE_PRIVS */
+
+ {
+ .rid = 0,
+ .flags = 0,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = 0,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_ACCESS_DENIED,
+ .expected_num_results = 0,
+ .comment = "NULL DELTA"
+ },
+ {
+ .rid = 0,
+ .flags = 0,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_MODIFY_COUNT,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED,
+ .expected_num_results = 0,
+ .comment = "NETR_DELTA_MODIFY_COUNT"
+ },
+ {
+ .rid = 0,
+ .flags = 0,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_POLICY,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_POLICY,
+ .comment = "NETR_DELTA_POLICY"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_SID_INCLUDED,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_POLICY,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_POLICY,
+ .comment = "NETR_DELTA_POLICY by null sid and flags"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_SID_INCLUDED,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_POLICY,
+ .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32"),
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_POLICY,
+ .comment = "NETR_DELTA_POLICY by sid S-1-5-32 and flags"
+ },
+ {
+ .rid = DOMAIN_RID_ADMINISTRATOR,
+ .flags = 0,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_ACCOUNT,
+ .sid = null_sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED, /* strange */
+ .expected_num_results = 0,
+ .comment = "NETR_DELTA_ACCOUNT by rid 500"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_SID_INCLUDED,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_ACCOUNT,
+ .sid = *dom_sid_parse_talloc(tctx, "S-1-1-0"),
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_ACCOUNT,
+ .comment = "NETR_DELTA_ACCOUNT by sid S-1-1-0 and flags"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_SID_INCLUDED |
+ NETR_CHANGELOG_IMMEDIATE_REPL_REQUIRED,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_ACCOUNT,
+ .sid = *dom_sid_parse_talloc(tctx, "S-1-1-0"),
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_ACCOUNT,
+ .comment = "NETR_DELTA_ACCOUNT by sid S-1-1-0 and 2 flags"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_SID_INCLUDED |
+ NETR_CHANGELOG_NAME_INCLUDED,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_ACCOUNT,
+ .sid = *dom_sid_parse_talloc(tctx, "S-1-1-0"),
+ .name = NULL,
+ .expected_error = NT_STATUS_INVALID_PARAMETER,
+ .expected_num_results = 0,
+ .comment = "NETR_DELTA_ACCOUNT by sid S-1-1-0 and invalid flags"
+ },
+ {
+ .rid = DOMAIN_RID_ADMINISTRATOR,
+ .flags = NETR_CHANGELOG_SID_INCLUDED,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_ACCOUNT,
+ .sid = *sid,
+ .name = NULL,
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DELETE_ACCOUNT,
+ .comment = "NETR_DELTA_ACCOUNT by rid 500, sid and flags"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_NAME_INCLUDED,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_SECRET,
+ .sid = null_sid,
+ .name = "IsurelydontexistIhope",
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_DELETE_SECRET,
+ .comment = "NETR_DELTA_SECRET by name 'IsurelydontexistIhope' and flags"
+ },
+ {
+ .rid = 0,
+ .flags = NETR_CHANGELOG_NAME_INCLUDED,
+ .db_index = SAM_DATABASE_PRIVS,
+ .delta_type = NETR_DELTA_SECRET,
+ .sid = null_sid,
+ .name = "G$BCKUPKEY_P",
+ .expected_error = NT_STATUS_OK,
+ .expected_num_results = 1,
+ .expected_delta_type_1 = NETR_DELTA_SECRET,
+ .comment = "NETR_DELTA_SECRET by name 'G$BCKUPKEY_P' and flags"
+ }
+ };
+
+ ZERO_STRUCT(return_authenticator);
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.return_authenticator = &return_authenticator;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.delta_enum_array = &delta_enum_array;
+
+ for (d=0; d<3; d++) {
+ const char *database = NULL;
+
+ switch (d) {
+ case 0:
+ database = "SAM";
+ break;
+ case 1:
+ database = "BUILTIN";
+ break;
+ case 2:
+ database = "LSA";
+ break;
+ default:
+ break;
+ }
+
+ torture_comment(tctx, "Testing DatabaseRedo\n");
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ for (i=0;i<ARRAY_SIZE(changes);i++) {
+
+ if (d != changes[i].db_index) {
+ continue;
+ }
+
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ r.in.credential = &credential;
+
+ e.serial_number1 = 0;
+ e.serial_number2 = 0;
+ e.object_rid = changes[i].rid;
+ e.flags = changes[i].flags;
+ e.db_index = changes[i].db_index;
+ e.delta_type = changes[i].delta_type;
+
+ switch (changes[i].flags & (NETR_CHANGELOG_NAME_INCLUDED | NETR_CHANGELOG_SID_INCLUDED)) {
+ case NETR_CHANGELOG_SID_INCLUDED:
+ e.object.object_sid = changes[i].sid;
+ break;
+ case NETR_CHANGELOG_NAME_INCLUDED:
+ e.object.object_name = changes[i].name;
+ break;
+ default:
+ break;
+ }
+
+ r.in.change_log_entry = e;
+
+ torture_comment(tctx, "Testing DatabaseRedo with database %s and %s\n",
+ database, changes[i].comment);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_DatabaseRedo_r(b, tctx, &r),
+ "DatabaseRedo failed");
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_SUPPORTED)) {
+ return true;
+ }
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, changes[i].expected_error, changes[i].comment);
+ if (delta_enum_array) {
+ torture_assert_int_equal(tctx,
+ delta_enum_array->num_deltas,
+ changes[i].expected_num_results,
+ changes[i].comment);
+ if (delta_enum_array->num_deltas > 0) {
+ torture_assert_int_equal(tctx,
+ delta_enum_array->delta_enum[0].delta_type,
+ changes[i].expected_delta_type_1,
+ changes[i].comment);
+ }
+ if (delta_enum_array->num_deltas > 1) {
+ torture_assert_int_equal(tctx,
+ delta_enum_array->delta_enum[1].delta_type,
+ changes[i].expected_delta_type_2,
+ changes[i].comment);
+ }
+ }
+
+ if (!netlogon_creds_client_check(creds, &return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+/*
+ try a netlogon AccountDeltas
+*/
+static bool test_AccountDeltas(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_AccountDeltas r;
+ struct netlogon_creds_CredentialState *creds;
+
+ struct netr_AccountBuffer buffer;
+ uint32_t count_returned = 0;
+ uint32_t total_entries = 0;
+ struct netr_UAS_INFO_0 recordid;
+ struct netr_Authenticator return_authenticator;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) {
+ return false;
+ }
+
+ ZERO_STRUCT(return_authenticator);
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.return_authenticator = &return_authenticator;
+ netlogon_creds_client_authenticator(creds, &r.in.credential);
+ ZERO_STRUCT(r.in.uas);
+ r.in.count=10;
+ r.in.level=0;
+ r.in.buffersize=100;
+ r.out.buffer = &buffer;
+ r.out.count_returned = &count_returned;
+ r.out.total_entries = &total_entries;
+ r.out.recordid = &recordid;
+ r.out.return_authenticator = &return_authenticator;
+
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_AccountDeltas_r(b, tctx, &r),
+ "AccountDeltas failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, 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)
+{
+ struct netr_AccountSync r;
+ struct netlogon_creds_CredentialState *creds;
+
+ struct netr_AccountBuffer buffer;
+ uint32_t count_returned = 0;
+ uint32_t total_entries = 0;
+ uint32_t next_reference = 0;
+ struct netr_UAS_INFO_0 recordid;
+ struct netr_Authenticator return_authenticator;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(recordid);
+ ZERO_STRUCT(return_authenticator);
+
+ 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.return_authenticator = &return_authenticator;
+ netlogon_creds_client_authenticator(creds, &r.in.credential);
+ r.in.recordid = &recordid;
+ r.in.reference=0;
+ r.in.level=0;
+ r.in.buffersize=100;
+ r.out.buffer = &buffer;
+ r.out.count_returned = &count_returned;
+ r.out.total_entries = &total_entries;
+ r.out.next_reference = &next_reference;
+ r.out.recordid = &recordid;
+ r.out.return_authenticator = &return_authenticator;
+
+ /* w2k3 returns "NOT IMPLEMENTED" for this call */
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_AccountSync_r(b, tctx, &r),
+ "AccountSync failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_IMPLEMENTED, "AccountSync");
+
+ return true;
+}
+
+/*
+ try a netlogon GetDcName
+*/
+static bool test_GetDcName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct netr_GetDcName r;
+ const char *dcname = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domainname = lpcfg_workgroup(tctx->lp_ctx);
+ r.out.dcname = &dcname;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_GetDcName_r(b, tctx, &r),
+ "GetDcName failed");
+ torture_assert_werr_ok(tctx, r.out.result, "GetDcName failed");
+
+ torture_comment(tctx, "\tDC is at '%s'\n", dcname);
+
+ return true;
+}
+
+static const char *function_code_str(TALLOC_CTX *mem_ctx,
+ enum netr_LogonControlCode function_code)
+{
+ switch (function_code) {
+ case NETLOGON_CONTROL_QUERY:
+ return "NETLOGON_CONTROL_QUERY";
+ case NETLOGON_CONTROL_REPLICATE:
+ return "NETLOGON_CONTROL_REPLICATE";
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+ return "NETLOGON_CONTROL_SYNCHRONIZE";
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+ return "NETLOGON_CONTROL_PDC_REPLICATE";
+ case NETLOGON_CONTROL_REDISCOVER:
+ return "NETLOGON_CONTROL_REDISCOVER";
+ case NETLOGON_CONTROL_TC_QUERY:
+ return "NETLOGON_CONTROL_TC_QUERY";
+ case NETLOGON_CONTROL_TRANSPORT_NOTIFY:
+ return "NETLOGON_CONTROL_TRANSPORT_NOTIFY";
+ case NETLOGON_CONTROL_FIND_USER:
+ return "NETLOGON_CONTROL_FIND_USER";
+ case NETLOGON_CONTROL_CHANGE_PASSWORD:
+ return "NETLOGON_CONTROL_CHANGE_PASSWORD";
+ case NETLOGON_CONTROL_TC_VERIFY:
+ return "NETLOGON_CONTROL_TC_VERIFY";
+ case NETLOGON_CONTROL_FORCE_DNS_REG:
+ return "NETLOGON_CONTROL_FORCE_DNS_REG";
+ case NETLOGON_CONTROL_QUERY_DNS_REG:
+ return "NETLOGON_CONTROL_QUERY_DNS_REG";
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+ return "NETLOGON_CONTROL_BACKUP_CHANGE_LOG";
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+ return "NETLOGON_CONTROL_TRUNCATE_LOG";
+ case NETLOGON_CONTROL_SET_DBFLAG:
+ return "NETLOGON_CONTROL_SET_DBFLAG";
+ case NETLOGON_CONTROL_BREAKPOINT:
+ return "NETLOGON_CONTROL_BREAKPOINT";
+ default:
+ return talloc_asprintf(mem_ctx, "unknown function code: %d",
+ function_code);
+ }
+}
+
+
+/*
+ try a netlogon LogonControl
+*/
+static bool test_LogonControl(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+
+{
+ NTSTATUS status;
+ struct netr_LogonControl r;
+ union netr_CONTROL_QUERY_INFORMATION query;
+ int i,f;
+ enum netr_SchannelType secure_channel_type = SEC_CHAN_NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ uint32_t function_codes[] = {
+ NETLOGON_CONTROL_QUERY,
+ NETLOGON_CONTROL_REPLICATE,
+ NETLOGON_CONTROL_SYNCHRONIZE,
+ NETLOGON_CONTROL_PDC_REPLICATE,
+ NETLOGON_CONTROL_REDISCOVER,
+ NETLOGON_CONTROL_TC_QUERY,
+ NETLOGON_CONTROL_TRANSPORT_NOTIFY,
+ NETLOGON_CONTROL_FIND_USER,
+ NETLOGON_CONTROL_CHANGE_PASSWORD,
+ NETLOGON_CONTROL_TC_VERIFY,
+ NETLOGON_CONTROL_FORCE_DNS_REG,
+ NETLOGON_CONTROL_QUERY_DNS_REG,
+ NETLOGON_CONTROL_BACKUP_CHANGE_LOG,
+ NETLOGON_CONTROL_TRUNCATE_LOG,
+ NETLOGON_CONTROL_SET_DBFLAG,
+ NETLOGON_CONTROL_BREAKPOINT
+ };
+
+ if (machine_credentials) {
+ secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials);
+ }
+
+ torture_comment(tctx, "Testing LogonControl with secure channel type: %d\n",
+ secure_channel_type);
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.function_code = 1;
+ r.out.query = &query;
+
+ for (f=0;f<ARRAY_SIZE(function_codes); f++) {
+ for (i=1;i<5;i++) {
+
+ r.in.function_code = function_codes[f];
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+
+ switch (r.in.level) {
+ case 1:
+ switch (r.in.function_code) {
+ case NETLOGON_CONTROL_REPLICATE:
+ case NETLOGON_CONTROL_SYNCHRONIZE:
+ case NETLOGON_CONTROL_PDC_REPLICATE:
+ case NETLOGON_CONTROL_BREAKPOINT:
+ case NETLOGON_CONTROL_BACKUP_CHANGE_LOG:
+ if ((secure_channel_type == SEC_CHAN_BDC) ||
+ (secure_channel_type == SEC_CHAN_WKSTA)) {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_ACCESS_DENIED,
+ "LogonControl returned unexpected error code");
+ } else {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "LogonControl returned unexpected error code");
+ }
+ break;
+
+ case NETLOGON_CONTROL_REDISCOVER:
+ case NETLOGON_CONTROL_TC_QUERY:
+ case NETLOGON_CONTROL_TRANSPORT_NOTIFY:
+ case NETLOGON_CONTROL_FIND_USER:
+ case NETLOGON_CONTROL_CHANGE_PASSWORD:
+ case NETLOGON_CONTROL_TC_VERIFY:
+ case NETLOGON_CONTROL_FORCE_DNS_REG:
+ case NETLOGON_CONTROL_QUERY_DNS_REG:
+ case NETLOGON_CONTROL_SET_DBFLAG:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "LogonControl returned unexpected error code");
+ break;
+ case NETLOGON_CONTROL_TRUNCATE_LOG:
+ if ((secure_channel_type == SEC_CHAN_BDC) ||
+ (secure_channel_type == SEC_CHAN_WKSTA)) {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_ACCESS_DENIED,
+ "LogonControl returned unexpected error code");
+ } else if (!W_ERROR_EQUAL(r.out.result, WERR_NOT_SUPPORTED)) {
+ torture_assert_werr_ok(tctx, r.out.result,
+ "LogonControl returned unexpected result");
+ }
+ break;
+ default:
+ torture_assert_werr_ok(tctx, r.out.result,
+ "LogonControl returned unexpected result");
+ break;
+ }
+ break;
+ case 2:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "LogonControl returned unexpected error code");
+ break;
+ default:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL,
+ "LogonControl returned unexpected error code");
+ break;
+ }
+ }
+ }
+
+ r.in.level = 52;
+ torture_comment(tctx, "Testing LogonControl function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+ status = dcerpc_netr_LogonControl_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "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;
+ const char *dcname = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.domainname = lpcfg_workgroup(tctx->lp_ctx);
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.out.dcname = &dcname;
+
+ status = dcerpc_netr_GetAnyDCName_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName");
+ if ((!W_ERROR_IS_OK(r.out.result)) &&
+ (!W_ERROR_EQUAL(r.out.result, WERR_NO_SUCH_DOMAIN))) {
+ return false;
+ }
+
+ if (dcname) {
+ torture_comment(tctx, "\tDC is at '%s'\n", dcname);
+ }
+
+ r.in.domainname = NULL;
+
+ status = dcerpc_netr_GetAnyDCName_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName");
+ if ((!W_ERROR_IS_OK(r.out.result)) &&
+ (!W_ERROR_EQUAL(r.out.result, WERR_NO_SUCH_DOMAIN))) {
+ return false;
+ }
+
+ r.in.domainname = "";
+
+ status = dcerpc_netr_GetAnyDCName_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName");
+ if ((!W_ERROR_IS_OK(r.out.result)) &&
+ (!W_ERROR_EQUAL(r.out.result, WERR_NO_SUCH_DOMAIN))) {
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon LogonControl2
+*/
+static bool test_LogonControl2(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+
+{
+ NTSTATUS status;
+ struct netr_LogonControl2 r;
+ union netr_CONTROL_DATA_INFORMATION data;
+ union netr_CONTROL_QUERY_INFORMATION query;
+ enum netr_SchannelType secure_channel_type = SEC_CHAN_NULL;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ data.domain = lpcfg_workgroup(tctx->lp_ctx);
+
+ if (machine_credentials) {
+ secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials);
+ }
+
+ torture_comment(tctx, "Testing LogonControl2 with secure channel type: %d\n",
+ secure_channel_type);
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ r.in.function_code = NETLOGON_CONTROL_REDISCOVER;
+ r.in.data = &data;
+ r.out.query = &query;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2");
+ }
+
+ data.domain = lpcfg_workgroup(tctx->lp_ctx);
+
+ r.in.function_code = NETLOGON_CONTROL_TC_QUERY;
+ r.in.data = &data;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2");
+ }
+
+ data.domain = lpcfg_workgroup(tctx->lp_ctx);
+
+ r.in.function_code = NETLOGON_CONTROL_TRANSPORT_NOTIFY;
+ r.in.data = &data;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2");
+ }
+
+ data.debug_level = ~0;
+
+ r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG;
+ r.in.data = &data;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2");
+ }
+
+ ZERO_STRUCT(data);
+ r.in.function_code = 52;
+ r.in.data = &data;
+
+ torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2");
+ switch (secure_channel_type) {
+ case SEC_CHAN_NULL:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, "LogonControl2");
+ break;
+ default:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_ACCESS_DENIED, "LogonControl2");
+ break;
+ }
+ data.debug_level = ~0;
+
+ r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG;
+ r.in.data = &data;
+
+ r.in.level = 52;
+ torture_comment(tctx, "Testing LogonControl2 function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "LogonControl2");
+
+ return true;
+}
+
+/*
+ try a netlogon DatabaseSync2
+*/
+static bool test_DatabaseSync2(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_DatabaseSync2 r;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ struct netr_Authenticator return_authenticator, credential;
+
+ struct netlogon_creds_CredentialState *creds;
+ const uint32_t database_ids[] = {0, 1, 2};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_SetupCredentials2(p, tctx, NETLOGON_NEG_AUTH2_FLAGS,
+ machine_credentials,
+ cli_credentials_get_secure_channel_type(machine_credentials),
+ &creds)) {
+ return false;
+ }
+
+ ZERO_STRUCT(return_authenticator);
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.preferredmaximumlength = (uint32_t)-1;
+ r.in.return_authenticator = &return_authenticator;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.delta_enum_array = &delta_enum_array;
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint32_t sync_context = 0;
+
+ r.in.database_id = database_ids[i];
+ r.in.sync_context = &sync_context;
+ r.out.sync_context = &sync_context;
+ r.in.restart_state = 0;
+
+ torture_comment(tctx, "Testing DatabaseSync2 of id %d\n", r.in.database_id);
+
+ do {
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ r.in.credential = &credential;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_DatabaseSync2_r(b, tctx, &r),
+ "DatabaseSync2 failed");
+ if (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES))
+ break;
+
+ /* Native mode servers don't do this */
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_SUPPORTED)) {
+ return true;
+ }
+
+ torture_assert_ntstatus_ok(tctx, r.out.result, "DatabaseSync2");
+
+ if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+
+/*
+ try a netlogon LogonControl2Ex
+*/
+static bool test_LogonControl2Ex(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+
+{
+ NTSTATUS status;
+ struct netr_LogonControl2Ex r;
+ union netr_CONTROL_DATA_INFORMATION data;
+ union netr_CONTROL_QUERY_INFORMATION query;
+ enum netr_SchannelType secure_channel_type = SEC_CHAN_NULL;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ data.domain = lpcfg_workgroup(tctx->lp_ctx);
+
+ if (machine_credentials) {
+ secure_channel_type = cli_credentials_get_secure_channel_type(machine_credentials);
+ }
+
+ torture_comment(tctx, "Testing LogonControl2Ex with secure channel type: %d\n",
+ secure_channel_type);
+
+ r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ r.in.function_code = NETLOGON_CONTROL_REDISCOVER;
+ r.in.data = &data;
+ r.out.query = &query;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex");
+ }
+
+ data.domain = lpcfg_workgroup(tctx->lp_ctx);
+
+ r.in.function_code = NETLOGON_CONTROL_TC_QUERY;
+ r.in.data = &data;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex");
+ }
+
+ data.domain = lpcfg_workgroup(tctx->lp_ctx);
+
+ r.in.function_code = NETLOGON_CONTROL_TRANSPORT_NOTIFY;
+ r.in.data = &data;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex");
+ }
+
+ data.debug_level = ~0;
+
+ r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG;
+ r.in.data = &data;
+
+ for (i=1;i<4;i++) {
+ r.in.level = i;
+
+ torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex");
+ }
+
+ ZERO_STRUCT(data);
+ r.in.function_code = 52;
+ r.in.data = &data;
+
+ torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex");
+ switch (secure_channel_type) {
+ case SEC_CHAN_NULL:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED, "LogonControl2Ex");
+ break;
+ default:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_ACCESS_DENIED, "LogonControl2Ex");
+ break;
+ }
+ data.debug_level = ~0;
+
+ r.in.function_code = NETLOGON_CONTROL_SET_DBFLAG;
+ r.in.data = &data;
+
+ r.in.level = 52;
+ torture_comment(tctx, "Testing LogonControl2Ex function code %s (%d) level %d\n",
+ function_code_str(tctx, r.in.function_code), r.in.function_code, r.in.level);
+
+ status = dcerpc_netr_LogonControl2Ex_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonControl2Ex");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "LogonControl2Ex");
+
+ return true;
+}
+
+static bool test_netr_GetForestTrustInformation(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_GetForestTrustInformation r;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Authenticator a;
+ struct netr_Authenticator return_authenticator;
+ struct lsa_ForestTrustInformation *forest_trust_info;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+
+ if (!test_SetupCredentials3(p1, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES,
+ machine_credentials, &creds)) {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ netlogon_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.credential = &a;
+ r.in.flags = 0;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.forest_trust_info = &forest_trust_info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_GetForestTrustInformation_r(b, tctx, &r),
+ "netr_GetForestTrustInformation failed");
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_IMPLEMENTED)) {
+ torture_comment(tctx, "not considering NT_STATUS_NOT_IMPLEMENTED as an error\n");
+ } else {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "netr_GetForestTrustInformation failed");
+ }
+
+ torture_assert(tctx,
+ netlogon_creds_client_check(creds, &return_authenticator.cred),
+ "Credential chaining failed");
+
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct netr_DomainTrustList trusts;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.trust_flags = 0x3f;
+ r.out.trusts = &trusts;
+
+ status = dcerpc_netr_DsrEnumerateDomainTrusts_r(b, 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.trusts->count) {
+ if (!test_netr_DsRGetForestTrustInformation(tctx, p, NULL)) {
+ return false;
+ }
+ }
+
+ for (i=0; i<r.out.trusts->count; i++) {
+
+ /* get info for transitive forest trusts */
+
+ if (r.out.trusts->array[i].trust_attributes & LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE) {
+ if (!test_netr_DsRGetForestTrustInformation(tctx, p,
+ r.out.trusts->array[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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.out.trusted_domains_blob = &trusted_domains_blob;
+
+ status = dcerpc_netr_NetrEnumerateTrustedDomains_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_NetrEnumerateTrustedDomains");
+ torture_assert_ntstatus_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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.out.dom_trust_list = &dom_trust_list;
+
+ status = dcerpc_netr_NetrEnumerateTrustedDomainsEx_r(b, 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;
+ const char *site = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.computer_name = computer_name;
+ r.out.site = &site;
+ torture_comment(tctx, "Testing netr_DsRGetSiteName\n");
+
+ status = dcerpc_netr_DsRGetSiteName_r(b, 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, site, "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;
+ struct netr_DsRGetDCNameInfo *info = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domain_name = lpcfg_dnsdomain(tctx->lp_ctx);
+ r.in.domain_guid = NULL;
+ r.in.site_guid = NULL;
+ r.in.flags = DS_RETURN_DNS_NAME;
+ r.out.info = &info;
+
+ status = dcerpc_netr_DsRGetDCName_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "DsRGetDCName");
+ torture_assert_werr_ok(tctx, r.out.result, "DsRGetDCName");
+
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_CONTROLLER)),
+ DS_DNS_CONTROLLER,
+ "DsRGetDCName");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_DOMAIN)),
+ DS_DNS_DOMAIN,
+ "DsRGetDCName");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_FOREST_ROOT)),
+ DS_DNS_FOREST_ROOT,
+ "DsRGetDCName");
+
+ r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx);
+ r.in.flags = 0;
+
+ status = dcerpc_netr_DsRGetDCName_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "DsRGetDCName");
+ torture_assert_werr_ok(tctx, r.out.result, "DsRGetDCName");
+
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_CONTROLLER)), 0,
+ "DsRGetDCName");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_DOMAIN)), 0,
+ "DsRGetDCName");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_FOREST_ROOT)),
+ DS_DNS_FOREST_ROOT,
+ "DsRGetDCName");
+
+ if (strcasecmp(info->dc_site_name, info->client_site_name) == 0) {
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_SERVER_CLOSEST)),
+ DS_SERVER_CLOSEST,
+ "DsRGetDCName");
+ }
+
+ return test_netr_DsRGetSiteName(p, tctx,
+ info->dc_unc,
+ 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;
+ struct netr_DsRGetDCNameInfo *info = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.domain_name = lpcfg_dnsdomain(tctx->lp_ctx);
+ r.in.domain_guid = NULL;
+ r.in.site_name = NULL;
+ r.in.flags = DS_RETURN_DNS_NAME;
+ r.out.info = &info;
+
+ status = dcerpc_netr_DsRGetDCNameEx_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx");
+
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_CONTROLLER)),
+ DS_DNS_CONTROLLER,
+ "DsRGetDCNameEx");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_DOMAIN)),
+ DS_DNS_DOMAIN,
+ "DsRGetDCNameEx");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_FOREST_ROOT)),
+ DS_DNS_FOREST_ROOT,
+ "DsRGetDCNameEx");
+
+ r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx);
+ r.in.flags = 0;
+
+ status = dcerpc_netr_DsRGetDCNameEx_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx");
+
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_CONTROLLER)), 0,
+ "DsRGetDCNameEx");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_DOMAIN)), 0,
+ "DsRGetDCNameEx");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_FOREST_ROOT)),
+ DS_DNS_FOREST_ROOT,
+ "DsRGetDCNameEx");
+
+ if (strcasecmp(info->dc_site_name, info->client_site_name) == 0) {
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_SERVER_CLOSEST)),
+ DS_SERVER_CLOSEST,
+ "DsRGetDCNameEx");
+ }
+
+ return test_netr_DsRGetSiteName(p, tctx, info->dc_unc,
+ 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;
+ struct netr_DsRGetDCNameInfo *info = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 with no inputs\n");
+ ZERO_STRUCT(r.in);
+ r.in.flags = DS_RETURN_DNS_NAME;
+ r.out.info = &info;
+
+ status = dcerpc_netr_DsRGetDCNameEx2_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2");
+
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_CONTROLLER)),
+ DS_DNS_CONTROLLER,
+ "DsRGetDCNameEx2");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_DOMAIN)),
+ DS_DNS_DOMAIN,
+ "DsRGetDCNameEx2");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_FOREST_ROOT)),
+ DS_DNS_FOREST_ROOT,
+ "DsRGetDCNameEx2");
+
+ 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 = lpcfg_dnsdomain(tctx->lp_ctx);
+ r.in.domain_guid = NULL;
+ r.in.site_name = NULL;
+ r.in.flags = DS_RETURN_DNS_NAME;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 without client account\n");
+
+ status = dcerpc_netr_DsRGetDCNameEx2_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2");
+
+ r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx);
+ r.in.flags = 0;
+
+ status = dcerpc_netr_DsRGetDCNameEx2_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "netr_DsRGetDCNameEx2");
+ torture_assert_werr_ok(tctx, r.out.result, "netr_DsRGetDCNameEx2");
+
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_CONTROLLER)), 0,
+ "DsRGetDCNameEx2");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_DOMAIN)), 0,
+ "DsRGetDCNameEx2");
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_DNS_FOREST_ROOT)),
+ DS_DNS_FOREST_ROOT,
+ "DsRGetDCNameEx2");
+
+ if (strcasecmp(info->dc_site_name, info->client_site_name) == 0) {
+ torture_assert_int_equal(tctx,
+ (info->dc_flags & (DS_SERVER_CLOSEST)),
+ DS_SERVER_CLOSEST,
+ "DsRGetDCNameEx2");
+ }
+
+ torture_comment(tctx, "Testing netr_DsRGetDCNameEx2 with client account\n");
+ r.in.client_account = TEST_MACHINE_NAME"$";
+ r.in.mask = ACB_SVRTRUST;
+ r.in.flags = DS_RETURN_FLAT_NAME;
+ r.out.info = &info;
+
+ status = dcerpc_netr_DsRGetDCNameEx2_r(b, 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, info->dc_unc,
+ info->dc_site_name);
+}
+
+/* This is a substitution for "samdb_server_site_name" which relies on the
+ * correct "lp_ctx" and therefore can't be used here. */
+static const char *server_site_name(struct torture_context *tctx,
+ struct ldb_context *ldb)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn, *server_dn;
+ const struct ldb_val *site_name_val;
+ const char *server_dn_str, *site_name;
+
+ tmp_ctx = talloc_new(ldb);
+ if (tmp_ctx == NULL) {
+ goto failed;
+ }
+
+ dn = ldb_dn_new(tmp_ctx, ldb, "");
+ if (dn == NULL) {
+ goto failed;
+ }
+
+ server_dn_str = samdb_search_string(ldb, tmp_ctx, dn, "serverName",
+ NULL);
+ if (server_dn_str == NULL) {
+ goto failed;
+ }
+
+ server_dn = ldb_dn_new(tmp_ctx, ldb, server_dn_str);
+ if (server_dn == NULL) {
+ goto failed;
+ }
+
+ /* CN=<Server name>, CN=Servers, CN=<Site name>, CN=Sites, ... */
+ site_name_val = ldb_dn_get_component_val(server_dn, 2);
+ if (site_name_val == NULL) {
+ goto failed;
+ }
+
+ site_name = (const char *) site_name_val->data;
+
+ talloc_steal(tctx, site_name);
+ talloc_free(tmp_ctx);
+
+ return site_name;
+
+failed:
+ talloc_free(tmp_ctx);
+ return NULL;
+}
+
+static bool test_netr_DsrGetDcSiteCoverageW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ char *url;
+ struct ldb_context *sam_ctx = NULL;
+ NTSTATUS status;
+ struct netr_DsrGetDcSiteCoverageW r;
+ struct DcSitesCtr *ctr = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "This does only pass with the default site\n");
+
+ /* We won't double-check this when we are over 'local' transports */
+ if (dcerpc_server_name(p)) {
+ /* Set up connection to SAMDB on DC */
+ url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p));
+ sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url,
+ NULL,
+ samba_cmdline_get_creds(),
+ 0);
+
+ torture_assert(tctx, sam_ctx, "Connection to the SAMDB on DC failed!");
+ }
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.out.ctr = &ctr;
+
+ status = dcerpc_netr_DsrGetDcSiteCoverageW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ torture_assert(tctx, ctr->num_sites == 1,
+ "we should per default only get the default site");
+ if (sam_ctx != NULL) {
+ torture_assert_casestr_equal(tctx, ctr->sites[0].string,
+ server_site_name(tctx, sam_ctx),
+ "didn't return default site");
+ }
+
+ return true;
+}
+
+static bool test_netr_DsRAddressToSitenamesW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ char *url;
+ struct ldb_context *sam_ctx = NULL;
+ NTSTATUS status;
+ struct netr_DsRAddressToSitenamesW r;
+ struct netr_DsRAddress addrs[6];
+ struct sockaddr_in *addr;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 *addr6;
+#endif
+ struct netr_DsRAddressToSitenamesWCtr *ctr;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t i;
+ int ret;
+
+ torture_comment(tctx, "This does only pass with the default site\n");
+
+ /* We won't double-check this when we are over 'local' transports */
+ if (dcerpc_server_name(p)) {
+ /* Set up connection to SAMDB on DC */
+ url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p));
+ sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url,
+ NULL,
+ samba_cmdline_get_creds(),
+ 0);
+
+ torture_assert(tctx, sam_ctx, "Connection to the SAMDB on DC failed!");
+ }
+
+ /* First try valid IP addresses */
+
+ addrs[0].size = sizeof(struct sockaddr_in);
+ addrs[0].buffer = talloc_zero_array(tctx, uint8_t, addrs[0].size);
+ addr = (struct sockaddr_in *) addrs[0].buffer;
+ addrs[0].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "127.0.0.1", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[1].size = sizeof(struct sockaddr_in);
+ addrs[1].buffer = talloc_zero_array(tctx, uint8_t, addrs[1].size);
+ addr = (struct sockaddr_in *) addrs[1].buffer;
+ addrs[1].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "0.0.0.0", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[2].size = sizeof(struct sockaddr_in);
+ addrs[2].buffer = talloc_zero_array(tctx, uint8_t, addrs[2].size);
+ addr = (struct sockaddr_in *) addrs[2].buffer;
+ addrs[2].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "255.255.255.255", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+#ifdef HAVE_IPV6
+ addrs[3].size = sizeof(struct sockaddr_in6);
+ addrs[3].buffer = talloc_zero_array(tctx, uint8_t, addrs[3].size);
+ addr6 = (struct sockaddr_in6 *) addrs[3].buffer;
+ addrs[3].buffer[0] = AF_INET6;
+ ret = inet_pton(AF_INET6, "::1", &addr6->sin6_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[4].size = sizeof(struct sockaddr_in6);
+ addrs[4].buffer = talloc_zero_array(tctx, uint8_t, addrs[4].size);
+ addr6 = (struct sockaddr_in6 *) addrs[4].buffer;
+ addrs[4].buffer[0] = AF_INET6;
+ ret = inet_pton(AF_INET6, "::", &addr6->sin6_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[5].size = sizeof(struct sockaddr_in6);
+ addrs[5].buffer = talloc_zero_array(tctx, uint8_t, addrs[5].size);
+ addr6 = (struct sockaddr_in6 *) addrs[5].buffer;
+ addrs[5].buffer[0] = AF_INET6;
+ ret = inet_pton(AF_INET6, "ff02::1", &addr6->sin6_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+#else
+ /* the test cases are repeated to have exactly 6. This is for
+ * compatibility with IPv4-only machines */
+ addrs[3].size = sizeof(struct sockaddr_in);
+ addrs[3].buffer = talloc_zero_array(tctx, uint8_t, addrs[3].size);
+ addr = (struct sockaddr_in *) addrs[3].buffer;
+ addrs[3].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "127.0.0.1", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[4].size = sizeof(struct sockaddr_in);
+ addrs[4].buffer = talloc_zero_array(tctx, uint8_t, addrs[4].size);
+ addr = (struct sockaddr_in *) addrs[4].buffer;
+ addrs[4].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "0.0.0.0", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[5].size = sizeof(struct sockaddr_in);
+ addrs[5].buffer = talloc_zero_array(tctx, uint8_t, addrs[5].size);
+ addr = (struct sockaddr_in *) addrs[5].buffer;
+ addrs[5].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "255.255.255.255", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+#endif
+
+ ctr = talloc(tctx, struct netr_DsRAddressToSitenamesWCtr);
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.count = 6;
+ r.in.addresses = addrs;
+ r.out.ctr = &ctr;
+
+ status = dcerpc_netr_DsRAddressToSitenamesW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ if (sam_ctx != NULL) {
+ for (i = 0; i < 3; i++) {
+ torture_assert_casestr_equal(tctx,
+ ctr->sitename[i].string,
+ server_site_name(tctx, sam_ctx),
+ "didn't return default site");
+ }
+ for (i = 3; i < 6; i++) {
+ /* Windows returns "NULL" for the sitename if it isn't
+ * IPv6 configured */
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_assert_casestr_equal(tctx,
+ ctr->sitename[i].string,
+ server_site_name(tctx, sam_ctx),
+ "didn't return default site");
+ }
+ }
+ }
+
+ /* Now try invalid ones (too short buffers) */
+
+ addrs[0].size = 0;
+ addrs[1].size = 1;
+ addrs[2].size = 4;
+
+ addrs[3].size = 0;
+ addrs[4].size = 1;
+ addrs[5].size = 4;
+
+ status = dcerpc_netr_DsRAddressToSitenamesW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ for (i = 0; i < 6; i++) {
+ torture_assert(tctx, ctr->sitename[i].string == NULL,
+ "sitename should be null");
+ }
+
+ /* Now try invalid ones (wrong address types) */
+
+ addrs[0].size = 10;
+ addrs[0].buffer[0] = AF_UNSPEC;
+ addrs[1].size = 10;
+ addrs[1].buffer[0] = AF_UNIX; /* AF_LOCAL = AF_UNIX */
+ addrs[2].size = 10;
+ addrs[2].buffer[0] = AF_UNIX;
+
+ addrs[3].size = 10;
+ addrs[3].buffer[0] = 250;
+ addrs[4].size = 10;
+ addrs[4].buffer[0] = 251;
+ addrs[5].size = 10;
+ addrs[5].buffer[0] = 252;
+
+ status = dcerpc_netr_DsRAddressToSitenamesW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ for (i = 0; i < 6; i++) {
+ torture_assert(tctx, ctr->sitename[i].string == NULL,
+ "sitename should be null");
+ }
+
+ return true;
+}
+
+static bool test_netr_DsRAddressToSitenamesExW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ char *url;
+ struct ldb_context *sam_ctx = NULL;
+ NTSTATUS status;
+ struct netr_DsRAddressToSitenamesExW r;
+ struct netr_DsRAddress addrs[6];
+ struct sockaddr_in *addr;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 *addr6;
+#endif
+ struct netr_DsRAddressToSitenamesExWCtr *ctr;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t i;
+ int ret;
+
+ torture_comment(tctx, "This does pass with the default site\n");
+
+ /* We won't double-check this when we are over 'local' transports */
+ if (dcerpc_server_name(p)) {
+ /* Set up connection to SAMDB on DC */
+ url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p));
+ sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url,
+ NULL,
+ samba_cmdline_get_creds(),
+ 0);
+
+ torture_assert(tctx, sam_ctx, "Connection to the SAMDB on DC failed!");
+ }
+
+ /* First try valid IP addresses */
+
+ addrs[0].size = sizeof(struct sockaddr_in);
+ addrs[0].buffer = talloc_zero_array(tctx, uint8_t, addrs[0].size);
+ addr = (struct sockaddr_in *) addrs[0].buffer;
+ addrs[0].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "127.0.0.1", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[1].size = sizeof(struct sockaddr_in);
+ addrs[1].buffer = talloc_zero_array(tctx, uint8_t, addrs[1].size);
+ addr = (struct sockaddr_in *) addrs[1].buffer;
+ addrs[1].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "0.0.0.0", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[2].size = sizeof(struct sockaddr_in);
+ addrs[2].buffer = talloc_zero_array(tctx, uint8_t, addrs[2].size);
+ addr = (struct sockaddr_in *) addrs[2].buffer;
+ addrs[2].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "255.255.255.255", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+#ifdef HAVE_IPV6
+ addrs[3].size = sizeof(struct sockaddr_in6);
+ addrs[3].buffer = talloc_zero_array(tctx, uint8_t, addrs[3].size);
+ addr6 = (struct sockaddr_in6 *) addrs[3].buffer;
+ addrs[3].buffer[0] = AF_INET6;
+ ret = inet_pton(AF_INET6, "::1", &addr6->sin6_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[4].size = sizeof(struct sockaddr_in6);
+ addrs[4].buffer = talloc_zero_array(tctx, uint8_t, addrs[4].size);
+ addr6 = (struct sockaddr_in6 *) addrs[4].buffer;
+ addrs[4].buffer[0] = AF_INET6;
+ ret = inet_pton(AF_INET6, "::", &addr6->sin6_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[5].size = sizeof(struct sockaddr_in6);
+ addrs[5].buffer = talloc_zero_array(tctx, uint8_t, addrs[5].size);
+ addr6 = (struct sockaddr_in6 *) addrs[5].buffer;
+ addrs[5].buffer[0] = AF_INET6;
+ ret = inet_pton(AF_INET6, "ff02::1", &addr6->sin6_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+#else
+ /* the test cases are repeated to have exactly 6. This is for
+ * compatibility with IPv4-only machines */
+ addrs[3].size = sizeof(struct sockaddr_in);
+ addrs[3].buffer = talloc_zero_array(tctx, uint8_t, addrs[3].size);
+ addr = (struct sockaddr_in *) addrs[3].buffer;
+ addrs[3].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "127.0.0.1", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[4].size = sizeof(struct sockaddr_in);
+ addrs[4].buffer = talloc_zero_array(tctx, uint8_t, addrs[4].size);
+ addr = (struct sockaddr_in *) addrs[4].buffer;
+ addrs[4].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "0.0.0.0", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+
+ addrs[5].size = sizeof(struct sockaddr_in);
+ addrs[5].buffer = talloc_zero_array(tctx, uint8_t, addrs[5].size);
+ addr = (struct sockaddr_in *) addrs[5].buffer;
+ addrs[5].buffer[0] = AF_INET;
+ ret = inet_pton(AF_INET, "255.255.255.255", &addr->sin_addr);
+ torture_assert(tctx, ret > 0, "inet_pton failed");
+#endif
+
+ ctr = talloc(tctx, struct netr_DsRAddressToSitenamesExWCtr);
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.count = 6;
+ r.in.addresses = addrs;
+ r.out.ctr = &ctr;
+
+ status = dcerpc_netr_DsRAddressToSitenamesExW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ if (sam_ctx != NULL) {
+ for (i = 0; i < 3; i++) {
+ torture_assert_casestr_equal(tctx,
+ ctr->sitename[i].string,
+ server_site_name(tctx, sam_ctx),
+ "didn't return default site");
+ torture_assert(tctx, ctr->subnetname[i].string == NULL,
+ "subnet should be null");
+ }
+ for (i = 3; i < 6; i++) {
+ /* Windows returns "NULL" for the sitename if it isn't
+ * IPv6 configured */
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_assert_casestr_equal(tctx,
+ ctr->sitename[i].string,
+ server_site_name(tctx, sam_ctx),
+ "didn't return default site");
+ }
+ torture_assert(tctx, ctr->subnetname[i].string == NULL,
+ "subnet should be null");
+ }
+ }
+
+ /* Now try invalid ones (too short buffers) */
+
+ addrs[0].size = 0;
+ addrs[1].size = 1;
+ addrs[2].size = 4;
+
+ addrs[3].size = 0;
+ addrs[4].size = 1;
+ addrs[5].size = 4;
+
+ status = dcerpc_netr_DsRAddressToSitenamesExW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ for (i = 0; i < 6; i++) {
+ torture_assert(tctx, ctr->sitename[i].string == NULL,
+ "sitename should be null");
+ torture_assert(tctx, ctr->subnetname[i].string == NULL,
+ "subnet should be null");
+ }
+
+ addrs[0].size = 10;
+ addrs[0].buffer[0] = AF_UNSPEC;
+ addrs[1].size = 10;
+ addrs[1].buffer[0] = AF_UNIX; /* AF_LOCAL = AF_UNIX */
+ addrs[2].size = 10;
+ addrs[2].buffer[0] = AF_UNIX;
+
+ addrs[3].size = 10;
+ addrs[3].buffer[0] = 250;
+ addrs[4].size = 10;
+ addrs[4].buffer[0] = 251;
+ addrs[5].size = 10;
+ addrs[5].buffer[0] = 252;
+
+ status = dcerpc_netr_DsRAddressToSitenamesExW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "failed");
+ torture_assert_werr_ok(tctx, r.out.result, "failed");
+
+ for (i = 0; i < 6; i++) {
+ torture_assert(tctx, ctr->sitename[i].string == NULL,
+ "sitename should be null");
+ torture_assert(tctx, ctr->subnetname[i].string == NULL,
+ "subnet should be null");
+ }
+
+ return true;
+}
+
+static bool test_netr_ServerGetTrustInfo_flags(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials,
+ uint32_t negotiate_flags)
+{
+ struct netr_ServerGetTrustInfo r;
+
+ struct netr_Authenticator a;
+ struct netr_Authenticator return_authenticator;
+ struct samr_Password new_owf_password;
+ struct samr_Password old_owf_password;
+ struct netr_TrustInfo *trust_info;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+
+ struct samr_Password nt_hash;
+
+ if (!test_SetupCredentials3(p1, tctx, negotiate_flags,
+ machine_credentials, &creds)) {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ netlogon_creds_client_authenticator(creds, &a);
+
+ 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 = cli_credentials_get_secure_channel_type(machine_credentials);
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &a;
+
+ r.out.return_authenticator = &return_authenticator;
+ r.out.new_owf_password = &new_owf_password;
+ r.out.old_owf_password = &old_owf_password;
+ r.out.trust_info = &trust_info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerGetTrustInfo_r(b, tctx, &r),
+ "ServerGetTrustInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerGetTrustInfo failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &return_authenticator.cred), "Credential chaining failed");
+
+ E_md4hash(cli_credentials_get_password(machine_credentials), nt_hash.hash);
+
+ netlogon_creds_des_decrypt(creds, &new_owf_password);
+
+ dump_data(1, new_owf_password.hash, 16);
+ dump_data(1, nt_hash.hash, 16);
+
+ torture_assert_mem_equal(tctx, new_owf_password.hash, nt_hash.hash, 16,
+ "received unexpected owf password\n");
+
+ return true;
+}
+
+static bool test_netr_ServerGetTrustInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ return test_netr_ServerGetTrustInfo_flags(tctx, p, machine_credentials,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS);
+}
+
+static bool test_netr_ServerGetTrustInfo_AES(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ return test_netr_ServerGetTrustInfo_flags(tctx, p, machine_credentials,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES);
+}
+
+static bool test_GetDomainInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ struct netr_LogonGetDomainInfo r;
+ struct netr_WorkstationInformation q1;
+ struct netr_Authenticator a;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_OsVersion os;
+ union netr_WorkstationInfo query;
+ union netr_DomainInfo info;
+ const char* const attrs[] = { "dNSHostName", "operatingSystem",
+ "operatingSystemServicePack", "operatingSystemVersion",
+ "servicePrincipalName", NULL };
+ char *url;
+ struct ldb_context *sam_ctx = NULL;
+ struct ldb_message **res;
+ struct ldb_message_element *spn_el;
+ int ret, i;
+ char *version_str;
+ const char *old_dnsname = NULL;
+ char **spns = NULL;
+ int num_spns = 0;
+ char *temp_str = NULL;
+ char *temp_str2 = NULL;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ struct netr_OneDomainInfo *odi1 = NULL;
+ struct netr_OneDomainInfo *odi2 = NULL;
+ struct netr_trust_extension_info *tex2 = NULL;
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo\n");
+
+ if (!test_SetupCredentials3(p1, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES,
+ machine_credentials, &creds)) {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ /* We won't double-check this when we are over 'local' transports */
+ if (dcerpc_server_name(p)) {
+ /* Set up connection to SAMDB on DC */
+ url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p));
+ sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url,
+ NULL,
+ samba_cmdline_get_creds(),
+ 0);
+
+ torture_assert(tctx, sam_ctx, "Connection to the SAMDB on DC failed!");
+ }
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo 1st call (no variation of DNS hostname)\n");
+ netlogon_creds_client_authenticator(creds, &a);
+
+ ZERO_STRUCT(r);
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &a;
+ r.in.level = 1;
+ r.in.return_authenticator = &a;
+ r.in.query = &query;
+ r.out.return_authenticator = &a;
+ r.out.info = &info;
+
+ ZERO_STRUCT(os);
+ os.os.MajorVersion = 123;
+ os.os.MinorVersion = 456;
+ os.os.BuildNumber = 789;
+ os.os.CSDVersion = "Service Pack 10";
+ os.os.ServicePackMajor = 10;
+ os.os.ServicePackMinor = 1;
+ os.os.SuiteMask = NETR_VER_SUITE_SINGLEUSERTS;
+ os.os.ProductType = NETR_VER_NT_SERVER;
+ os.os.Reserved = 0;
+
+ version_str = talloc_asprintf(tctx, "%d.%d (%d)", os.os.MajorVersion,
+ os.os.MinorVersion, os.os.BuildNumber);
+
+ ZERO_STRUCT(q1);
+ q1.dns_hostname = talloc_asprintf(tctx, "%s.%s", TEST_MACHINE_NAME,
+ lpcfg_dnsdomain(tctx->lp_ctx));
+ q1.sitename = "Default-First-Site-Name";
+ q1.os_version.os = &os;
+ q1.os_name.string = talloc_asprintf(tctx,
+ "Tortured by Samba4 RPC-NETLOGON: %s",
+ timestring(tctx, time(NULL)));
+
+ /* The workstation handles the "servicePrincipalName" and DNS hostname
+ updates */
+ q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE;
+
+ query.workstation_info = &q1;
+
+ if (sam_ctx) {
+ /* Gets back the old DNS hostname in AD */
+ ret = gendb_search(sam_ctx, tctx, NULL, &res, attrs,
+ "(sAMAccountName=%s$)", TEST_MACHINE_NAME);
+ old_dnsname =
+ ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL);
+
+ /* Gets back the "servicePrincipalName"s in AD */
+ spn_el = ldb_msg_find_element(res[0], "servicePrincipalName");
+ if (spn_el != NULL) {
+ for (i=0; i < spn_el->num_values; i++) {
+ spns = talloc_realloc(tctx, spns, char *, i + 1);
+ spns[i] = (char *) spn_el->values[i].data;
+ }
+ num_spns = i;
+ }
+ }
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r),
+ "LogonGetDomainInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ smb_msleep(250);
+
+ if (sam_ctx) {
+ /* AD workstation infos entry check */
+ ret = gendb_search(sam_ctx, tctx, NULL, &res, attrs,
+ "(sAMAccountName=%s$)", TEST_MACHINE_NAME);
+ torture_assert(tctx, ret == 1, "Test machine account not found in SAMDB on DC! Has the workstation been joined?");
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "operatingSystem", NULL),
+ q1.os_name.string, "'operatingSystem' wrong!");
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "operatingSystemServicePack", NULL),
+ os.os.CSDVersion, "'operatingSystemServicePack' wrong!");
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "operatingSystemVersion", NULL),
+ version_str, "'operatingSystemVersion' wrong!");
+
+ if (old_dnsname != NULL) {
+ /* If before a DNS hostname was set then it should remain
+ the same in combination with the "servicePrincipalName"s.
+ The DNS hostname should also be returned by our
+ "LogonGetDomainInfo" call (in the domain info structure). */
+
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL),
+ old_dnsname, "'DNS hostname' was not set!");
+
+ spn_el = ldb_msg_find_element(res[0], "servicePrincipalName");
+ torture_assert(tctx, ((spns != NULL) && (spn_el != NULL)),
+ "'servicePrincipalName's not set!");
+ torture_assert(tctx, spn_el->num_values == num_spns,
+ "'servicePrincipalName's incorrect!");
+ for (i=0; (i < spn_el->num_values) && (i < num_spns); i++)
+ torture_assert_str_equal(tctx,
+ (char *) spn_el->values[i].data,
+ spns[i], "'servicePrincipalName's incorrect!");
+
+ torture_assert_str_equal(tctx,
+ info.domain_info->dns_hostname.string,
+ old_dnsname,
+ "Out 'DNS hostname' doesn't match the old one!");
+ } else {
+ /* If no DNS hostname was set then also now none should be set,
+ the "servicePrincipalName"s should remain empty and no DNS
+ hostname should be returned by our "LogonGetDomainInfo"
+ call (in the domain info structure). */
+
+ torture_assert(tctx,
+ ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL) == NULL,
+ "'DNS hostname' was set!");
+
+ spn_el = ldb_msg_find_element(res[0], "servicePrincipalName");
+ torture_assert(tctx, ((spns == NULL) && (spn_el == NULL)),
+ "'servicePrincipalName's were set!");
+
+ torture_assert(tctx,
+ info.domain_info->dns_hostname.string == NULL,
+ "Out 'DNS host name' was set!");
+ }
+ }
+
+ /* Checks "workstation flags" */
+ torture_assert(tctx,
+ info.domain_info->workstation_flags
+ == NETR_WS_FLAG_HANDLES_SPN_UPDATE,
+ "Out 'workstation flags' don't match!");
+
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo 2nd call (variation of DNS hostname doesn't work)\n");
+ netlogon_creds_client_authenticator(creds, &a);
+
+ /* Wipe out the CSDVersion, and prove which values still 'stick' */
+ os.os.CSDVersion = "";
+
+ /* Change also the DNS hostname to test differences in behaviour */
+ talloc_free(discard_const_p(char, q1.dns_hostname));
+ q1.dns_hostname = talloc_asprintf(tctx, "%s2.%s", TEST_MACHINE_NAME,
+ lpcfg_dnsdomain(tctx->lp_ctx));
+
+ /* The workstation handles the "servicePrincipalName" and DNS hostname
+ updates */
+ q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r),
+ "LogonGetDomainInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ smb_msleep(250);
+
+ if (sam_ctx) {
+ /* AD workstation infos entry check */
+ ret = gendb_search(sam_ctx, tctx, NULL, &res, attrs,
+ "(sAMAccountName=%s$)", TEST_MACHINE_NAME);
+ torture_assert(tctx, ret == 1, "Test machine account not found in SAMDB on DC! Has the workstation been joined?");
+
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "operatingSystem", NULL),
+ q1.os_name.string, "'operatingSystem' should stick!");
+ torture_assert(tctx,
+ ldb_msg_find_attr_as_string(res[0], "operatingSystemServicePack", NULL) == NULL,
+ "'operatingSystemServicePack' shouldn't stick!");
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "operatingSystemVersion", NULL),
+ version_str, "'operatingSystemVersion' wrong!");
+
+ /* The DNS host name shouldn't have been updated by the server */
+
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL),
+ old_dnsname, "'DNS host name' did change!");
+
+ /* Find the two "servicePrincipalName"s which the DC shouldn't have been
+ updated (HOST/<Netbios name> and HOST/<FQDN name>) - see MS-NRPC
+ 3.5.4.3.9 */
+ spn_el = ldb_msg_find_element(res[0], "servicePrincipalName");
+ torture_assert(tctx, spn_el != NULL,
+ "There should exist 'servicePrincipalName's in AD!");
+ temp_str = talloc_asprintf(tctx, "HOST/%s", TEST_MACHINE_NAME);
+ for (i=0; i < spn_el->num_values; i++)
+ if (strcasecmp((char *) spn_el->values[i].data, temp_str) == 0)
+ break;
+ torture_assert(tctx, i != spn_el->num_values,
+ "'servicePrincipalName' HOST/<Netbios name> not found!");
+ temp_str = talloc_asprintf(tctx, "HOST/%s", old_dnsname);
+ for (i=0; i < spn_el->num_values; i++)
+ if (strcasecmp((char *) spn_el->values[i].data, temp_str) == 0)
+ break;
+ torture_assert(tctx, i != spn_el->num_values,
+ "'servicePrincipalName' HOST/<FQDN name> not found!");
+
+ /* Check that the out DNS hostname was set properly */
+ torture_assert_str_equal(tctx, info.domain_info->dns_hostname.string,
+ old_dnsname, "Out 'DNS hostname' doesn't match the old one!");
+ }
+
+ /* Checks "workstation flags" */
+ torture_assert(tctx,
+ info.domain_info->workstation_flags == NETR_WS_FLAG_HANDLES_SPN_UPDATE,
+ "Out 'workstation flags' don't match!");
+
+
+ /* Now try the same but the workstation flags set to 0 */
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo 3rd call (variation of DNS hostname doesn't work)\n");
+ netlogon_creds_client_authenticator(creds, &a);
+
+ /* Change also the DNS hostname to test differences in behaviour */
+ talloc_free(discard_const_p(char, q1.dns_hostname));
+ q1.dns_hostname = talloc_asprintf(tctx, "%s2.%s", TEST_MACHINE_NAME,
+ lpcfg_dnsdomain(tctx->lp_ctx));
+
+ /* Wipe out the osVersion, and prove which values still 'stick' */
+ q1.os_version.os = NULL;
+
+ /* Let the DC handle the "servicePrincipalName" and DNS hostname
+ updates */
+ q1.workstation_flags = 0;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r),
+ "LogonGetDomainInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ smb_msleep(250);
+
+ if (sam_ctx) {
+ /* AD workstation infos entry check */
+ ret = gendb_search(sam_ctx, tctx, NULL, &res, attrs,
+ "(sAMAccountName=%s$)", TEST_MACHINE_NAME);
+ torture_assert(tctx, ret == 1, "Test machine account not found in SAMDB on DC! Has the workstation been joined?");
+
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "operatingSystem", NULL),
+ q1.os_name.string, "'operatingSystem' should stick!");
+ torture_assert(tctx,
+ ldb_msg_find_attr_as_string(res[0], "operatingSystemServicePack", NULL) == NULL,
+ "'operatingSystemServicePack' shouldn't stick!");
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "operatingSystemVersion", NULL),
+ version_str, "'operatingSystemVersion' wrong!");
+
+ /* The DNS host name shouldn't have been updated by the server */
+
+ torture_assert_str_equal(tctx,
+ ldb_msg_find_attr_as_string(res[0], "dNSHostName", NULL),
+ old_dnsname, "'DNS host name' did change!");
+
+ /* Find the two "servicePrincipalName"s which the DC shouldn't have been
+ updated (HOST/<Netbios name> and HOST/<FQDN name>) - see MS-NRPC
+ 3.5.4.3.9 */
+ spn_el = ldb_msg_find_element(res[0], "servicePrincipalName");
+ torture_assert(tctx, spn_el != NULL,
+ "There should exist 'servicePrincipalName's in AD!");
+ temp_str = talloc_asprintf(tctx, "HOST/%s", TEST_MACHINE_NAME);
+ for (i=0; i < spn_el->num_values; i++)
+ if (strcasecmp((char *) spn_el->values[i].data, temp_str) == 0)
+ break;
+ torture_assert(tctx, i != spn_el->num_values,
+ "'servicePrincipalName' HOST/<Netbios name> not found!");
+ temp_str = talloc_asprintf(tctx, "HOST/%s", old_dnsname);
+ for (i=0; i < spn_el->num_values; i++)
+ if (strcasecmp((char *) spn_el->values[i].data, temp_str) == 0)
+ break;
+ torture_assert(tctx, i != spn_el->num_values,
+ "'servicePrincipalName' HOST/<FQDN name> not found!");
+
+ /* Here the server gives us NULL as the out DNS hostname */
+ torture_assert(tctx, info.domain_info->dns_hostname.string == NULL,
+ "Out 'DNS hostname' should be NULL!");
+ }
+
+ /* Checks "workstation flags" */
+ torture_assert(tctx,
+ info.domain_info->workstation_flags == 0,
+ "Out 'workstation flags' don't match!");
+
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo 4th call (verification of DNS hostname and check for trusted domains)\n");
+ netlogon_creds_client_authenticator(creds, &a);
+
+ /* Put the DNS hostname back */
+ talloc_free(discard_const_p(char, q1.dns_hostname));
+ q1.dns_hostname = talloc_asprintf(tctx, "%s.%s", TEST_MACHINE_NAME,
+ lpcfg_dnsdomain(tctx->lp_ctx));
+
+ /* The workstation handles the "servicePrincipalName" and DNS hostname
+ updates */
+ q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r),
+ "LogonGetDomainInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ smb_msleep(250);
+
+ /* Now the in/out DNS hostnames should be the same */
+ torture_assert_str_equal(tctx,
+ info.domain_info->dns_hostname.string,
+ query.workstation_info->dns_hostname,
+ "In/Out 'DNS hostnames' don't match!");
+ old_dnsname = info.domain_info->dns_hostname.string;
+
+ /* Checks "workstation flags" */
+ torture_assert(tctx,
+ info.domain_info->workstation_flags
+ == NETR_WS_FLAG_HANDLES_SPN_UPDATE,
+ "Out 'workstation flags' don't match!");
+
+ /* Checks for trusted domains */
+ torture_assert(tctx,
+ (info.domain_info->trusted_domain_count != 0)
+ && (info.domain_info->trusted_domains != NULL),
+ "Trusted domains have been requested!");
+
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo 5th call (check for trusted domains)\n");
+ netlogon_creds_client_authenticator(creds, &a);
+
+ /* The workstation handles the "servicePrincipalName" and DNS hostname
+ updates and requests inbound trusts */
+ q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE
+ | NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r),
+ "LogonGetDomainInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ smb_msleep(250);
+
+ /* Checks "workstation flags" */
+ torture_assert(tctx,
+ info.domain_info->workstation_flags
+ == (NETR_WS_FLAG_HANDLES_SPN_UPDATE
+ | NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS),
+ "Out 'workstation flags' don't match!");
+
+ /* Checks for trusted domains */
+ torture_assert(tctx,
+ (info.domain_info->trusted_domain_count != 0)
+ && (info.domain_info->trusted_domains != NULL),
+ "Trusted domains have been requested!");
+
+ odi1 = &info.domain_info->primary_domain;
+
+ torture_assert(tctx, !GUID_all_zero(&odi1->domain_guid),
+ "primary domain_guid needs to be valid");
+
+ for (i=0; i < info.domain_info->trusted_domain_count; i++) {
+ struct netr_OneDomainInfo *odiT =
+ &info.domain_info->trusted_domains[i];
+ struct netr_trust_extension_info *texT = NULL;
+
+ torture_assert_int_equal(tctx, odiT->trust_extension.length, 16,
+ "trust_list should have extension");
+ torture_assert(tctx, odiT->trust_extension.info != NULL,
+ "trust_list should have extension");
+ texT = &odiT->trust_extension.info->info;
+
+ if (GUID_equal(&odiT->domain_guid, &odi1->domain_guid)) {
+ odi2 = odiT;
+ tex2 = texT;
+ continue;
+ }
+
+ torture_assert_int_equal(tctx,
+ texT->flags & NETR_TRUST_FLAG_PRIMARY,
+ 0,
+ "trust_list flags should not have PRIMARY");
+
+ torture_assert(tctx, odiT->domainname.string != NULL,
+ "trust_list domainname should be valid");
+ if (texT->trust_type == LSA_TRUST_TYPE_DOWNLEVEL ||
+ texT->trust_type == LSA_TRUST_TYPE_MIT)
+ {
+ torture_assert(tctx, odiT->dns_domainname.string == NULL,
+ "trust_list dns_domainname should be NULL for downlevel or MIT");
+ } else {
+ torture_assert(tctx, odiT->dns_domainname.string != NULL,
+ "trust_list dns_domainname should be valid for uplevel");
+ }
+ torture_assert(tctx, odiT->dns_forestname.string == NULL,
+ "trust_list dns_forestname needs to be NULL");
+
+ torture_assert(tctx, odiT->domain_sid != NULL,
+ "trust_list domain_sid needs to be valid");
+ }
+
+ torture_assert(tctx, odi2 != NULL,
+ "trust_list primary domain not found.");
+
+ torture_assert_str_equal(tctx,
+ odi1->domainname.string,
+ odi2->domainname.string,
+ "netbios name should match");
+
+ temp_str = talloc_strdup(tctx, odi1->dns_domainname.string);
+ torture_assert(tctx, temp_str != NULL,
+ "primary_domain dns_domainname copy");
+ temp_str2 = strrchr(temp_str, '.');
+ torture_assert(tctx, temp_str2 != NULL && temp_str2[1] == '\0',
+ "primary_domain dns_domainname needs trailing '.'");
+ temp_str2[0] = '\0';
+ torture_assert_str_equal(tctx,
+ temp_str,
+ odi2->dns_domainname.string,
+ "dns domainname should match "
+ "(without trailing '.')");
+
+ temp_str = talloc_strdup(tctx, odi1->dns_forestname.string);
+ torture_assert(tctx, temp_str != NULL,
+ "primary_domain dns_forestname copy");
+ temp_str2 = strrchr(temp_str, '.');
+ torture_assert(tctx, temp_str2 != NULL && temp_str2[1] == '\0',
+ "primary_domain dns_forestname needs trailing '.'");
+ temp_str2[0] = '\0';
+ torture_assert(tctx, odi2->dns_forestname.string == NULL,
+ "trust_list dns_forestname needs to be NULL");
+
+ torture_assert_guid_equal(tctx, odi1->domain_guid, odi2->domain_guid,
+ "domain_guid should match");
+ torture_assert(tctx, odi1->domain_sid != NULL,
+ "primary domain_sid needs to be valid");
+ torture_assert(tctx, odi2->domain_sid != NULL,
+ "trust_list domain_sid needs to be valid");
+ torture_assert_sid_equal(tctx, odi1->domain_sid, odi2->domain_sid,
+ "domain_sid should match");
+
+ torture_assert_int_equal(tctx, odi1->trust_extension.length, 0,
+ "primary_domain should not have extension");
+ torture_assert_int_equal(tctx, odi2->trust_extension.length, 16,
+ "trust_list should have extension");
+ torture_assert(tctx, odi2->trust_extension.info != NULL,
+ "trust_list should have extension");
+ tex2 = &odi2->trust_extension.info->info;
+ torture_assert_int_equal(tctx,
+ tex2->flags & NETR_TRUST_FLAG_PRIMARY,
+ NETR_TRUST_FLAG_PRIMARY,
+ "trust_list flags should have PRIMARY");
+ torture_assert_int_equal(tctx,
+ tex2->flags & NETR_TRUST_FLAG_IN_FOREST,
+ NETR_TRUST_FLAG_IN_FOREST,
+ "trust_list flags should have IN_FOREST");
+ torture_assert_int_equal(tctx,
+ tex2->flags & NETR_TRUST_FLAG_NATIVE,
+ NETR_TRUST_FLAG_NATIVE,
+ "trust_list flags should have NATIVE");
+ torture_assert_int_equal(tctx,
+ tex2->flags & ~NETR_TRUST_FLAG_TREEROOT,
+ NETR_TRUST_FLAG_IN_FOREST |
+ NETR_TRUST_FLAG_PRIMARY |
+ NETR_TRUST_FLAG_NATIVE,
+ "trust_list flags IN_FOREST, PRIMARY, NATIVE "
+ "(TREEROOT optional)");
+ if (strcmp(odi1->dns_domainname.string, odi1->dns_forestname.string) == 0) {
+ torture_assert_int_equal(tctx,
+ tex2->flags & NETR_TRUST_FLAG_TREEROOT,
+ NETR_TRUST_FLAG_TREEROOT,
+ "trust_list flags TREEROOT on forest root");
+ torture_assert_int_equal(tctx,
+ tex2->parent_index, 0,
+ "trust_list no parent on foreset root");
+ }
+ torture_assert_int_equal(tctx,
+ tex2->trust_type, LSA_TRUST_TYPE_UPLEVEL,
+ "trust_list uplevel");
+ torture_assert_int_equal(tctx,
+ tex2->trust_attributes, 0,
+ "trust_list no attributes");
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo 6th call (no DNS hostname)\n");
+ netlogon_creds_client_authenticator(creds, &a);
+
+ query.workstation_info->dns_hostname = NULL;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r),
+ "LogonGetDomainInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ /* The old DNS hostname should stick */
+ torture_assert_str_equal(tctx,
+ info.domain_info->dns_hostname.string,
+ old_dnsname,
+ "'DNS hostname' changed!");
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo 7th call (extra workstation flags)\n");
+ netlogon_creds_client_authenticator(creds, &a);
+
+ q1.workstation_flags = NETR_WS_FLAG_HANDLES_SPN_UPDATE
+ | NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS | 0x4;
+
+ /* Put the DNS hostname back */
+ talloc_free(discard_const_p(char, q1.dns_hostname));
+ q1.dns_hostname = talloc_asprintf(tctx, "%s.%s", TEST_MACHINE_NAME,
+ lpcfg_dnsdomain(tctx->lp_ctx));
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r),
+ "LogonGetDomainInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed");
+
+ /* Checks "workstation flags" */
+ torture_assert(tctx,
+ info.domain_info->workstation_flags
+ == (NETR_WS_FLAG_HANDLES_SPN_UPDATE
+ | NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS),
+ "Out 'workstation flags' don't match!");
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ torture_comment(tctx, "Not testing netr_LogonGetDomainInfo 8th call (no workstation info) - enable dangerous tests in order to do so\n");
+ } else {
+ /* Try a call without the workstation information structure */
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo 8th call (no workstation info)\n");
+ netlogon_creds_client_authenticator(creds, &a);
+
+ query.workstation_info = NULL;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonGetDomainInfo_r(b, tctx, &r),
+ "LogonGetDomainInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonGetDomainInfo failed");
+ torture_assert(tctx, netlogon_creds_client_check(creds, &a.cred), "Credential chaining failed");
+ }
+
+ return true;
+}
+
+static bool test_GetDomainInfo_async(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct netr_LogonGetDomainInfo r;
+ struct netr_WorkstationInformation q1;
+ struct netr_Authenticator a;
+#define ASYNC_COUNT 100
+ struct netlogon_creds_CredentialState *creds;
+ struct netlogon_creds_CredentialState *creds_async[ASYNC_COUNT];
+ struct tevent_req *req[ASYNC_COUNT];
+ int i;
+ union netr_WorkstationInfo query;
+ union netr_DomainInfo info;
+ struct dcerpc_pipe *p = NULL;
+
+ torture_comment(tctx, "Testing netr_LogonGetDomainInfo - async count %d\n", ASYNC_COUNT);
+
+ if (!test_SetupCredentials3(p, tctx, NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES,
+ machine_credentials, &creds)) {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(p1, tctx, machine_credentials, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p)) {
+ 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.credential = &a;
+ r.in.level = 1;
+ r.in.return_authenticator = &a;
+ r.in.query = &query;
+ r.out.return_authenticator = &a;
+ r.out.info = &info;
+
+ ZERO_STRUCT(q1);
+ q1.dns_hostname = talloc_asprintf(tctx, "%s.%s", TEST_MACHINE_NAME,
+ lpcfg_dnsdomain(tctx->lp_ctx));
+ q1.sitename = "Default-First-Site-Name";
+ q1.os_name.string = "UNIX/Linux or similar";
+
+ query.workstation_info = &q1;
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ netlogon_creds_client_authenticator(creds, &a);
+
+ creds_async[i] = (struct netlogon_creds_CredentialState *)talloc_memdup(creds, creds, sizeof(*creds));
+ req[i] = dcerpc_netr_LogonGetDomainInfo_r_send(tctx, tctx->ev, p->binding_handle, &r);
+
+ /* even with this flush per request a w2k3 server seems to
+ clag with multiple outstanding requests. bleergh. */
+ torture_assert_int_equal(tctx, tevent_loop_once(tctx->ev), 0,
+ "tevent_loop_once failed");
+ }
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ torture_assert_int_equal(tctx, tevent_req_poll(req[i], tctx->ev), true,
+ "tevent_req_poll() failed");
+
+ status = dcerpc_netr_LogonGetDomainInfo_r_recv(req[i], tctx);
+
+ torture_assert_ntstatus_ok(tctx, status, "netr_LogonGetDomainInfo_async");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "netr_LogonGetDomainInfo_async");
+
+ torture_assert(tctx, netlogon_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_COUNT);
+
+ return true;
+}
+
+static bool test_ManyGetDCName(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct cli_credentials *anon_creds;
+ struct dcerpc_binding *binding2;
+ 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;
+ const char *dcname = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_binding_handle *b2;
+
+ int i;
+
+ if (p->conn->transport.transport != NCACN_NP) {
+ torture_skip(tctx, "test_ManyGetDCName works only with NCACN_NP");
+ }
+
+ torture_comment(tctx, "Torturing GetDCName\n");
+
+ anon_creds = cli_credentials_init_anon(tctx);
+ torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon failed");
+
+ binding2 = dcerpc_binding_dup(tctx, p->binding);
+ /* Swap the binding details from NETLOGON to LSA */
+ status = dcerpc_epm_map_binding(tctx, binding2, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx);
+ dcerpc_binding_set_assoc_group_id(binding2, 0);
+ torture_assert_ntstatus_ok(tctx, status, "epm map");
+
+ status = dcerpc_secondary_auth_connection(p, binding2, &ndr_table_lsarpc,
+ anon_creds, tctx->lp_ctx,
+ tctx, &p2);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+ b2 = p2->binding_handle;
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenPolicy2_r(b2, tctx, &o),
+ "OpenPolicy2 failed");
+ torture_assert_ntstatus_ok(tctx, o.out.result, "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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumTrustDom_r(b2, tctx, &t),
+ "EnumTrustDom failed");
+
+ if ((!NT_STATUS_IS_OK(t.out.result) &&
+ (!NT_STATUS_EQUAL(t.out.result, 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));
+ d.out.dcname = &dcname;
+
+ 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_r(b, tctx, &d);
+ torture_assert_ntstatus_ok(tctx, status, "GetAnyDCName");
+
+ torture_comment(tctx, "\tDC for domain %s is %s\n", info->name.string,
+ dcname ? dcname : "unknown");
+ }
+
+ return true;
+}
+
+static bool test_lsa_over_netlogon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct cli_credentials *anon_creds;
+ const struct dcerpc_binding *binding2;
+ struct dcerpc_pipe *p2;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 o;
+ struct policy_handle lsa_handle;
+
+ struct dcerpc_binding_handle *b2;
+
+
+ if (p->conn->transport.transport != NCACN_NP) {
+ torture_skip(tctx, "test_lsa_over_netlogon works only with NCACN_NP");
+ }
+
+ torture_comment(tctx, "Testing if we can access the LSA server over\n"
+ " \\\\pipe\\netlogon rather than \\\\pipe\\lsarpc\n");
+
+ anon_creds = cli_credentials_init_anon(tctx);
+ torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon failed");
+
+ binding2 = p->binding;
+
+ status = dcerpc_secondary_auth_connection(p, binding2, &ndr_table_lsarpc,
+ anon_creds, tctx->lp_ctx,
+ tctx, &p2);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+ b2 = p2->binding_handle;
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenPolicy2_r(b2, tctx, &o),
+ "OpenPolicy2 failed");
+ torture_assert_ntstatus_ok(tctx, o.out.result, "OpenPolicy2 failed");
+
+ talloc_free(p2);
+
+ return true;
+}
+
+static bool test_SetPassword_with_flags(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t flags[] = { 0, NETLOGON_NEG_STRONG_KEYS };
+ struct netlogon_creds_CredentialState *creds;
+ int i;
+
+ if (!test_SetupCredentials2(p, tctx, 0,
+ machine_credentials,
+ cli_credentials_get_secure_channel_type(machine_credentials),
+ &creds)) {
+ torture_skip(tctx, "DC does not support negotiation of 64bit session keys");
+ }
+
+ for (i=0; i < ARRAY_SIZE(flags); i++) {
+ torture_assert(tctx,
+ test_SetPassword_flags(tctx, p, machine_credentials, flags[i]),
+ talloc_asprintf(tctx, "failed to test SetPassword negotiating with 0x%08x flags", flags[i]));
+ }
+
+ 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_bdc_rpc_iface_tcase(suite, "netlogon",
+ &ndr_table_netlogon, TEST_MACHINE_NAME);
+
+ torture_rpc_tcase_add_test_creds(tcase, "SetupCredentialsDowngrade", test_SetupCredentialsDowngrade);
+ torture_rpc_tcase_add_test(tcase, "lsa_over_netlogon", test_lsa_over_netlogon);
+
+ torture_rpc_tcase_add_test_creds(tcase, "GetForestTrustInformation", test_netr_GetForestTrustInformation);
+ torture_rpc_tcase_add_test_creds(tcase, "ServerGetTrustInfo_AES", test_netr_ServerGetTrustInfo_AES);
+ torture_rpc_tcase_add_test_creds(tcase, "ServerGetTrustInfo", test_netr_ServerGetTrustInfo);
+ torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesExW", test_netr_DsRAddressToSitenamesExW);
+ torture_rpc_tcase_add_test(tcase, "DsRAddressToSitenamesW", test_netr_DsRAddressToSitenamesW);
+ torture_rpc_tcase_add_test(tcase, "DsrGetDcSiteCoverageW", test_netr_DsrGetDcSiteCoverageW);
+ torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx2", test_netr_DsRGetDCNameEx2);
+ torture_rpc_tcase_add_test(tcase, "DsRGetDCNameEx", test_netr_DsRGetDCNameEx);
+ torture_rpc_tcase_add_test(tcase, "DsRGetDCName", test_netr_DsRGetDCName);
+ test = torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo_async", test_GetDomainInfo_async);
+ test->dangerous = true;
+ torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomainsEx", test_netr_NetrEnumerateTrustedDomainsEx);
+ torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomains", test_netr_NetrEnumerateTrustedDomains);
+ torture_rpc_tcase_add_test(tcase, "DsrEnumerateDomainTrusts", test_DsrEnumerateDomainTrusts);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync2", test_DatabaseSync2);
+ torture_rpc_tcase_add_test(tcase, "GetAnyDCName", test_GetAnyDCName);
+ torture_rpc_tcase_add_test(tcase, "ManyGetDCName", test_ManyGetDCName);
+ torture_rpc_tcase_add_test(tcase, "GetDcName", test_GetDcName);
+ torture_rpc_tcase_add_test_creds(tcase, "AccountSync", test_AccountSync);
+ torture_rpc_tcase_add_test_creds(tcase, "AccountDeltas", test_AccountDeltas);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseRedo", test_DatabaseRedo);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseDeltas", test_DatabaseDeltas);
+ torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync", test_DatabaseSync);
+ torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo", test_GetDomainInfo);
+ torture_rpc_tcase_add_test_creds(tcase, "GetTrustPasswords", test_GetTrustPasswords);
+ torture_rpc_tcase_add_test_creds(tcase, "GetPassword", test_GetPassword);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword2_AES", test_SetPassword2_AES);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword2", test_SetPassword2);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword", test_SetPassword);
+ torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuse", test_ServerReqChallengeReuse);
+ torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuseGlobal4", test_ServerReqChallengeReuseGlobal4);
+ torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuseGlobal3", test_ServerReqChallengeReuseGlobal3);
+ torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuseGlobal2", test_ServerReqChallengeReuseGlobal2);
+ torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeReuseGlobal", test_ServerReqChallengeReuseGlobal);
+ torture_rpc_tcase_add_test_creds(tcase, "ServerReqChallengeGlobal", test_ServerReqChallengeGlobal);
+ torture_rpc_tcase_add_test_creds(tcase, "invalidAuthenticate2", test_invalidAuthenticate2);
+ torture_rpc_tcase_add_test_creds(tcase, "SamLogon", test_SamLogon);
+ torture_rpc_tcase_add_test(tcase, "LogonUasLogoff", test_LogonUasLogoff);
+ torture_rpc_tcase_add_test(tcase, "LogonUasLogon", test_LogonUasLogon);
+
+ torture_rpc_tcase_add_test(tcase, "Broken RPC binding handle",
+ test_netr_broken_binding_handle);
+
+ return suite;
+}
+
+struct torture_suite *torture_rpc_netlogon_s3(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "netlogon-s3");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netlogon",
+ &ndr_table_netlogon, TEST_MACHINE_NAME);
+
+ torture_rpc_tcase_add_test_creds(tcase, "SamLogon", test_SamLogon);
+ torture_rpc_tcase_add_test_creds(tcase, "SamLogon_NULL_domain", test_SamLogon_NULL_domain);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword", test_SetPassword);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword_with_flags", test_SetPassword_with_flags);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword2", test_SetPassword2);
+ torture_rpc_tcase_add_test_creds(tcase, "SetPassword2_AES", test_SetPassword2_AES);
+ torture_rpc_tcase_add_test(tcase, "NetrEnumerateTrustedDomains", test_netr_NetrEnumerateTrustedDomains);
+
+ return suite;
+}
+
+struct torture_suite *torture_rpc_netlogon_zerologon(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(
+ mem_ctx,
+ "netlogon.zerologon");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(
+ suite,
+ "netlogon",
+ &ndr_table_netlogon,
+ TEST_MACHINE_NAME);
+
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "ServerReqChallenge",
+ test_ServerReqChallenge);
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "ServerReqChallenge_zero_challenge",
+ test_ServerReqChallenge_zero_challenge);
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "ServerReqChallenge_5_repeats",
+ test_ServerReqChallenge_5_repeats);
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "ServerReqChallenge_4_repeats",
+ test_ServerReqChallenge_4_repeats);
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "test_SetPassword2_encrypted_to_all_zeros",
+ test_SetPassword2_encrypted_to_all_zeros);
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "test_SetPassword2_password_encrypts_to_zero",
+ test_SetPassword2_password_encrypts_to_zero);
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "test_SetPassword2_confounder",
+ test_SetPassword2_confounder);
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "test_SetPassword2_all_zeros",
+ test_SetPassword2_all_zeros);
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "test_SetPassword2_all_zero_password",
+ test_SetPassword2_all_zero_password);
+ torture_rpc_tcase_add_test_creds(
+ tcase,
+ "test_SetPassword2_maximum_length_password",
+ test_SetPassword2_maximum_length_password);
+
+ return suite;
+}
+
+struct torture_suite *torture_rpc_netlogon_admin(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "netlogon.admin");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "bdc",
+ &ndr_table_netlogon, TEST_MACHINE_NAME);
+ torture_rpc_tcase_add_test_creds(tcase, "LogonControl", test_LogonControl);
+ torture_rpc_tcase_add_test_creds(tcase, "LogonControl2", test_LogonControl2);
+ torture_rpc_tcase_add_test_creds(tcase, "LogonControl2Ex", test_LogonControl2Ex);
+
+ tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "wkst",
+ &ndr_table_netlogon, TEST_MACHINE_NAME);
+ torture_rpc_tcase_add_test_creds(tcase, "LogonControl", test_LogonControl);
+ torture_rpc_tcase_add_test_creds(tcase, "LogonControl2", test_LogonControl2);
+ torture_rpc_tcase_add_test_creds(tcase, "LogonControl2Ex", test_LogonControl2Ex);
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "admin",
+ &ndr_table_netlogon);
+ torture_rpc_tcase_add_test_creds(tcase, "LogonControl", test_LogonControl);
+ torture_rpc_tcase_add_test_creds(tcase, "LogonControl2", test_LogonControl2);
+ torture_rpc_tcase_add_test_creds(tcase, "LogonControl2Ex", test_LogonControl2Ex);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/netlogon.h b/source4/torture/rpc/netlogon.h
new file mode 100644
index 0000000..a4ab8f0
--- /dev/null
+++ b/source4/torture/rpc/netlogon.h
@@ -0,0 +1,37 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB torture tester
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2008-2009
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2010
+
+ 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/>.
+*/
+
+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 netlogon_creds_CredentialState **creds_out);
+
+bool test_SetupCredentials3(struct dcerpc_pipe *p, struct torture_context *tctx,
+ uint32_t negotiate_flags,
+ struct cli_credentials *machine_credentials,
+ struct netlogon_creds_CredentialState **creds_out);
+
+bool test_SetupCredentialsPipe(const struct dcerpc_pipe *p1,
+ struct torture_context *tctx,
+ struct cli_credentials *machine_credentials,
+ struct netlogon_creds_CredentialState *creds,
+ uint32_t additional_flags,
+ struct dcerpc_pipe **_p2);
diff --git a/source4/torture/rpc/netlogon_crypto.c b/source4/torture/rpc/netlogon_crypto.c
new file mode 100644
index 0000000..8584460
--- /dev/null
+++ b/source4/torture/rpc/netlogon_crypto.c
@@ -0,0 +1,274 @@
+/*
+ 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
+ Copyright (C) Matthias Dieter Wallnöfer 2009-2010
+
+ 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/replace/system/network.h"
+#include "lib/cmdline/cmdline.h"
+#include "torture/rpc/torture_rpc.h"
+#include "libcli/auth/libcli_auth.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "param/param.h"
+#include "lib/param/loadparm.h"
+#include "libcli/security/security.h"
+
+#undef strcasecmp
+
+#define TEST_MACHINE_NAME "torturetest"
+
+static bool test_ServerAuth3Crypto(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ uint32_t negotiate_flags,
+ struct cli_credentials *machine_credentials,
+ bool force_client_rc4)
+{
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate3 a;
+ struct netr_Credential netr_creds1 = {
+ .data = {0},
+ };
+ struct netr_Credential netr_creds2 = {
+ .data = {0},
+ };
+ struct netr_Credential netr_creds3 = {
+ .data = {0},
+ };
+ struct netlogon_creds_CredentialState *creds_state = NULL;
+ struct samr_Password machine_password = {
+ .hash = {0},
+ };
+ const char *machine_name = NULL;
+ const char *plain_pass = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ uint32_t rid = 0;
+ NTSTATUS status;
+ bool weak_crypto_allowed =
+ (lpcfg_weak_crypto(tctx->lp_ctx) ==
+ SAMBA_WEAK_CRYPTO_ALLOWED);
+
+ if (p == NULL) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(a);
+
+ torture_comment(tctx, "client negotiate_flags=0x%08x\n", negotiate_flags);
+
+ machine_name = cli_credentials_get_workstation(machine_credentials);
+ torture_assert_not_null(tctx, machine_name, "machine name is not set");
+
+ plain_pass = cli_credentials_get_password(machine_credentials);
+ torture_assert_not_null(tctx, plain_pass, "plain_pass is not set");
+
+
+ torture_comment(tctx, "Testing ServerReqChallenge\n");
+
+ r.in.server_name = NULL;
+ r.in.computer_name = machine_name;
+ r.in.credentials = &netr_creds1;
+ r.out.return_credentials = &netr_creds2;
+
+ netlogon_creds_random_challenge(&netr_creds1);
+
+ status = dcerpc_netr_ServerReqChallenge_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx,
+ r.out.result,
+ "ServerReqChallenge failed");
+
+ E_md4hash(plain_pass, machine_password.hash);
+
+ a.in.server_name = NULL;
+ a.in.account_name = talloc_asprintf(tctx, "%s$", machine_name);
+ a.in.secure_channel_type =
+ cli_credentials_get_secure_channel_type(machine_credentials);
+ a.in.computer_name = machine_name;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.in.credentials = &netr_creds3;
+ a.out.return_credentials = &netr_creds3;
+ a.out.negotiate_flags = &negotiate_flags;
+ a.out.rid = &rid;
+
+ if (force_client_rc4) {
+ GNUTLS_FIPS140_SET_LAX_MODE();
+ }
+ creds_state = netlogon_creds_client_init(tctx,
+ a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ &netr_creds1,
+ &netr_creds2,
+ &machine_password,
+ &netr_creds3,
+ negotiate_flags);
+ GNUTLS_FIPS140_SET_STRICT_MODE();
+ /* Test that we fail to encrypt with RC4 */
+ if (creds_state == NULL &&
+ !weak_crypto_allowed && !force_client_rc4 &&
+ (negotiate_flags & NETLOGON_NEG_ARCFOUR)) {
+ return false;
+ }
+ torture_assert_not_null(tctx,
+ creds_state,
+ "Failed init netlogon client creds");
+
+
+ torture_comment(tctx, "Testing ServerAuthenticate3\n");
+
+ status = dcerpc_netr_ServerAuthenticate3_r(b, tctx, &a);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "ServerAuthenticate3 failed");
+
+ /* Check that the server denies RC4 */
+ if (!NT_STATUS_IS_OK(a.out.result) &&
+ !weak_crypto_allowed &&
+ force_client_rc4) {
+ torture_assert_ntstatus_equal(tctx,
+ a.out.result,
+ NT_STATUS_DOWNGRADE_DETECTED,
+ "Unexpected status code");
+ return false;
+ }
+ torture_assert_ntstatus_ok(tctx,
+ a.out.result,
+ "ServerAuthenticate3 failed");
+ torture_assert(tctx,
+ netlogon_creds_client_check(creds_state, &netr_creds3),
+ "Credential chaining failed");
+
+ torture_comment(tctx,
+ "server negotiate_flags=0x%08x\n",
+ negotiate_flags);
+
+ if (!weak_crypto_allowed) {
+ torture_assert(tctx,
+ (negotiate_flags & NETLOGON_NEG_ARCFOUR) == 0,
+ "Server should not announce RC4 support");
+ }
+
+ /* Prove that requesting a challenge again won't break it */
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_ServerReqChallenge_r(b, tctx, &r),
+ "ServerReqChallenge failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ServerReqChallenge failed");
+
+ return true;
+}
+
+
+/* Test that we can successfully authenticate using AES. */
+static bool test_AES_Crypto(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t negotiate_flags =
+ NETLOGON_NEG_AUTH2_ADS_FLAGS|
+ NETLOGON_NEG_SUPPORTS_AES;
+ bool ok;
+
+ ok = test_ServerAuth3Crypto(p,
+ tctx,
+ negotiate_flags,
+ machine_credentials,
+ false);
+ if (!ok) {
+ return false;
+ }
+
+ return true;
+}
+
+/* If we try to use RC4, the client code should fail to encrypt. */
+static bool test_RC4_Crypto_Fail(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t negotiate_flags =
+ NETLOGON_NEG_AUTH2_ADS_FLAGS|
+ NETLOGON_NEG_ARCFOUR;
+ bool ok;
+
+ ok = test_ServerAuth3Crypto(p,
+ tctx,
+ negotiate_flags,
+ machine_credentials,
+ false);
+ if (!ok) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Enforce the use of RC4 and try to authenticate. The server should fail
+ * in this case as it doesn't allow RC4
+ */
+static bool test_RC4_Crypto_Force(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ uint32_t negotiate_flags =
+ NETLOGON_NEG_AUTH2_ADS_FLAGS|
+ NETLOGON_NEG_ARCFOUR;
+ bool ok;
+
+ ok = test_ServerAuth3Crypto(p,
+ tctx,
+ negotiate_flags,
+ machine_credentials,
+ true);
+ if (!ok) {
+ return true;
+ }
+
+ return false;
+}
+
+struct torture_suite *torture_rpc_netlogon_crypto_fips(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx,
+ "fips.netlogon.crypto");
+ struct torture_rpc_tcase *tcase = NULL;
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite,
+ "netlogon",
+ &ndr_table_netlogon,
+ TEST_MACHINE_NAME);
+
+ torture_rpc_tcase_add_test_creds(tcase,
+ "test_AES_Crytpo",
+ test_AES_Crypto);
+ torture_rpc_tcase_add_test_creds(tcase,
+ "test_RC4_Crytpo_Fail",
+ test_RC4_Crypto_Fail);
+ torture_rpc_tcase_add_test_creds(tcase,
+ "test_RC4_Crytpo_Force",
+ test_RC4_Crypto_Force);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/ntsvcs.c b/source4/torture/rpc/ntsvcs.c
new file mode 100644
index 0000000..a25129d
--- /dev/null
+++ b/source4/torture/rpc/ntsvcs.c
@@ -0,0 +1,189 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for rpc ntsvcs operations
+
+ Copyright (C) Guenther Deschner 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "torture/rpc/torture_rpc.h"
+#include "librpc/gen_ndr/ndr_ntsvcs_c.h"
+
+static bool test_PNP_GetVersion(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+ struct PNP_GetVersion r;
+ uint16_t version = 0;
+
+ r.out.version = &version;
+
+ status = dcerpc_PNP_GetVersion_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "PNP_GetVersion");
+ torture_assert_werr_ok(tctx, r.out.result, "PNP_GetVersion");
+ torture_assert_int_equal(tctx, version, 0x400, "invalid version");
+
+ return true;
+}
+
+static bool test_PNP_GetDeviceListSize(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct PNP_GetDeviceListSize r;
+ uint32_t size = 0;
+
+ r.in.devicename = NULL;
+ r.in.flags = CM_GETIDLIST_FILTER_SERVICE;
+ r.out.size = &size;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_PNP_GetDeviceListSize_r(b, tctx, &r),
+ "PNP_GetDeviceListSize");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_CM_INVALID_POINTER,
+ "PNP_GetDeviceListSize");
+
+ r.in.devicename = "Spooler";
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_PNP_GetDeviceListSize_r(b, tctx, &r),
+ "PNP_GetDeviceListSize");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "PNP_GetDeviceListSize");
+
+ return true;
+}
+
+static bool test_PNP_GetDeviceList(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct PNP_GetDeviceList r;
+ uint16_t *buffer = NULL;
+ uint32_t length = 0;
+
+ buffer = talloc_array(tctx, uint16_t, 0);
+
+ r.in.filter = NULL;
+ r.in.flags = CM_GETIDLIST_FILTER_SERVICE;
+ r.in.length = &length;
+ r.out.length = &length;
+ r.out.buffer = buffer;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_PNP_GetDeviceList_r(b, tctx, &r),
+ "PNP_GetDeviceList failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_CM_INVALID_POINTER,
+ "PNP_GetDeviceList failed");
+
+ r.in.filter = "Spooler";
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_PNP_GetDeviceList_r(b, tctx, &r),
+ "PNP_GetDeviceList failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_CM_BUFFER_SMALL)) {
+ struct PNP_GetDeviceListSize s;
+
+ s.in.devicename = "Spooler";
+ s.in.flags = CM_GETIDLIST_FILTER_SERVICE;
+ s.out.size = &length;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_PNP_GetDeviceListSize_r(b, tctx, &s),
+ "PNP_GetDeviceListSize failed");
+ torture_assert_werr_ok(tctx, s.out.result,
+ "PNP_GetDeviceListSize failed");
+ }
+
+ buffer = talloc_array(tctx, uint16_t, length);
+
+ r.in.length = &length;
+ r.out.length = &length;
+ r.out.buffer = buffer;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_PNP_GetDeviceList_r(b, tctx, &r),
+ "PNP_GetDeviceList failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "PNP_GetDeviceList failed");
+
+ return true;
+}
+
+static bool test_PNP_GetDeviceRegProp(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+ struct PNP_GetDeviceRegProp r;
+
+ enum winreg_Type reg_data_type = REG_NONE;
+ uint32_t buffer_size = 0;
+ uint32_t needed = 0;
+ uint8_t *buffer;
+
+ buffer = talloc(tctx, uint8_t);
+
+ r.in.devicepath = "ACPI\\ACPI0003\\1";
+ r.in.property = DEV_REGPROP_DESC;
+ r.in.flags = 0;
+ r.in.reg_data_type = &reg_data_type;
+ r.in.buffer_size = &buffer_size;
+ r.in.needed = &needed;
+ r.out.buffer = buffer;
+ r.out.reg_data_type = &reg_data_type;
+ r.out.buffer_size = &buffer_size;
+ r.out.needed = &needed;
+
+ status = dcerpc_PNP_GetDeviceRegProp_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceRegProp");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_CM_BUFFER_SMALL)) {
+
+ buffer = talloc_array(tctx, uint8_t, needed);
+ r.in.buffer_size = &needed;
+
+ status = dcerpc_PNP_GetDeviceRegProp_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceRegProp");
+ }
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_ntsvcs(TALLOC_CTX *mem_ctx)
+{
+ struct torture_rpc_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "ntsvcs");
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "ntsvcs",
+ &ndr_table_ntsvcs);
+
+ torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceRegProp",
+ test_PNP_GetDeviceRegProp);
+ torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceList",
+ test_PNP_GetDeviceList);
+ torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceListSize",
+ test_PNP_GetDeviceListSize);
+ torture_rpc_tcase_add_test(tcase, "PNP_GetVersion",
+ test_PNP_GetVersion);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/object_uuid.c b/source4/torture/rpc/object_uuid.c
new file mode 100644
index 0000000..2209954
--- /dev/null
+++ b/source4/torture/rpc/object_uuid.c
@@ -0,0 +1,85 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for behaviour of object uuids in rpc requests
+
+ Copyright (C) Stefan Metzmacher 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 "librpc/gen_ndr/ndr_dssetup.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
+#include "torture/rpc/torture_rpc.h"
+
+/*
+ this tests the send object uuids in the dcerpc request
+*/
+
+static bool test_random_uuid(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p1, *p2;
+ struct GUID uuid;
+ struct dssetup_DsRoleGetPrimaryDomainInformation r1;
+ struct lsa_GetUserName r2;
+ struct lsa_String *authority_name_p = NULL;
+ struct lsa_String *account_name_p = NULL;
+
+ torture_comment(torture, "RPC-OBJECTUUID-RANDOM\n");
+
+ status = torture_rpc_connection(torture, &p1, &ndr_table_dssetup);
+ torture_assert_ntstatus_ok(torture, status, "opening dsetup pipe1");
+
+ status = torture_rpc_connection(torture, &p2, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1");
+
+ uuid = GUID_random();
+
+ r1.in.level = DS_ROLE_BASIC_INFORMATION;
+ status = dcerpc_binding_handle_call(p1->binding_handle,
+ &uuid,
+ &ndr_table_dssetup,
+ NDR_DSSETUP_DSROLEGETPRIMARYDOMAININFORMATION,
+ torture, &r1);
+ torture_assert_ntstatus_ok(torture, status, "DsRoleGetPrimaryDomainInformation failed");
+ torture_assert_werr_ok(torture, r1.out.result, "DsRoleGetPrimaryDomainInformation failed");
+
+ uuid = GUID_random();
+
+ r2.in.system_name = "\\";
+ r2.in.account_name = &account_name_p;
+ r2.in.authority_name = &authority_name_p;
+ r2.out.account_name = &account_name_p;
+ r2.out.authority_name = &authority_name_p;
+
+ status = dcerpc_binding_handle_call(p2->binding_handle,
+ &uuid,
+ &ndr_table_lsarpc,
+ NDR_LSA_GETUSERNAME,
+ torture, &r2);
+ torture_assert_ntstatus_ok(torture, status, "lsaClose failed");
+ torture_assert_ntstatus_ok(torture, r2.out.result, "lsaClose failed");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_object_uuid(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite;
+ suite = torture_suite_create(mem_ctx, "objectuuid");
+ torture_suite_add_simple_test(suite, "random-uuid", test_random_uuid);
+ return suite;
+}
diff --git a/source4/torture/rpc/oxidresolve.c b/source4/torture/rpc/oxidresolve.c
new file mode 100644
index 0000000..11cd8fe
--- /dev/null
+++ b/source4/torture/rpc/oxidresolve.c
@@ -0,0 +1,263 @@
+/*
+ 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 "librpc/gen_ndr/ndr_oxidresolver_c.h"
+#include "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/epmapper.h"
+#include "torture/rpc/torture_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;
+ struct dcerpc_binding_handle *b;
+ struct ORPCTHAT that;
+ struct DUALSTRINGARRAY *pdsaOxidBindings;
+ uint32_t AuthnHint;
+ struct COMVERSION ServerVersion;
+ HRESULT hr;
+ struct MInterfacePointer *ifaces;
+
+ status = torture_rpc_connection(tctx, &p,
+ &ndr_table_IRemoteActivation);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ZERO_STRUCT(r);
+
+ r.in.this_object.version.MajorVersion = 5;
+ r.in.this_object.version.MinorVersion = 1;
+ r.in.this_object.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.that = &that;
+ r.out.pOxid = oxid;
+ r.out.pdsaOxidBindings = &pdsaOxidBindings;
+ r.out.ipidRemUnknown = oid;
+ r.out.AuthnHint = &AuthnHint;
+ r.out.ServerVersion = &ServerVersion;
+ r.out.hr = &hr;
+ r.out.ifaces = &ifaces;
+
+ status = dcerpc_RemoteActivation_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "RemoteActivation failed");
+ torture_assert_werr_ok(tctx, r.out.result, "RemoteActivation failed");
+ torture_assert_hresult_ok(tctx, *r.out.hr, "RemoteActivation failed");
+ torture_assert_hresult_ok(tctx, r.out.results[0], "RemoteActivation failed");
+
+ return true;
+}
+
+static bool test_SimplePing(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct SimplePing r;
+ NTSTATUS status;
+ uint64_t setid;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.SetId = &setid;
+
+ status = dcerpc_SimplePing_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ status = dcerpc_ServerAlive_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct DUALSTRINGARRAY *ppdsaOxidBindings;
+ struct GUID pipidRemUnknown;
+ uint32_t pAuthnHint;
+
+ if (!test_RemoteActivation(tctx, &oxid, &oid))
+ return false;
+
+ r.in.pOxid = oxid;
+ r.in.cRequestedProtseqs = 2;
+ r.in.arRequestedProtseqs = protseq;
+ r.out.ppdsaOxidBindings = &ppdsaOxidBindings;
+ r.out.pipidRemUnknown = &pipidRemUnknown;
+ r.out.pAuthnHint = &pAuthnHint;
+
+ status = dcerpc_ResolveOxid_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct DUALSTRINGARRAY *pdsaOxidBindings;
+ struct GUID ipidRemUnknown;
+ uint32_t AuthnHint;
+ struct COMVERSION ComVersion;
+
+ if (!test_RemoteActivation(tctx, &oxid, &oid))
+ return false;
+
+ r.in.pOxid = oxid;
+ r.in.cRequestedProtseqs = 2;
+ r.in.arRequestedProtseqs = protseq;
+ r.out.pdsaOxidBindings = &pdsaOxidBindings;
+ r.out.ipidRemUnknown = &ipidRemUnknown;
+ r.out.AuthnHint = &AuthnHint;
+ r.out.ComVersion = &ComVersion;
+
+ status = dcerpc_ResolveOxid2_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct COMINFO info;
+ struct DUALSTRINGARRAY *dualstring;
+ uint8_t pReserved;
+
+ r.out.info = &info;
+ r.out.dualstring = &dualstring;
+ r.out.pReserved = &pReserved;
+
+ status = dcerpc_ServerAlive2_r(b, 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 0000000..9e9642e
--- /dev/null
+++ b/source4/torture/rpc/remact.c
@@ -0,0 +1,104 @@
+/*
+ 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 "librpc/gen_ndr/ndr_remact_c.h"
+#include "librpc/gen_ndr/ndr_epmapper_c.h"
+#include "torture/rpc/torture_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 };
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct ORPCTHAT that;
+ uint64_t pOxid;
+ struct DUALSTRINGARRAY *pdsaOxidBindings;
+ struct GUID ipidRemUnknown;
+ uint32_t AuthnHint;
+ struct COMVERSION ServerVersion;
+ HRESULT hr;
+ struct MInterfacePointer *ifaces;
+
+ ZERO_STRUCT(r);
+
+ r.in.this_object.version.MajorVersion = 5;
+ r.in.this_object.version.MinorVersion = 1;
+ r.in.this_object.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;
+
+ r.out.that = &that;
+ r.out.pOxid = &pOxid;
+ r.out.pdsaOxidBindings = &pdsaOxidBindings;
+ r.out.ipidRemUnknown = &ipidRemUnknown;
+ r.out.AuthnHint = &AuthnHint;
+ r.out.ServerVersion = &ServerVersion;
+ r.out.hr = &hr;
+ r.out.ifaces = &ifaces;
+
+ status = dcerpc_RemoteActivation_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "RemoteActivation");
+
+ torture_assert_werr_ok(tctx, r.out.result, "RemoteActivation");
+
+ torture_assert_hresult_ok(tctx, *r.out.hr, "RemoteActivation");
+
+ torture_assert_hresult_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_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "RemoteActivation(GetClassObject)");
+
+ torture_assert_werr_ok(tctx, r.out.result, "RemoteActivation(GetClassObject)");
+
+ torture_assert_hresult_ok(tctx, *r.out.hr, "RemoteActivation(GetClassObject)");
+
+ torture_assert_hresult_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 0000000..c306006
--- /dev/null
+++ b/source4/torture/rpc/remote_pac.c
@@ -0,0 +1,1372 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for netlogon PAC operations
+
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2012
+
+ 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/auth.h"
+#include "auth/auth_sam_reply.h"
+#include "auth/gensec/gensec.h"
+#include "system/kerberos.h"
+#include "auth/kerberos/kerberos.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "lib/cmdline/cmdline.h"
+#include "torture/rpc/torture_rpc.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "param/param.h"
+#include <ldb.h>
+#include "ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+
+#define TEST_MACHINE_NAME_BDC "torturepacbdc"
+#define TEST_MACHINE_NAME_WKSTA "torturepacwksta"
+#define TEST_MACHINE_NAME_S4U2SELF_BDC "tests4u2selfbdc"
+#define TEST_MACHINE_NAME_S4U2SELF_WKSTA "tests4u2selfwk"
+#define TEST_MACHINE_NAME_S4U2PROXY_WKSTA "tests4u2proxywk"
+
+struct pac_data {
+ DATA_BLOB pac_blob;
+ struct PAC_SIGNATURE_DATA *pac_srv_sig;
+ struct PAC_SIGNATURE_DATA *pac_kdc_sig;
+};
+
+/* A helper function which avoids touching the local databases to
+ * generate the session info, as we just want to verify the PAC
+ * details, not the full local token */
+static NTSTATUS test_generate_session_info_pac(struct auth4_context *auth_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ DATA_BLOB *pac_blob,
+ const char *principal_name,
+ const struct tsocket_address *remote_address,
+ uint32_t session_info_flags,
+ struct auth_session_info **session_info)
+{
+ NTSTATUS nt_status;
+ struct auth_user_info_dc *user_info_dc;
+ TALLOC_CTX *tmp_ctx;
+ struct pac_data *pac_data;
+
+ if (pac_blob == NULL) {
+ DBG_ERR("pac_blob missing\n");
+ return NT_STATUS_NO_IMPERSONATION_TOKEN;
+ }
+
+ tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context");
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ auth_ctx->private_data = pac_data = talloc_zero(auth_ctx, struct pac_data);
+
+ pac_data->pac_blob = data_blob_dup_talloc(pac_data, *pac_blob);
+ if (pac_data->pac_blob.length != pac_blob->length) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pac_data->pac_srv_sig = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA);
+ if (!pac_data->pac_srv_sig) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ pac_data->pac_kdc_sig = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA);
+ if (!pac_data->pac_kdc_sig) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
+ *pac_blob,
+ smb_krb5_context->krb5_context,
+ &user_info_dc,
+ pac_data->pac_srv_sig,
+ pac_data->pac_kdc_sig);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ talloc_steal(pac_data, pac_data->pac_srv_sig);
+ talloc_steal(pac_data, pac_data->pac_kdc_sig);
+
+ if (user_info_dc->info->authenticated) {
+ session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
+ }
+
+ session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
+ nt_status = auth_generate_session_info(mem_ctx,
+ NULL,
+ NULL,
+ user_info_dc, session_info_flags,
+ session_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+ return nt_status;
+ }
+
+ talloc_free(tmp_ctx);
+ return nt_status;
+}
+
+/* Check to see if we can pass the PAC across to the NETLOGON server for validation */
+
+static const struct PAC_BUFFER *get_pac_buffer(const struct PAC_DATA *pac_data,
+ enum PAC_TYPE type)
+{
+ const struct PAC_BUFFER *pac_buf = NULL;
+ uint32_t i;
+
+ for (i = 0; i < pac_data->num_buffers; ++i) {
+ pac_buf = &pac_data->buffers[i];
+
+ if (pac_buf->type == type) {
+ break;
+ }
+ }
+
+ return pac_buf;
+}
+
+/* Also happens to be a really good one-step verfication of our Kerberos stack */
+
+static bool netlogon_validate_pac(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *server_creds,
+ enum netr_SchannelType secure_channel_type,
+ const char *test_machine_name,
+ uint32_t negotiate_flags,
+ struct pac_data *pac_data,
+ struct auth_session_info *session_info);
+
+static bool test_PACVerify(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials,
+ enum netr_SchannelType secure_channel_type,
+ const char *test_machine_name,
+ uint32_t negotiate_flags)
+{
+ NTSTATUS status;
+ bool ok;
+ const char *pkinit_ccache = torture_setting_string(tctx, "pkinit_ccache", NULL);
+ bool pkinit_in_use = pkinit_ccache != NULL;
+ bool expect_pac_upn_dns_info = torture_setting_bool(tctx, "expect_pac_upn_dns_info", true);
+ size_t num_pac_buffers;
+ struct gensec_security *gensec_client_context;
+ struct gensec_security *gensec_server_context;
+ struct cli_credentials *client_creds;
+ struct cli_credentials *server_creds;
+
+ DATA_BLOB client_to_server, server_to_client;
+ struct PAC_DATA pac_data_struct;
+ enum ndr_err_code ndr_err;
+
+ struct auth4_context *auth_context;
+ struct auth_session_info *session_info;
+ struct pac_data *pac_data;
+ const struct PAC_BUFFER *pac_buf = NULL;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+ torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed");
+
+ torture_comment(tctx,
+ "Testing PAC Verify (secure_channel_type: %d, machine: %s, negotiate_flags: 0x%08x\n",
+ secure_channel_type, test_machine_name, negotiate_flags);
+
+ if (pkinit_in_use) {
+ struct cli_credentials *tmp_creds = NULL;
+ const char *error_string = NULL;
+ int rc;
+
+ torture_comment(tctx,
+ "Using pkinit_ccache=%s\n",
+ pkinit_ccache);
+
+ tmp_creds = cli_credentials_init(tctx);
+ torture_assert(tctx, tmp_creds, "Failed to create credentials");
+
+ rc = cli_credentials_set_ccache(tmp_creds,
+ tctx->lp_ctx,
+ pkinit_ccache,
+ CRED_SPECIFIED,
+ &error_string);
+ torture_assert_int_equal(tctx,
+ rc,
+ 0,
+ "cli_credentials_set_ccache failed");
+ cli_credentials_set_kerberos_state(tmp_creds,
+ CRED_USE_KERBEROS_REQUIRED,
+ CRED_SPECIFIED);
+
+ /*
+ * Copy the credentials in order to use a different MEMORY krb5
+ * ccache for each client/server setup. The MEMORY cache
+ * identifier is a pointer to the creds container. If we copy
+ * it the pointer changes and we will get a new clean memory
+ * cache.
+ */
+ client_creds =
+ cli_credentials_shallow_copy(tmp_ctx, tmp_creds);
+ torture_assert(tctx,
+ client_creds,
+ "Failed to copy of credentials");
+ } else {
+ /*
+ * Copy the credentials in order to use a different MEMORY krb5
+ * ccache for each client/server setup. The MEMORY cache
+ * identifier is a pointer to the creds container. If we copy
+ * it the pointer changes and we will get a new clean memory
+ * cache.
+ */
+ client_creds =
+ cli_credentials_shallow_copy(tmp_ctx,
+ samba_cmdline_get_creds());
+ torture_assert(tctx,
+ client_creds,
+ "Failed to copy of credentials");
+ cli_credentials_invalidate_ccache(client_creds, CRED_SPECIFIED);
+ }
+
+
+ server_creds = cli_credentials_shallow_copy(tmp_ctx,
+ credentials);
+ torture_assert(tctx, server_creds, "Failed to copy of credentials");
+
+ auth_context = talloc_zero(tmp_ctx, struct auth4_context);
+ torture_assert(tctx, auth_context != NULL, "talloc_new() failed");
+
+ auth_context->generate_session_info_pac = test_generate_session_info_pac;
+
+ status = gensec_client_start(tctx, &gensec_client_context,
+ lpcfg_gensec_settings(tctx, 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, client_creds);
+ 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,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx),
+ auth_context, &gensec_server_context);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed");
+
+ status = gensec_set_credentials(gensec_server_context, server_creds);
+ 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");
+ }
+
+ 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 (NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ } while (1);
+
+ /* Extract the PAC using Samba's code */
+
+ status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
+
+ pac_data = talloc_get_type(auth_context->private_data, struct pac_data);
+
+ torture_assert(tctx, pac_data != NULL, "gensec_update failed to fill in pac_data in auth_context");
+ torture_assert(tctx, pac_data->pac_srv_sig != NULL, "pac_srv_sig not present");
+ torture_assert(tctx, pac_data->pac_kdc_sig != NULL, "pac_kdc_sig not present");
+
+ ndr_err = ndr_pull_struct_blob(&pac_data->pac_blob, tmp_ctx, &pac_data_struct,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+ torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed");
+
+ num_pac_buffers = 6;
+ if (expect_pac_upn_dns_info) {
+ num_pac_buffers += 1;
+ }
+ if (pkinit_in_use) {
+ num_pac_buffers += 1;
+ }
+
+ torture_assert_int_equal(tctx, pac_data_struct.version, 0, "version");
+ torture_assert_int_equal(tctx, pac_data_struct.num_buffers, num_pac_buffers, "num_buffers");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_LOGON_INFO);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_LOGON_INFO");
+ torture_assert(tctx,
+ pac_buf->info != NULL,
+ "PAC_TYPE_LOGON_INFO info");
+
+ if (pkinit_in_use) {
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_CREDENTIAL_INFO);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_CREDENTIAL_INFO");
+ torture_assert(tctx,
+ pac_buf->info != NULL,
+ "PAC_TYPE_CREDENTIAL_INFO info");
+ }
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_LOGON_NAME);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_LOGON_NAME");
+ torture_assert(tctx,
+ pac_buf->info != NULL,
+ "PAC_TYPE_LOGON_NAME info");
+
+ if (expect_pac_upn_dns_info) {
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_UPN_DNS_INFO);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_UPN_DNS_INFO");
+ torture_assert(tctx,
+ pac_buf->info != NULL,
+ "PAC_TYPE_UPN_DNS_INFO info");
+ }
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_SRV_CHECKSUM);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_SRV_CHECKSUM");
+ torture_assert(tctx,
+ pac_buf->info != NULL,
+ "PAC_TYPE_SRV_CHECKSUM info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_KDC_CHECKSUM);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_KDC_CHECKSUM");
+ torture_assert(tctx,
+ pac_buf->info != NULL,
+ "PAC_TYPE_KDC_CHECKSUM info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_TICKET_CHECKSUM);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_TICKET_CHECKSUM");
+ torture_assert(tctx,
+ pac_buf->info != NULL,
+ "PAC_TYPE_TICKET_CHECKSUM info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_FULL_CHECKSUM);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_FULL_CHECKSUM");
+ torture_assert(tctx,
+ pac_buf->info != NULL,
+ "PAC_TYPE_FULL_CHECKSUM info");
+
+ ok = netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name,
+ negotiate_flags, pac_data, session_info);
+
+ talloc_free(tmp_ctx);
+
+ return ok;
+}
+
+static bool netlogon_validate_pac(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *server_creds,
+ enum netr_SchannelType secure_channel_type,
+ const char *test_machine_name,
+ uint32_t negotiate_flags,
+ struct pac_data *pac_data,
+ struct auth_session_info *session_info)
+{
+ struct PAC_Validate pac_wrapped_struct;
+ struct netlogon_creds_CredentialState *creds = NULL;
+ struct netr_Authenticator return_authenticator;
+ struct netr_Authenticator auth, auth2;
+ struct netr_GenericInfo generic;
+ struct netr_LogonSamLogon r;
+ union netr_Validation validation;
+ union netr_LogonLevel logon;
+ uint8_t authoritative;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB payload, pac_wrapped;
+
+ if (!test_SetupCredentials2(p1, tctx, negotiate_flags,
+ server_creds, secure_channel_type,
+ &creds)) {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(p1, tctx, server_creds, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ pac_wrapped_struct.ChecksumLength = pac_data->pac_srv_sig->signature.length;
+ pac_wrapped_struct.SignatureType = pac_data->pac_kdc_sig->type;
+ pac_wrapped_struct.SignatureLength = pac_data->pac_kdc_sig->signature.length;
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ pac_data->pac_srv_sig->signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ pac_data->pac_kdc_sig->signature.data,
+ pac_wrapped_struct.SignatureLength);
+
+ ndr_err = ndr_push_struct_blob(&pac_wrapped, tctx, &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");
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds, pac_wrapped.data, pac_wrapped.length);
+ } else {
+ netlogon_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 = 0;
+ generic.identity_info.domain_name.string = session_info->info->domain_name;
+ generic.identity_info.account_name.string = session_info->info->account_name;
+ generic.identity_info.workstation.string = test_machine_name;
+
+ generic.package_name.string = "Kerberos";
+
+ logon.generic = &generic;
+
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon = &logon;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(server_creds);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+ r.out.return_authenticator = &return_authenticator;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed");
+
+ /* This will break the signature nicely (even in the crypto wrapping), check we get a logon failure */
+ generic.data[generic.length-1]++;
+
+ logon.generic = &generic;
+
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon = &logon;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(server_creds);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed");
+
+ torture_assert(tctx, netlogon_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--;
+
+ logon.generic = &generic;
+
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon = &logon;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(server_creds);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds,
+ &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ pac_wrapped_struct.ChecksumLength = pac_data->pac_srv_sig->signature.length;
+ pac_wrapped_struct.SignatureType = pac_data->pac_kdc_sig->type;
+
+ /* Break the SignatureType */
+ pac_wrapped_struct.SignatureType++;
+
+ pac_wrapped_struct.SignatureLength = pac_data->pac_kdc_sig->signature.length;
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ pac_data->pac_srv_sig->signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ pac_data->pac_kdc_sig->signature.data,
+ pac_wrapped_struct.SignatureLength);
+
+ ndr_err = ndr_push_struct_blob(&pac_wrapped, tctx, &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");
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds, pac_wrapped.data, pac_wrapped.length);
+ } else {
+ netlogon_creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length);
+ }
+
+ generic.length = pac_wrapped.length;
+ generic.data = pac_wrapped.data;
+
+ logon.generic = &generic;
+
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon = &logon;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(server_creds);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_LOGON_FAILURE, "LogonSamLogon failed");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ pac_wrapped_struct.ChecksumLength = pac_data->pac_srv_sig->signature.length;
+ pac_wrapped_struct.SignatureType = pac_data->pac_kdc_sig->type;
+ pac_wrapped_struct.SignatureLength = pac_data->pac_kdc_sig->signature.length;
+
+ pac_wrapped_struct.ChecksumAndSignature = payload
+ = data_blob_talloc(tctx, NULL,
+ pac_wrapped_struct.ChecksumLength
+ + pac_wrapped_struct.SignatureLength);
+ memcpy(&payload.data[0],
+ pac_data->pac_srv_sig->signature.data,
+ pac_wrapped_struct.ChecksumLength);
+ memcpy(&payload.data[pac_wrapped_struct.ChecksumLength],
+ pac_data->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, tctx, &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");
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds, pac_wrapped.data, pac_wrapped.length);
+ } else {
+ netlogon_creds_arcfour_crypt(creds, pac_wrapped.data, pac_wrapped.length);
+ }
+
+ generic.length = pac_wrapped.length;
+ generic.data = pac_wrapped.data;
+
+ logon.generic = &generic;
+
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonGenericInformation;
+ r.in.logon = &logon;
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(server_creds);
+ r.in.validation_level = NetlogonValidationGenericInfo2;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogon_r(b, tctx, &r),
+ "LogonSamLogon failed");
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_INVALID_PARAMETER, "LogonSamLogon failed");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds, &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ return true;
+}
+
+static bool test_PACVerify_bdc_arcfour(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ return test_PACVerify(tctx, p, credentials, SEC_CHAN_BDC,
+ TEST_MACHINE_NAME_BDC,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS);
+}
+
+static bool test_PACVerify_bdc_aes(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ return test_PACVerify(tctx, p, credentials, SEC_CHAN_BDC,
+ TEST_MACHINE_NAME_BDC,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES);
+}
+
+static bool test_PACVerify_workstation_arcfour(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ return test_PACVerify(tctx, p, credentials, SEC_CHAN_WKSTA,
+ TEST_MACHINE_NAME_WKSTA,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS);
+}
+
+static bool test_PACVerify_workstation_aes(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ return test_PACVerify(tctx, p, credentials, SEC_CHAN_WKSTA,
+ TEST_MACHINE_NAME_WKSTA,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES);
+}
+
+#ifdef SAMBA4_USES_HEIMDAL
+static NTSTATUS check_primary_group_in_validation(TALLOC_CTX *mem_ctx,
+ uint16_t validation_level,
+ const union netr_Validation *validation)
+{
+ const struct netr_SamBaseInfo *base = NULL;
+ int i;
+ switch (validation_level) {
+ case 2:
+ if (!validation || !validation->sam2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ base = &validation->sam2->base;
+ break;
+ case 3:
+ if (!validation || !validation->sam3) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ base = &validation->sam3->base;
+ break;
+ case 6:
+ if (!validation || !validation->sam6) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ base = &validation->sam6->base;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ for (i = 0; i < base->groups.count; i++) {
+ if (base->groups.rids[i].rid == base->primary_gid) {
+ return NT_STATUS_OK;
+ }
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/* Check various ways to get the PAC, in particular check the group membership and
+ * other details between the PAC from a normal kinit, S4U2Self and a SamLogon */
+static bool test_S4U2Self(struct torture_context *tctx,
+ struct dcerpc_pipe *p1,
+ struct cli_credentials *credentials,
+ enum netr_SchannelType secure_channel_type,
+ const char *test_machine_name,
+ uint32_t negotiate_flags)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+
+ struct netr_LogonSamLogon r;
+
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative;
+
+ struct netr_Authenticator auth, auth2;
+
+ DATA_BLOB client_to_server, server_to_client;
+
+ struct netlogon_creds_CredentialState *creds;
+ struct gensec_security *gensec_client_context;
+ struct gensec_security *gensec_server_context;
+ struct cli_credentials *client_creds;
+ struct cli_credentials *server_creds;
+
+ struct auth4_context *auth_context;
+ struct auth_session_info *kinit_session_info;
+ struct auth_session_info *s4u2self_session_info;
+ struct auth_user_info_dc *netlogon_user_info_dc;
+
+ struct netr_NetworkInfo ninfo;
+ DATA_BLOB names_blob, chal, lm_resp, nt_resp;
+ size_t i;
+ int flags = CLI_CRED_NTLMv2_AUTH;
+
+ struct dom_sid *builtin_domain;
+
+ struct dom_sid *ai_auth_authority = NULL;
+ struct dom_sid *ai_service = NULL;
+ size_t ai_auth_authority_count = 0;
+ size_t ai_service_count = 0;
+ bool ok;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(tctx);
+
+ torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed");
+
+ torture_comment(tctx,
+ "Testing S4U2SELF (secure_channel_type: %d, machine: %s, negotiate_flags: 0x%08x\n",
+ secure_channel_type, test_machine_name, negotiate_flags);
+
+ /*
+ * Copy the credentials in order to use a different MEMORY krb5 ccache
+ * for each client/server setup. The MEMORY cache identifier is a
+ * pointer to the creds container. If we copy it the pointer changes and
+ * we will get a new clean memory cache.
+ */
+ client_creds = cli_credentials_shallow_copy(tmp_ctx,
+ samba_cmdline_get_creds());
+ torture_assert(tctx, client_creds, "Failed to copy of credentials");
+ /* We use cli_credentials_get_ntlm_response(), so relax krb5 requirements. */
+ cli_credentials_set_kerberos_state(client_creds,
+ CRED_USE_KERBEROS_DESIRED,
+ CRED_SPECIFIED);
+
+ server_creds = cli_credentials_shallow_copy(tmp_ctx,
+ credentials);
+ torture_assert(tctx, server_creds, "Failed to copy of credentials");
+
+ if (!test_SetupCredentials2(p1, tctx, negotiate_flags,
+ server_creds, secure_channel_type,
+ &creds)) {
+ return false;
+ }
+ if (!test_SetupCredentialsPipe(p1, tctx, server_creds, creds,
+ DCERPC_SIGN | DCERPC_SEAL, &p)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ auth_context = talloc_zero(tmp_ctx, struct auth4_context);
+ torture_assert(tctx, auth_context != NULL, "talloc_new() failed");
+
+ auth_context->generate_session_info_pac = test_generate_session_info_pac;
+
+ /* First, do a normal Kerberos connection */
+
+ status = gensec_client_start(tctx, &gensec_client_context,
+ lpcfg_gensec_settings(tctx, 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, client_creds);
+ 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,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx),
+ auth_context, &gensec_server_context);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed");
+
+ status = gensec_set_credentials(gensec_server_context, server_creds);
+ 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");
+ }
+
+ 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 (NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ } while (1);
+
+ /* Extract the PAC using Samba's code */
+
+ status = gensec_session_info(gensec_server_context, gensec_server_context, &kinit_session_info);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
+
+
+ /* Now do the dance with S4U2Self */
+
+ /* Wipe out any existing ccache */
+ cli_credentials_invalidate_ccache(client_creds, CRED_SPECIFIED);
+ cli_credentials_invalidate_ccache(server_creds, CRED_SPECIFIED);
+ cli_credentials_set_impersonate_principal(server_creds,
+ cli_credentials_get_principal(client_creds, tmp_ctx),
+ talloc_asprintf(tmp_ctx, "host/%s", test_machine_name));
+
+ status = gensec_client_start(tctx, &gensec_client_context,
+ lpcfg_gensec_settings(tctx, 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);
+
+ /* We now set the same credentials on both client and server contexts */
+ status = gensec_set_credentials(gensec_client_context, server_creds);
+ 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,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx),
+ auth_context, &gensec_server_context);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed");
+
+ status = gensec_set_credentials(gensec_server_context, server_creds);
+ 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");
+ }
+
+ 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 (NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ } while (1);
+
+ /* Don't pollute the remaining tests with the changed credentials */
+ cli_credentials_invalidate_ccache(server_creds, CRED_SPECIFIED);
+ cli_credentials_set_target_service(server_creds, NULL);
+ cli_credentials_set_impersonate_principal(server_creds, NULL, NULL);
+
+ /* Extract the PAC using Samba's code */
+
+ status = gensec_session_info(gensec_server_context, gensec_server_context, &s4u2self_session_info);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
+
+ cli_credentials_get_ntlm_username_domain(client_creds, tctx,
+ &ninfo.identity_info.account_name.string,
+ &ninfo.identity_info.domain_name.string);
+
+ /* Now try with SamLogon */
+ generate_random_buffer(ninfo.challenge,
+ sizeof(ninfo.challenge));
+ chal = data_blob_const(ninfo.challenge,
+ sizeof(ninfo.challenge));
+
+ names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(server_creds),
+ cli_credentials_get_domain(server_creds));
+
+ status = cli_credentials_get_ntlm_response(client_creds, tctx,
+ &flags,
+ chal,
+ NULL, /* server_timestamp */
+ 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 = 0;
+ ninfo.identity_info.workstation.string = cli_credentials_get_workstation(server_creds);
+
+ logon.network = &ninfo;
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(server_creds);
+ r.in.credential = &auth;
+ r.in.return_authenticator = &auth2;
+ r.in.logon_level = NetlogonNetworkInformation;
+ r.in.logon = &logon;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = 3;
+
+ status = dcerpc_netr_LogonSamLogon_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LogonSamLogon failed");
+
+ torture_assert(tctx, netlogon_creds_client_check(creds,
+ &r.out.return_authenticator->cred),
+ "Credential chaining failed");
+
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogon failed");
+
+ status = make_user_info_dc_netlogon_validation(tmp_ctx,
+ ninfo.identity_info.account_name.string,
+ r.in.validation_level,
+ r.out.validation,
+ true, /* This user was authenticated */
+ &netlogon_user_info_dc);
+
+ torture_assert_ntstatus_ok(tctx, status, "make_user_info_dc_netlogon_validation failed");
+
+ /* Check that the primary group is present in validation's RID array */
+ status = check_primary_group_in_validation(tmp_ctx, r.in.validation_level, r.out.validation);
+ torture_assert_ntstatus_ok(tctx, status, "check_primary_group_in_validation failed");
+
+ /* Check that the primary group is not duplicated in user_info_dc SID array */
+ for (i = 2; i < netlogon_user_info_dc->num_sids; i++) {
+ torture_assert(tctx, !dom_sid_equal(&netlogon_user_info_dc->sids[1],
+ &netlogon_user_info_dc->sids[i]),
+ "Duplicate PrimaryGroupId in return SID array");
+ }
+
+ torture_assert_str_equal(tctx, netlogon_user_info_dc->info->account_name == NULL ? "" : netlogon_user_info_dc->info->account_name,
+ kinit_session_info->info->account_name, "Account name differs for kinit-based PAC");
+ torture_assert_str_equal(tctx,netlogon_user_info_dc->info->account_name == NULL ? "" : netlogon_user_info_dc->info->account_name,
+ s4u2self_session_info->info->account_name, "Account name differs for S4U2Self");
+ torture_assert_str_equal(tctx, netlogon_user_info_dc->info->full_name == NULL ? "" : netlogon_user_info_dc->info->full_name, kinit_session_info->info->full_name, "Full name differs for kinit-based PAC");
+ torture_assert_str_equal(tctx, netlogon_user_info_dc->info->full_name == NULL ? "" : netlogon_user_info_dc->info->full_name, s4u2self_session_info->info->full_name, "Full name differs for S4U2Self");
+
+ builtin_domain = dom_sid_parse_talloc(tmp_ctx, SID_BUILTIN);
+
+ /* KRB5 might have an additional sid, the asserted identity */
+ ai_auth_authority = dom_sid_parse_talloc(
+ tmp_ctx,
+ SID_AUTHENTICATION_AUTHORITY_ASSERTED_IDENTITY);
+
+ ai_service = dom_sid_parse_talloc(
+ tmp_ctx,
+ SID_SERVICE_ASSERTED_IDENTITY);
+
+ ai_auth_authority_count = 0;
+ ai_service_count = 0;
+ for (i = 0; i < kinit_session_info->torture->num_dc_sids; i++) {
+ ok = dom_sid_equal(&kinit_session_info->torture->dc_sids[i],
+ ai_auth_authority);
+ if (ok) {
+ ai_auth_authority_count++;
+ }
+
+ ok = dom_sid_equal(&kinit_session_info->torture->dc_sids[i],
+ ai_service);
+ if (ok) {
+ ai_service_count++;
+ }
+ }
+
+ torture_assert_int_equal(tctx, ai_auth_authority_count, 1,
+ "Kinit authority asserted identity should be (1)");
+ torture_assert_int_equal(tctx, ai_service_count, 0,
+ "Kinit service asserted identity should be (0)");
+
+ ai_auth_authority_count = 0;
+ ai_service_count = 0;
+ for (i = 0; i < s4u2self_session_info->torture->num_dc_sids; i++) {
+ ok = dom_sid_equal(&s4u2self_session_info->torture->dc_sids[i],
+ ai_auth_authority);
+ if (ok) {
+ ai_auth_authority_count++;
+ }
+
+ ok = dom_sid_equal(&s4u2self_session_info->torture->dc_sids[i],
+ ai_service);
+ if (ok) {
+ ai_service_count++;
+ }
+ }
+
+ torture_assert_int_equal(tctx, ai_auth_authority_count, 0,
+ "S4U2Self authority asserted identity should be (0)");
+ torture_assert_int_equal(tctx, ai_service_count, 1,
+ "S4U2Self service asserted identity should be (1)");
+
+ torture_assert_int_equal(tctx, netlogon_user_info_dc->num_sids, kinit_session_info->torture->num_dc_sids - 1, "Different numbers of domain groups for kinit-based PAC");
+ torture_assert_int_equal(tctx, netlogon_user_info_dc->num_sids, s4u2self_session_info->torture->num_dc_sids - 1, "Different numbers of domain groups for S4U2Self");
+
+ for (i = 0; i < netlogon_user_info_dc->num_sids; i++) {
+ torture_assert(tctx, dom_sid_equal(&netlogon_user_info_dc->sids[i], &kinit_session_info->torture->dc_sids[i]), "Different domain groups for kinit-based PAC");
+ torture_assert(tctx, dom_sid_equal(&netlogon_user_info_dc->sids[i], &s4u2self_session_info->torture->dc_sids[i]), "Different domain groups for S4U2Self");
+ torture_assert(tctx, !dom_sid_in_domain(builtin_domain, &s4u2self_session_info->torture->dc_sids[i]), "Returned BUILTIN domain in groups for S4U2Self");
+ torture_assert(tctx, !dom_sid_in_domain(builtin_domain, &kinit_session_info->torture->dc_sids[i]), "Returned BUILTIN domain in groups kinit-based PAC");
+ torture_assert(tctx, !dom_sid_in_domain(builtin_domain, &netlogon_user_info_dc->sids[i]), "Returned BUILTIN domian in groups from NETLOGON SamLogon reply");
+ }
+
+ return true;
+}
+
+static bool test_S4U2Self_bdc_arcfour(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ return test_S4U2Self(tctx, p, credentials, SEC_CHAN_BDC,
+ TEST_MACHINE_NAME_S4U2SELF_BDC,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS);
+}
+
+static bool test_S4U2Self_bdc_aes(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ return test_S4U2Self(tctx, p, credentials, SEC_CHAN_BDC,
+ TEST_MACHINE_NAME_S4U2SELF_BDC,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES);
+}
+
+static bool test_S4U2Self_workstation_arcfour(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ return test_S4U2Self(tctx, p, credentials, SEC_CHAN_WKSTA,
+ TEST_MACHINE_NAME_S4U2SELF_WKSTA,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS);
+}
+
+static bool test_S4U2Self_workstation_aes(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials)
+{
+ return test_S4U2Self(tctx, p, credentials, SEC_CHAN_WKSTA,
+ TEST_MACHINE_NAME_S4U2SELF_WKSTA,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES);
+}
+
+static bool test_S4U2Proxy(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials,
+ enum netr_SchannelType secure_channel_type,
+ const char *test_machine_name,
+ uint32_t negotiate_flags)
+{
+ NTSTATUS status;
+ struct gensec_security *gensec_client_context = NULL;
+ struct gensec_security *gensec_server_context = NULL;
+ struct cli_credentials *server_creds = NULL;
+ size_t num_pac_buffers;
+ struct auth4_context *auth_context = NULL;
+ struct auth_session_info *session_info = NULL;
+ struct pac_data *pac_data = NULL;
+ const struct PAC_BUFFER *pac_buf = NULL;
+ char *impersonate_princ = NULL, *self_princ = NULL, *target_princ = NULL;
+ enum ndr_err_code ndr_err;
+ struct PAC_DATA pac_data_struct;
+ struct PAC_CONSTRAINED_DELEGATION *deleg = NULL;
+
+ DATA_BLOB client_to_server, server_to_client;
+
+ auth_context = talloc_zero(tctx, struct auth4_context);
+ torture_assert_not_null(tctx, auth_context, "talloc_new() failed");
+
+ auth_context->generate_session_info_pac = test_generate_session_info_pac;
+
+ torture_comment(tctx,
+ "Testing S4U2Proxy (secure_channel_type: %d, machine: %s, negotiate_flags: 0x%08x\n",
+ secure_channel_type, test_machine_name, negotiate_flags);
+
+ impersonate_princ = cli_credentials_get_principal(samba_cmdline_get_creds(), tctx);
+ torture_assert_not_null(tctx, impersonate_princ, "Failed to get impersonate client name");
+
+ server_creds = cli_credentials_shallow_copy(tctx, credentials);
+ torture_assert_not_null(tctx, server_creds, "Failed to copy of credentials");
+
+ self_princ = talloc_asprintf(tctx, "host/%s", test_machine_name);
+ cli_credentials_invalidate_ccache(server_creds, CRED_SPECIFIED);
+ cli_credentials_set_impersonate_principal(server_creds, impersonate_princ, self_princ);
+
+ /* Trigger S4U2Proxy by setting a target_service different than self_principal */
+ target_princ = talloc_asprintf(tctx, "%s$", test_machine_name);
+ cli_credentials_set_target_service(server_creds, target_princ);
+
+ status = gensec_client_start(tctx, &gensec_client_context,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+ torture_assert_ntstatus_ok(tctx, status, "gensec_client_start (client) failed");
+
+ status = gensec_set_target_principal(gensec_client_context, target_princ);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_set_target_hostname (client) failed");
+
+ /* We now set the same credentials on both client and server contexts */
+ status = gensec_set_credentials(gensec_client_context, server_creds);
+ 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,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx),
+ auth_context, &gensec_server_context);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed");
+
+ status = gensec_set_credentials(gensec_server_context, server_creds);
+ 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, tctx, 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");
+ }
+
+ status = gensec_update(gensec_server_context, tctx, 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 (NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ } while (1);
+
+ /* Extract the PAC using Samba's code */
+
+ status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info);
+ torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
+
+ pac_data = talloc_get_type(auth_context->private_data, struct pac_data);
+
+ torture_assert_not_null(tctx, pac_data, "gensec_update failed to fill in pac_data in auth_context");
+ torture_assert_not_null(tctx, pac_data->pac_srv_sig, "pac_srv_sig not present");
+ torture_assert_not_null(tctx, pac_data->pac_kdc_sig, "pac_kdc_sig not present");
+
+ ndr_err = ndr_pull_struct_blob(&pac_data->pac_blob, tctx, &pac_data_struct,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+ torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed");
+
+ num_pac_buffers = 8;
+
+ torture_assert_int_equal(tctx, pac_data_struct.version, 0, "version");
+ torture_assert_int_equal(tctx, pac_data_struct.num_buffers, num_pac_buffers, "num_buffers");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_LOGON_INFO);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_LOGON_INFO");
+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_LOGON_INFO info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_LOGON_NAME);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_LOGON_NAME");
+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_LOGON_NAME info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_UPN_DNS_INFO);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_UPN_DNS_INFO");
+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_UPN_DNS_INFO info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_SRV_CHECKSUM);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_SRV_CHECKSUM");
+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_SRV_CHECKSUM info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_KDC_CHECKSUM);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_KDC_CHECKSUM");
+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_KDC_CHECKSUM info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_TICKET_CHECKSUM);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_TICKET_CHECKSUM");
+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_TICKET_CHECKSUM info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_FULL_CHECKSUM);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_FULL_CHECKSUM");
+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_FULL_CHECKSUM info");
+
+ pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_CONSTRAINED_DELEGATION);
+ torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_CONSTRAINED_DELEGATION");
+ torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_CONSTRAINED_DELEGATION info");
+
+ deleg = pac_buf->info->constrained_delegation.info;
+ torture_assert_str_equal(tctx, deleg->proxy_target.string, target_princ, "wrong proxy_target");
+ torture_assert_int_equal(tctx, deleg->num_transited_services, 1, "wrong transited_services number");
+ torture_assert_str_equal(tctx, deleg->transited_services[0].string,
+ talloc_asprintf(tctx, "%s@%s", self_princ, cli_credentials_get_realm(credentials)),
+ "wrong transited_services[0]");
+
+ return netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name,
+ negotiate_flags, pac_data, session_info);
+}
+
+static bool setup_constrained_delegation(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct test_join *join_ctx,
+ const char *machine_name)
+{
+ struct samr_SetUserInfo r;
+ union samr_UserInfo user_info;
+ struct dcerpc_pipe *samr_pipe = torture_join_samr_pipe(join_ctx);
+ const char *server_dn_str = NULL;
+ struct ldb_context *sam_ctx = NULL;
+ struct ldb_dn *server_dn = NULL;
+ struct ldb_message *msg = NULL;
+ char *url = NULL;
+ int ret;
+
+ url = talloc_asprintf(tctx, "ldap://%s", dcerpc_server_name(p));
+ sam_ctx = ldb_wrap_connect(tctx, tctx->ev, tctx->lp_ctx, url, NULL, samba_cmdline_get_creds(), 0);
+ torture_assert_not_null(tctx, sam_ctx, "Connection to the SAMDB on DC failed!");
+
+ server_dn_str = samdb_search_string(sam_ctx, tctx, ldb_get_default_basedn(sam_ctx), "distinguishedName",
+ "samaccountname=%s$", machine_name);
+ torture_assert_not_null(tctx, server_dn_str, "samdb_search_string()");
+
+ server_dn = ldb_dn_new(tctx, sam_ctx, server_dn_str);
+ torture_assert_not_null(tctx, server_dn, "ldb_dn_new()");
+
+ msg = ldb_msg_new(tctx);
+ torture_assert_not_null(tctx, msg, "ldb_msg_new()");
+
+ msg->dn = server_dn;
+ ret = ldb_msg_add_string(msg, "msDS-AllowedToDelegateTo", talloc_asprintf(tctx, "%s$", machine_name));
+ torture_assert_int_equal(tctx, ret, 0, "ldb_msg_add_string())");
+
+ ret = ldb_modify(sam_ctx, msg);
+ torture_assert_int_equal(tctx, ret, 0, "ldb_modify()");
+
+ /* Allow forwardable flag in S4U2Self */
+ user_info.info16.acct_flags = ACB_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION | ACB_WSTRUST;
+ r.in.user_handle = torture_join_samr_user_policy(join_ctx);
+ r.in.level = 16;
+ r.in.info = &user_info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(samr_pipe->binding_handle, tctx, &r),
+ "failed to set ACB_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION info account flags");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to set ACB_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION into account flags");
+
+ return true;
+}
+
+static bool test_S4U2Proxy_workstation_arcfour(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials,
+ struct test_join *join_ctx)
+{
+ torture_assert(tctx, setup_constrained_delegation(tctx, p, join_ctx,
+ TEST_MACHINE_NAME_S4U2PROXY_WKSTA),
+ "setup_constrained_delegation() failed");
+ return test_S4U2Proxy(tctx, p, credentials, SEC_CHAN_WKSTA,
+ TEST_MACHINE_NAME_S4U2PROXY_WKSTA,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS);
+}
+
+static bool test_S4U2Proxy_workstation_aes(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *credentials,
+ struct test_join *join_ctx)
+{
+ torture_assert(tctx, setup_constrained_delegation(tctx, p, join_ctx,
+ TEST_MACHINE_NAME_S4U2PROXY_WKSTA),
+ "setup_constrained_delegation() failed");
+ return test_S4U2Proxy(tctx, p, credentials, SEC_CHAN_WKSTA,
+ TEST_MACHINE_NAME_S4U2PROXY_WKSTA,
+ NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES);
+}
+#endif
+
+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_bdc_rpc_iface_tcase(suite, "netr-bdc-arcfour",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_BDC);
+ torture_rpc_tcase_add_test_creds(tcase, "verify-sig-arcfour", test_PACVerify_bdc_arcfour);
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netr-bdc-aes",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_BDC);
+ torture_rpc_tcase_add_test_creds(tcase, "verify-sig-aes", test_PACVerify_bdc_aes);
+
+ tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-arcfour",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_WKSTA);
+ torture_rpc_tcase_add_test_creds(tcase, "verify-sig-arcfour", test_PACVerify_workstation_arcfour);
+
+ tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-aes",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_WKSTA);
+ torture_rpc_tcase_add_test_creds(tcase, "verify-sig-aes", test_PACVerify_workstation_aes);
+
+#ifdef SAMBA4_USES_HEIMDAL
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netr-bdc-arcfour",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2SELF_BDC);
+ torture_rpc_tcase_add_test_creds(tcase, "s4u2self-arcfour", test_S4U2Self_bdc_arcfour);
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "netr-bcd-aes",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2SELF_BDC);
+ torture_rpc_tcase_add_test_creds(tcase, "s4u2self-aes", test_S4U2Self_bdc_aes);
+
+ tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-arcfour",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2SELF_WKSTA);
+ torture_rpc_tcase_add_test_creds(tcase, "s4u2self-arcfour", test_S4U2Self_workstation_arcfour);
+
+ tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-aes",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2SELF_WKSTA);
+ torture_rpc_tcase_add_test_creds(tcase, "s4u2self-aes", test_S4U2Self_workstation_aes);
+
+ tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-arcfour",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2PROXY_WKSTA);
+ torture_rpc_tcase_add_test_join(tcase, "s4u2proxy-arcfour", test_S4U2Proxy_workstation_arcfour);
+
+ tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "netr-mem-aes",
+ &ndr_table_netlogon, TEST_MACHINE_NAME_S4U2PROXY_WKSTA);
+ torture_rpc_tcase_add_test_join(tcase, "s4u2proxy-aes", test_S4U2Proxy_workstation_aes);
+#endif
+ return suite;
+}
diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c
new file mode 100644
index 0000000..773e564
--- /dev/null
+++ b/source4/torture/rpc/rpc.c
@@ -0,0 +1,663 @@
+/*
+ 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 "lib/cmdline/cmdline.h"
+#include "torture/rpc/torture_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)) {
+ torture_comment(tctx,
+ "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;
+ }
+
+ return torture_rpc_connection_with_binding(tctx, binding, p, table);
+}
+
+/**
+ * open a rpc connection to the chosen binding string
+ */
+_PUBLIC_ NTSTATUS torture_rpc_connection_with_binding(struct torture_context *tctx,
+ struct dcerpc_binding *binding,
+ struct dcerpc_pipe **p,
+ const struct ndr_interface_table *table)
+{
+ NTSTATUS status;
+
+ dcerpc_init();
+
+ status = dcerpc_pipe_connect_b(tctx,
+ p, binding, table,
+ samba_cmdline_get_creds(),
+ tctx->ev, tctx->lp_ctx);
+
+ if (NT_STATUS_IS_ERR(status)) {
+ torture_warning(tctx, "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,
+ uint32_t extra_flags)
+{
+ NTSTATUS status;
+ struct dcerpc_binding *binding;
+
+ *p = NULL;
+
+ status = torture_rpc_binding(tctx, &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcerpc_binding_set_transport(binding, transport);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcerpc_binding_set_assoc_group_id(binding, assoc_group_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcerpc_binding_set_flags(binding, extra_flags, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcerpc_pipe_connect_b(tctx, p, binding, table,
+ samba_cmdline_get_creds(),
+ tctx->ev, tctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ *p = NULL;
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static bool torture_rpc_setup_machine_workstation(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 = samba_cmdline_get_creds();
+ tcase_data->join_ctx = torture_join_domain(tctx, tcase->machine_name,
+ ACB_WSTRUST,
+ &tcase_data->credentials);
+ if (tcase_data->join_ctx == NULL)
+ torture_fail(tctx, "Failed to join as WORKSTATION");
+
+ 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 NT_STATUS_IS_OK(status);
+}
+
+static bool torture_rpc_setup_machine_bdc(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 = samba_cmdline_get_creds();
+ 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 NT_STATUS_IS_OK(status);
+}
+
+_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_machine_workstation_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_workstation;
+ tcase->tcase.teardown = torture_rpc_teardown;
+
+ return tcase;
+}
+
+_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_machine_bdc_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_bdc;
+ 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 NT_STATUS_IS_OK(status);
+}
+
+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 = samba_cmdline_get_creds();
+
+ status = torture_rpc_connection(tctx,
+ &(tcase_data->pipe),
+ tcase->table);
+
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ return NT_STATUS_IS_OK(status);
+}
+
+
+
+_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);
+}
+
+static bool torture_rpc_wrap_test_join(struct torture_context *tctx,
+ struct torture_tcase *tcase,
+ struct torture_test *test)
+{
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *, struct cli_credentials *, struct test_join *);
+ 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, tcase_data->join_ctx);
+}
+
+_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_END(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_END(tcase->tcase.tests, test);
+
+ return test;
+}
+
+_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_join(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *,
+ struct cli_credentials *, struct test_join *))
+{
+ 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_join;
+ test->dangerous = false;
+ test->data = NULL;
+ test->fn = fn;
+
+ DLIST_ADD_END(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_END(tcase->tcase.tests, test);
+
+ return test;
+}
+
+static bool torture_rpc_wrap_test_setup(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 *rpc_tcase = talloc_get_type_abort(
+ tctx->active_tcase, struct torture_rpc_tcase);
+ struct torture_rpc_tcase_data *tcase_data = talloc_get_type_abort(
+ tcase->data, struct torture_rpc_tcase_data);
+ void *data = discard_const_p(void, test->data);
+ bool ok;
+
+ ok = rpc_tcase->setup_fn(tctx, tcase_data->pipe, data);
+ if (!ok) {
+ return false;
+ }
+
+ fn = test->fn;
+
+ ok = fn(tctx, tcase_data->pipe, data);
+ if (!ok) {
+ return false;
+ }
+
+ ok = rpc_tcase->teardown_fn(tctx, tcase_data->pipe, data);
+ if (!ok) {
+ return false;
+ }
+
+ return true;
+}
+
+_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_setup(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn)(struct torture_context *,
+ struct dcerpc_pipe *,
+ void *),
+ void *userdata)
+{
+ struct torture_test *test = NULL;
+
+ test = talloc(tcase, struct torture_test);
+
+ test->name = talloc_strdup(test, name);
+ test->description = NULL;
+ test->run = torture_rpc_wrap_test_setup;
+ test->dangerous = false;
+ test->data = userdata;
+ test->fn = fn;
+
+ DLIST_ADD_END(tcase->tcase.tests, test);
+
+ return test;
+}
+
+_PUBLIC_ struct torture_rpc_tcase *torture_suite_add_rpc_setup_tcase(
+ struct torture_suite *suite,
+ const char *name,
+ const struct ndr_interface_table *table,
+ bool (*setup_fn)(struct torture_context *,
+ struct dcerpc_pipe *,
+ void *),
+ bool (*teardown_fn)(struct torture_context *,
+ struct dcerpc_pipe *,
+ void *))
+{
+ struct torture_rpc_tcase *tcase = talloc(
+ suite, struct torture_rpc_tcase);
+
+ torture_suite_init_rpc_tcase(suite, tcase, name, table);
+
+ tcase->setup_fn = setup_fn;
+ tcase->teardown_fn = teardown_fn;
+ tcase->tcase.setup = torture_rpc_setup;
+ tcase->tcase.teardown = torture_rpc_teardown;
+
+ return tcase;
+}
+
+NTSTATUS torture_rpc_init(TALLOC_CTX *ctx)
+{
+ struct torture_suite *suite = torture_suite_create(ctx, "rpc");
+
+ 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_lookup_sids(suite));
+ torture_suite_add_suite(suite, torture_rpc_lsa_lookup_names(suite));
+ torture_suite_add_suite(suite, torture_rpc_lsa_secrets(suite));
+ torture_suite_add_suite(suite, torture_rpc_lsa_trusted_domains(suite));
+ torture_suite_add_suite(suite, torture_rpc_lsa_forest_trust(suite));
+ torture_suite_add_suite(suite, torture_rpc_lsa_privileges(suite));
+ torture_suite_add_suite(suite, torture_rpc_echo(suite));
+ torture_suite_add_suite(suite, torture_rpc_dfs(suite));
+ 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_object_uuid(suite));
+ torture_suite_add_suite(suite, torture_rpc_winreg(suite));
+ torture_suite_add_suite(suite, torture_rpc_spoolss(suite));
+#ifdef WITH_NTVFS_FILESERVER
+ torture_suite_add_suite(suite, torture_rpc_spoolss_notify(suite));
+#endif
+ torture_suite_add_suite(suite, torture_rpc_spoolss_win(suite));
+ torture_suite_add_suite(suite, torture_rpc_spoolss_driver(suite));
+ torture_suite_add_suite(suite, torture_rpc_spoolss_access(suite));
+ torture_suite_add_suite(suite, torture_rpc_iremotewinspool(suite));
+ torture_suite_add_suite(suite, torture_rpc_iremotewinspool_drv(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.default", torture_rpc_samr_passwords);
+ torture_suite_add_suite(suite, torture_rpc_netlogon(suite));
+ torture_suite_add_suite(suite, torture_rpc_netlogon_s3(suite));
+ torture_suite_add_suite(suite, torture_rpc_netlogon_admin(suite));
+ torture_suite_add_suite(suite, torture_rpc_netlogon_zerologon(suite));
+ torture_suite_add_suite(suite, torture_rpc_netlogon_crypto_fips(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_simple_test(suite, "schannel_anon_setpw", torture_rpc_schannel_anon_setpw);
+ 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_samr_handletype(suite));
+ torture_suite_add_suite(suite, torture_rpc_samr_workstation_auth(suite));
+ torture_suite_add_suite(suite, torture_rpc_samr_passwords_pwdlastset(suite));
+ torture_suite_add_suite(suite, torture_rpc_samr_passwords_badpwdcount(suite));
+ torture_suite_add_suite(suite, torture_rpc_samr_passwords_lockout(suite));
+ torture_suite_add_suite(suite, torture_rpc_samr_passwords_validate(suite));
+ torture_suite_add_suite(suite, torture_rpc_samr_user_privileges(suite));
+ torture_suite_add_suite(suite, torture_rpc_samr_large_dc(suite));
+ torture_suite_add_suite(suite, torture_rpc_samr_priv(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, "countcalls", torture_rpc_countcalls);
+ torture_suite_add_simple_test(suite, "authcontext", torture_bind_authcontext);
+ torture_suite_add_suite(suite, torture_rpc_samba3(suite));
+ torture_rpc_drsuapi_tcase(suite);
+ torture_rpc_drsuapi_w2k8_tcase(suite);
+ torture_rpc_drsuapi_cracknames_tcase(suite);
+ torture_suite_add_suite(suite, torture_rpc_dssetup(suite));
+ torture_suite_add_suite(suite, torture_rpc_browser(suite));
+ torture_suite_add_simple_test(suite, "altercontext", torture_rpc_alter_context);
+ torture_suite_add_simple_test(suite, "join", torture_rpc_join);
+ torture_drs_rpc_dsgetinfo_tcase(suite);
+ torture_suite_add_simple_test(suite, "bench-rpc", torture_bench_rpc);
+ torture_suite_add_simple_test(suite, "asyncbind", torture_async_bind);
+ torture_suite_add_suite(suite, torture_rpc_ntsvcs(suite));
+ torture_suite_add_suite(suite, torture_rpc_bind(suite));
+#ifdef AD_DC_BUILD_IS_ENABLED
+ torture_suite_add_suite(suite, torture_rpc_backupkey(suite));
+#endif
+ torture_suite_add_suite(suite, torture_rpc_fsrvp(suite));
+ torture_suite_add_suite(suite, torture_rpc_clusapi(suite));
+ torture_suite_add_suite(suite, torture_rpc_witness(suite));
+ torture_suite_add_suite(suite, torture_rpc_mdssvc(suite));
+
+ suite->description = talloc_strdup(suite, "DCE/RPC protocol and interface tests");
+
+ torture_register_suite(ctx, suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/rpc/samba3rpc.c b/source4/torture/rpc/samba3rpc.c
new file mode 100644
index 0000000..36eabdc
--- /dev/null
+++ b/source4/torture/rpc/samba3rpc.c
@@ -0,0 +1,4729 @@
+/*
+ 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 "torture/util.h"
+#include "libcli/rap/rap.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_netlogon_c.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+#include "librpc/gen_ndr/ndr_wkssvc_c.h"
+#include "librpc/gen_ndr/ndr_svcctl_c.h"
+#include "lib/cmdline/cmdline.h"
+#include "torture/rpc/torture_rpc.h"
+#include "libcli/libcli.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "lib/registry/registry.h"
+#include "libcli/resolve/resolve.h"
+#include "torture/ndr/ndr.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "libcli/smb/smbXcli_base.h"
+#include "source3/rpc_client/init_samr.h"
+
+/*
+ * open pipe and bind, given an IPC$ context
+ */
+
+static NTSTATUS pipe_bind_smb(struct torture_context *tctx,
+ TALLOC_CTX *mem_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, tctx->ev))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_pipe_open_smb(result, tree, pipe_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "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)) {
+ torture_comment(tctx, "dcerpc_bind_auth_none failed: %s\n", nt_errstr(status));
+ talloc_free(result);
+ return status;
+ }
+
+ *p = result;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pipe_bind_smb2(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct smb2_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, tctx->ev))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_pipe_open_smb2(result, tree, pipe_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_pipe_open_smb2 failed: %s\n",
+ nt_errstr(status));
+ talloc_free(result);
+ return status;
+ }
+
+ status = dcerpc_bind_auth_none(result, iface);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_bind_auth_none failed: %s\n", nt_errstr(status));
+ talloc_free(result);
+ return status;
+ }
+
+ *p = result;
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pipe_bind_smb_auth(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct smbcli_tree *tree,
+ struct cli_credentials *creds,
+ uint8_t auth_type,
+ uint8_t auth_level,
+ 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, tctx->ev))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_pipe_open_smb(result, tree, pipe_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_pipe_open_smb failed: %s\n",
+ nt_errstr(status));
+ talloc_free(result);
+ return status;
+ }
+
+ status = dcerpc_bind_auth(result, iface, creds,
+ lpcfg_gensec_settings(tctx->lp_ctx, tctx->lp_ctx),
+ auth_type, auth_level, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_bind_auth failed: %s\n", nt_errstr(status));
+ talloc_free(result);
+ return status;
+ }
+
+ *p = result;
+ return NT_STATUS_OK;
+}
+
+/*
+ * 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;
+ uint16_t tmp_vuid;
+ struct smbcli_session *session2;
+ struct smbcli_state *cli;
+ struct dcerpc_pipe *lsa_pipe;
+ struct dcerpc_binding_handle *lsa_handle;
+ struct cli_credentials *anon_creds;
+ struct smb_composite_sesssetup setup;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+
+ mem_ctx = talloc_init("torture_bind_authcontext");
+
+ if (mem_ctx == NULL) {
+ torture_comment(torture, "talloc_init failed\n");
+ return false;
+ }
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+ lpcfg_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lpcfg_socket_options(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(torture, "smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = pipe_bind_smb(torture, mem_ctx, cli->tree, "\\lsarpc",
+ &ndr_table_lsarpc, &lsa_pipe);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "pipe_bind_smb failed");
+ lsa_handle = lsa_pipe->binding_handle;
+
+ 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_r(lsa_handle, mem_ctx, &openpolicy);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(torture, "dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(openpolicy.out.result)) {
+ torture_comment(torture, "dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ nt_errstr(openpolicy.out.result));
+ goto done;
+ }
+
+ close_handle.in.handle = &handle;
+ close_handle.out.handle = &handle;
+
+ status = dcerpc_lsa_Close_r(lsa_handle, mem_ctx, &close_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(torture, "dcerpc_lsa_Close failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(close_handle.out.result)) {
+ torture_comment(torture, "dcerpc_lsa_Close failed: %s\n",
+ nt_errstr(close_handle.out.result));
+ goto done;
+ }
+
+ session2 = smbcli_session_init(cli->transport, mem_ctx, false, session_options);
+ if (session2 == NULL) {
+ torture_comment(torture, "smbcli_session_init failed\n");
+ goto done;
+ }
+
+ if (!(anon_creds = cli_credentials_init_anon(mem_ctx))) {
+ torture_comment(torture, "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;
+ setup.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx);
+
+ status = smb_composite_sesssetup(session2, &setup);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(torture, "anon session setup failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ session2->vuid = setup.out.vuid;
+
+ tmp = cli->tree->session;
+ tmp_vuid = smb1cli_session_current_id(tmp->smbXcli);
+ smb1cli_session_set_id(tmp->smbXcli, session2->vuid);
+ cli->tree->session = session2;
+
+ status = dcerpc_lsa_OpenPolicy2_r(lsa_handle, mem_ctx, &openpolicy);
+
+ torture_assert(torture, smbXcli_conn_is_connected(cli->transport->conn),
+ "smb still connected");
+ torture_assert(torture, !dcerpc_binding_handle_is_connected(lsa_handle),
+ "dcerpc disonnected");
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_HANDLE)) {
+ torture_comment(torture, "dcerpc_lsa_OpenPolicy2 with wrong vuid gave %s, "
+ "expected NT_STATUS_CONNECTION_DISCONNECTED\n",
+ nt_errstr(status));
+ status = NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_IO_DEVICE_ERROR)) {
+ torture_comment(torture, "dcerpc_lsa_OpenPolicy2 with wrong vuid gave %s, "
+ "expected NT_STATUS_CONNECTION_DISCONNECTED\n",
+ nt_errstr(status));
+ status = NT_STATUS_CONNECTION_DISCONNECTED;
+ }
+
+ torture_assert_ntstatus_equal(torture, status, NT_STATUS_CONNECTION_DISCONNECTED,
+ "lsa connection disconnected");
+
+ smb1cli_session_set_id(tmp->smbXcli, tmp_vuid);
+ cli->tree->session = tmp;
+ talloc_free(lsa_pipe);
+ lsa_pipe = NULL;
+
+ ret = true;
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Bind to lsa using a specific auth method
+ */
+
+static bool bindtest(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct cli_credentials *credentials,
+ uint8_t auth_type, uint8_t auth_level)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = false;
+ NTSTATUS status;
+
+ struct dcerpc_pipe *lsa_pipe;
+ struct dcerpc_binding_handle *lsa_handle;
+ struct lsa_ObjectAttribute objectattr;
+ struct lsa_OpenPolicy2 openpolicy;
+ struct lsa_QueryInfoPolicy query;
+ union lsa_PolicyInformation *info = NULL;
+ struct policy_handle handle;
+ struct lsa_Close close_handle;
+
+ if ((mem_ctx = talloc_init("bindtest")) == NULL) {
+ torture_comment(tctx, "talloc_init failed\n");
+ return false;
+ }
+
+ status = pipe_bind_smb_auth(tctx, mem_ctx, cli->tree,
+ credentials, auth_type, auth_level,
+ "\\lsarpc", &ndr_table_lsarpc, &lsa_pipe);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "pipe_bind_smb_auth failed");
+ lsa_handle = lsa_pipe->binding_handle;
+
+ 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_r(lsa_handle, mem_ctx, &openpolicy);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(openpolicy.out.result)) {
+ torture_comment(tctx, "dcerpc_lsa_OpenPolicy2 failed: %s\n",
+ nt_errstr(openpolicy.out.result));
+ goto done;
+ }
+
+ query.in.handle = &handle;
+ query.in.level = LSA_POLICY_INFO_DOMAIN;
+ query.out.info = &info;
+
+ status = dcerpc_lsa_QueryInfoPolicy_r(lsa_handle, mem_ctx, &query);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_lsa_QueryInfoPolicy failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(query.out.result)) {
+ torture_comment(tctx, "dcerpc_lsa_QueryInfoPolicy failed: %s\n",
+ nt_errstr(query.out.result));
+ goto done;
+ }
+
+ close_handle.in.handle = &handle;
+ close_handle.out.handle = &handle;
+
+ status = dcerpc_lsa_Close_r(lsa_handle, mem_ctx, &close_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_lsa_Close failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(close_handle.out.result)) {
+ torture_comment(tctx, "dcerpc_lsa_Close failed: %s\n",
+ nt_errstr(close_handle.out.result));
+ goto done;
+ }
+
+
+ ret = true;
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * test authenticated RPC binds with the variants Samba3 does support
+ */
+
+static 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;
+ struct smbcli_session_options session_options;
+
+ mem_ctx = talloc_init("torture_bind_authcontext");
+
+ if (mem_ctx == NULL) {
+ torture_comment(torture, "talloc_init failed\n");
+ return false;
+ }
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+ lpcfg_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lpcfg_socket_options(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(torture, "smbcli_full_connection failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = true;
+
+ ret &= bindtest(torture, cli, samba_cmdline_get_creds(),
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_INTEGRITY);
+ ret &= bindtest(torture, cli, samba_cmdline_get_creds(),
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_PRIVACY);
+ ret &= bindtest(torture, cli, samba_cmdline_get_creds(),
+ DCERPC_AUTH_TYPE_SPNEGO,
+ DCERPC_AUTH_LEVEL_INTEGRITY);
+ ret &= bindtest(torture, cli, samba_cmdline_get_creds(),
+ 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 bool get_usr_handle(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ TALLOC_CTX *mem_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_p)
+{
+ struct dcerpc_pipe *samr_pipe;
+ struct dcerpc_binding_handle *samr_handle;
+ 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;
+ uint32_t num_entries = 0;
+ struct samr_SamArray *sam = NULL;
+ struct samr_LookupDomain l;
+ struct dom_sid2 *sid = NULL;
+ 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;
+
+ if (admin_creds != NULL) {
+ status = pipe_bind_smb_auth(tctx, mem_ctx, cli->tree,
+ admin_creds, auth_type, auth_level,
+ "\\samr", &ndr_table_samr, &samr_pipe);
+ torture_assert_ntstatus_ok(tctx, status, "pipe_bind_smb_auth failed");
+ } else {
+ /* We must have an authenticated SMB connection */
+ status = pipe_bind_smb(tctx, mem_ctx, cli->tree,
+ "\\samr", &ndr_table_samr, &samr_pipe);
+ torture_assert_ntstatus_ok(tctx, status, "pipe_bind_smb_auth failed");
+ }
+#if 0
+ samr_pipe->conn->flags |= DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT;
+#endif
+ samr_handle = samr_pipe->binding_handle;
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_Connect2_r(samr_handle, mem_ctx, &conn),
+ "samr_Connect2 failed");
+ torture_assert_ntstatus_ok(tctx, conn.out.result,
+ "samr_Connect2 failed");
+
+ 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;
+ enumdom.out.num_entries = &num_entries;
+ enumdom.out.sam = &sam;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_EnumDomains_r(samr_handle, mem_ctx, &enumdom),
+ "samr_EnumDomains failed");
+ torture_assert_ntstatus_ok(tctx, enumdom.out.result,
+ "samr_EnumDomains failed");
+
+ torture_assert_int_equal(tctx, *enumdom.out.num_entries, 2,
+ "samr_EnumDomains returned unexpected num_entries");
+
+ dom_idx = strequal(sam->entries[0].name.string,
+ "builtin") ? 1:0;
+
+ l.in.connect_handle = &conn_handle;
+ domain_name.string = sam->entries[dom_idx].name.string;
+ *domain = talloc_strdup(mem_ctx, domain_name.string);
+ l.in.domain_name = &domain_name;
+ l.out.sid = &sid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_LookupDomain_r(samr_handle, mem_ctx, &l),
+ "samr_LookupDomain failed");
+ torture_assert_ntstatus_ok(tctx, l.out.result,
+ "samr_LookupDomain failed");
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_OpenDomain_r(samr_handle, mem_ctx, &o),
+ "samr_OpenDomain failed");
+ torture_assert_ntstatus_ok(tctx, o.out.result,
+ "samr_OpenDomain failed");
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_CreateUser2_r(samr_handle, mem_ctx, &c),
+ "samr_CreateUser2 failed");
+
+ if (NT_STATUS_EQUAL(c.out.result, NT_STATUS_USER_EXISTS)) {
+ struct samr_LookupNames ln;
+ struct samr_OpenUser ou;
+ struct samr_Ids rids, types;
+
+ ln.in.domain_handle = &domain_handle;
+ ln.in.num_names = 1;
+ ln.in.names = &user_name;
+ ln.out.rids = &rids;
+ ln.out.types = &types;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_LookupNames_r(samr_handle, mem_ctx, &ln),
+ "samr_LookupNames failed");
+ torture_assert_ntstatus_ok(tctx, ln.out.result,
+ "samr_LookupNames failed");
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_OpenUser_r(samr_handle, mem_ctx, &ou),
+ "samr_OpenUser failed");
+ status = ou.out.result;
+ } else {
+ status = c.out.result;
+ }
+
+ torture_assert_ntstatus_ok(tctx, status,
+ "samr_CreateUser failed");
+
+ *result_pipe = samr_pipe;
+ *result_handle = user_handle;
+ if (sid_p != NULL) {
+ *sid_p = dom_sid_add_rid(mem_ctx, *l.out.sid, user_rid);
+ }
+ return true;
+
+}
+
+/*
+ * Create a test user
+ */
+
+static bool create_user(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx, struct smbcli_state *cli,
+ 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 dcerpc_binding_handle *samr_handle;
+ struct policy_handle *wks_handle;
+ bool ret = false;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ torture_comment(tctx, "talloc_init failed\n");
+ return false;
+ }
+
+ ret = get_usr_handle(tctx, cli, tmp_ctx, admin_creds,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_INTEGRITY,
+ username, domain_name, &samr_pipe, &wks_handle,
+ user_sid);
+ if (ret == false) {
+ torture_comment(tctx, "get_usr_handle failed\n");
+ goto done;
+ }
+ samr_handle = samr_pipe->binding_handle;
+
+ {
+ struct samr_SetUserInfo2 sui2;
+ struct samr_SetUserInfo sui;
+ struct samr_QueryUserInfo qui;
+ union samr_UserInfo u_info;
+ union samr_UserInfo *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)) {
+ torture_comment(tctx, "dcerpc_fetch_session_key failed\n");
+ goto done;
+ }
+
+ status = init_samr_CryptPassword(password,
+ &session_key,
+ &u_info.info23.password);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "init_samr_CryptPassword failed\n");
+ goto done;
+ }
+
+ u_info.info23.info.password_expired = 0;
+ u_info.info23.info.fields_present = SAMR_FIELD_NT_PASSWORD_PRESENT |
+ SAMR_FIELD_LM_PASSWORD_PRESENT |
+ SAMR_FIELD_EXPIRED_FLAG;
+ sui2.in.user_handle = wks_handle;
+ sui2.in.info = &u_info;
+ sui2.in.level = 23;
+
+ status = dcerpc_samr_SetUserInfo2_r(samr_handle, tmp_ctx, &sui2);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "samr_SetUserInfo(23) failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(sui2.out.result)) {
+ torture_comment(tctx, "samr_SetUserInfo(23) failed: %s\n",
+ nt_errstr(sui2.out.result));
+ 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_r(samr_handle, tmp_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(sui.out.result)) {
+ torture_comment(tctx, "samr_SetUserInfo(16) failed\n");
+ goto done;
+ }
+
+ qui.in.user_handle = wks_handle;
+ qui.in.level = 21;
+ qui.out.info = &info;
+
+ status = dcerpc_samr_QueryUserInfo_r(samr_handle, tmp_ctx, &qui);
+ if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(qui.out.result)) {
+ torture_comment(tctx, "samr_QueryUserInfo(21) failed\n");
+ goto done;
+ }
+
+ info->info21.allow_password_change = 0;
+ info->info21.force_password_change = 0;
+ info->info21.account_name.string = NULL;
+ info->info21.rid = 0;
+ info->info21.acct_expiry = 0;
+ info->info21.fields_present = 0x81827fa; /* copy usrmgr.exe */
+
+ u_info.info21 = info->info21;
+ sui.in.user_handle = wks_handle;
+ sui.in.info = &u_info;
+ sui.in.level = 21;
+
+ status = dcerpc_samr_SetUserInfo_r(samr_handle, tmp_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(sui.out.result)) {
+ torture_comment(tctx, "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 torture_context *tctx,
+ struct smbcli_state *cli,
+ struct cli_credentials *admin_creds,
+ const char *username)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ char *dom_name;
+ struct dcerpc_pipe *samr_pipe;
+ struct dcerpc_binding_handle *samr_handle;
+ struct policy_handle *user_handle;
+ bool ret = false;
+
+ if ((mem_ctx = talloc_init("leave")) == NULL) {
+ torture_comment(tctx, "talloc_init failed\n");
+ return false;
+ }
+
+ ret = get_usr_handle(tctx, cli, mem_ctx, admin_creds,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_INTEGRITY,
+ username, &dom_name, &samr_pipe,
+ &user_handle, NULL);
+ if (ret == false) {
+ torture_comment(tctx, "get_wks_handle failed\n");
+ goto done;
+ }
+ samr_handle = samr_pipe->binding_handle;
+
+ {
+ struct samr_DeleteUser d;
+
+ d.in.user_handle = user_handle;
+ d.out.user_handle = user_handle;
+
+ status = dcerpc_samr_DeleteUser_r(samr_handle, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "samr_DeleteUser failed %s\n", nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(d.out.result)) {
+ torture_comment(tctx, "samr_DeleteUser failed %s\n", nt_errstr(d.out.result));
+ goto done;
+ }
+
+ }
+
+ ret = true;
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Do a Samba3-style join
+ */
+
+static bool join3(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ 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 dcerpc_binding_handle *samr_handle;
+ struct policy_handle *wks_handle;
+ bool ret = false;
+ NTTIME last_password_change;
+
+ if ((mem_ctx = talloc_init("join3")) == NULL) {
+ torture_comment(tctx, "talloc_init failed\n");
+ return false;
+ }
+
+ ret = get_usr_handle(
+ tctx, cli, mem_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 (ret == false) {
+ torture_comment(tctx, "get_wks_handle failed\n");
+ goto done;
+ }
+ samr_handle = samr_pipe->binding_handle;
+ ret = false;
+ {
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+
+ q.in.user_handle = wks_handle;
+ q.in.level = 21;
+ q.out.info = &info;
+
+ status = dcerpc_samr_QueryUserInfo_r(samr_handle, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "QueryUserInfo failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_warning(tctx, "QueryUserInfo failed: %s\n",
+ nt_errstr(q.out.result));
+ goto done;
+ }
+
+
+ last_password_change = 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;
+
+ 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_NT_PASSWORD_PRESENT;
+ /* this would break the test result expectations
+ i21->fields_present |= SAMR_FIELD_EXPIRED_FLAG;
+ i21->password_expired = 1;
+ */
+
+ status = dcerpc_fetch_session_key(samr_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_fetch_session_key failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = init_samr_CryptPasswordEx(cli_credentials_get_password(wks_creds),
+ &session_key,
+ &u_info.info25.password);
+
+ sui2.in.user_handle = wks_handle;
+ sui2.in.level = 25;
+ sui2.in.info = &u_info;
+
+ status = dcerpc_samr_SetUserInfo2_r(samr_handle, mem_ctx, &sui2);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "samr_SetUserInfo2(25) failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(sui2.out.result)) {
+ torture_comment(tctx, "samr_SetUserInfo2(25) failed: %s\n",
+ nt_errstr(sui2.out.result));
+ 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);
+ /* just to make this test pass */
+ u_info.info24.password_expired = 1;
+
+ status = dcerpc_fetch_session_key(samr_pipe, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_fetch_session_key failed\n");
+ goto done;
+ }
+
+ status = init_samr_CryptPassword(cli_credentials_get_password(wks_creds),
+ &session_key,
+ &u_info.info24.password);
+
+ sui2.in.user_handle = wks_handle;
+ sui2.in.info = &u_info;
+ sui2.in.level = 24;
+
+ status = dcerpc_samr_SetUserInfo2_r(samr_handle, mem_ctx, &sui2);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "samr_SetUserInfo(24) failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(sui2.out.result)) {
+ torture_comment(tctx, "samr_SetUserInfo(24) failed: %s\n",
+ nt_errstr(sui2.out.result));
+ 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_r(samr_handle, mem_ctx, &sui);
+ if (!NT_STATUS_IS_OK(status) || !NT_STATUS_IS_OK(sui.out.result)) {
+ torture_comment(tctx, "samr_SetUserInfo(16) failed\n");
+ goto done;
+ }
+ }
+
+ {
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+
+ q.in.user_handle = wks_handle;
+ q.in.level = 21;
+ q.out.info = &info;
+
+ status = dcerpc_samr_QueryUserInfo_r(samr_handle, mem_ctx, &q);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "QueryUserInfo failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_warning(tctx, "QueryUserInfo failed: %s\n",
+ nt_errstr(q.out.result));
+ goto done;
+ }
+
+ if (use_level25) {
+ if (last_password_change
+ == info->info21.last_password_change) {
+ torture_warning(tctx, "last_password_change unchanged "
+ "during join, level25 must change "
+ "it\n");
+ goto done;
+ }
+ }
+ else {
+ if (last_password_change
+ != info->info21.last_password_change) {
+ torture_warning(tctx, "last_password_change changed "
+ "during join, level24 doesn't "
+ "change it\n");
+ goto done;
+ }
+ }
+ }
+
+ ret = true;
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * Do a ReqChallenge/Auth2 and get the wks creds
+ */
+
+static bool auth2(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct cli_credentials *wks_cred)
+{
+ TALLOC_CTX *mem_ctx;
+ struct dcerpc_pipe *net_pipe;
+ struct dcerpc_binding_handle *net_handle;
+ 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 netlogon_creds_CredentialState *creds_state;
+ struct netr_Credential netr_cred;
+ struct samr_Password mach_pw;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ torture_comment(tctx, "talloc_new failed\n");
+ return false;
+ }
+
+ status = pipe_bind_smb(tctx, mem_ctx, cli->tree, "\\netlogon",
+ &ndr_table_netlogon, &net_pipe);
+ torture_assert_ntstatus_ok_goto(tctx, status, result, done,
+ "pipe_bind_smb failed");
+ net_handle = net_pipe->binding_handle;
+
+ 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) {
+ torture_comment(tctx, "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.return_credentials = &netr_srv_creds;
+
+ status = dcerpc_netr_ServerReqChallenge_r(net_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "netr_ServerReqChallenge failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "netr_ServerReqChallenge failed: %s\n",
+ nt_errstr(r.out.result));
+ goto done;
+ }
+
+ negotiate_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS | NETLOGON_NEG_SUPPORTS_AES;
+ E_md4hash(cli_credentials_get_password(wks_cred), mach_pw.hash);
+
+ 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.return_credentials = &netr_cred;
+
+ creds_state = netlogon_creds_client_init(mem_ctx,
+ a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ r.in.credentials,
+ r.out.return_credentials, &mach_pw,
+ &netr_cred, negotiate_flags);
+ torture_assert(tctx, (creds_state != NULL), "memory allocation failed");
+
+ status = dcerpc_netr_ServerAuthenticate2_r(net_handle, mem_ctx, &a);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "netr_ServerServerAuthenticate2 failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(a.out.result)) {
+ torture_comment(tctx, "netr_ServerServerAuthenticate2 failed: %s\n",
+ nt_errstr(a.out.result));
+ goto done;
+ }
+
+ if (!netlogon_creds_client_check(creds_state, a.out.return_credentials)) {
+ torture_comment(tctx, "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 torture_context *tctx,
+ struct smbcli_state *cli,
+ struct cli_credentials *wks_creds,
+ struct cli_credentials *user_creds)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct dcerpc_pipe *net_pipe;
+ struct dcerpc_binding_handle *net_handle;
+ int i;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ torture_comment(tctx, "talloc_new failed\n");
+ return false;
+ }
+
+#if 1
+ status = pipe_bind_smb_auth(tctx, mem_ctx, cli->tree,
+ wks_creds,
+ DCERPC_AUTH_TYPE_SCHANNEL,
+ DCERPC_AUTH_LEVEL_PRIVACY,
+ "\\netlogon", &ndr_table_netlogon, &net_pipe);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "pipe_bind_smb_auth failed");
+ net_pipe->conn->flags |= (DCERPC_SIGN | DCERPC_SEAL);
+#else
+ status = pipe_bind_smb(tctx, mem_ctx, cli->tree,
+ "\\netlogon", &ndr_table_netlogon, &net_pipe);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "pipe_bind_smb failed");
+#endif
+#if 0
+ net_pipe->conn->flags |= DCERPC_DEBUG_PRINT_IN |
+ DCERPC_DEBUG_PRINT_OUT;
+#endif
+ net_handle = net_pipe->binding_handle;
+
+
+ for (i=2; i<4; i++) {
+ int flags;
+ DATA_BLOB chal, nt_resp, lm_resp, names_blob;
+ struct netlogon_creds_CredentialState *creds_state;
+ struct netr_Authenticator netr_auth, netr_auth2;
+ struct netr_NetworkInfo ninfo;
+ struct netr_PasswordInfo pinfo;
+ struct netr_LogonSamLogon r;
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative;
+ struct netr_Authenticator return_authenticator;
+
+ 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) {
+ torture_comment(tctx, "data_blob_talloc failed\n");
+ goto done;
+ }
+
+ generate_random_buffer(chal.data, chal.length);
+ names_blob = NTLMv2_generate_names_blob(
+ mem_ctx,
+ cli_credentials_get_workstation(wks_creds),
+ cli_credentials_get_domain(wks_creds));
+ status = cli_credentials_get_ntlm_response(
+ user_creds, mem_ctx, &flags, chal, NULL, names_blob,
+ &lm_resp, &nt_resp, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "cli_credentials_get_ntlm_response failed:"
+ " %s\n", nt_errstr(status));
+ goto done;
+ }
+
+ creds_state = cli_credentials_get_netlogon_creds(wks_creds);
+ netlogon_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 = 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;
+
+ logon.network = &ninfo;
+
+ 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 = NetlogonNetworkInformation;
+ r.in.validation_level = i;
+ r.in.logon = &logon;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+ r.out.return_authenticator = &return_authenticator;
+
+ status = dcerpc_netr_LogonSamLogon_r(net_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "netr_LogonSamLogon failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "netr_LogonSamLogon failed: %s\n",
+ nt_errstr(r.out.result));
+ goto done;
+ }
+
+ if ((r.out.return_authenticator == NULL) ||
+ (!netlogon_creds_client_check(creds_state,
+ &r.out.return_authenticator->cred))) {
+ torture_comment(tctx, "Credentials check failed!\n");
+ goto done;
+ }
+
+ netlogon_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);
+
+ logon.password = &pinfo;
+
+ /*
+ * We don't use this here:
+ *
+ * netlogon_creds_encrypt_samlogon_logon(creds_state,
+ * NetlogonInteractiveInformation,
+ * &logon);
+ *
+ * in order to detect bugs
+ */
+ netlogon_creds_aes_encrypt(creds_state, pinfo.ntpassword.hash, 16);
+
+ r.in.logon_level = NetlogonInteractiveInformation;
+ r.in.logon = &logon;
+ r.out.return_authenticator = &return_authenticator;
+
+ status = dcerpc_netr_LogonSamLogon_r(net_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "netr_LogonSamLogon failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "netr_LogonSamLogon failed: %s\n",
+ nt_errstr(r.out.result));
+ goto done;
+ }
+
+ if ((r.out.return_authenticator == NULL) ||
+ (!netlogon_creds_client_check(creds_state,
+ &r.out.return_authenticator->cred))) {
+ torture_comment(tctx, "Credentials check failed!\n");
+ goto done;
+ }
+ }
+
+ {
+ struct netr_ServerPasswordSet s;
+ char *password = generate_random_password(wks_creds, 8, 255);
+ struct netlogon_creds_CredentialState *creds_state;
+ struct netr_Authenticator credential, return_authenticator;
+ struct samr_Password new_password;
+
+ 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;
+ s.in.credential = &credential;
+ s.in.new_password = &new_password;
+ s.out.return_authenticator = &return_authenticator;
+
+ E_md4hash(password, new_password.hash);
+
+ creds_state = cli_credentials_get_netlogon_creds(wks_creds);
+ netlogon_creds_des_encrypt(creds_state, &new_password);
+ netlogon_creds_client_authenticator(creds_state, &credential);
+
+ status = dcerpc_netr_ServerPasswordSet_r(net_handle, mem_ctx, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "ServerPasswordSet - %s\n", nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_comment(tctx, "ServerPasswordSet - %s\n", nt_errstr(s.out.result));
+ goto done;
+ }
+
+ if (!netlogon_creds_client_check(creds_state,
+ &s.out.return_authenticator->cred)) {
+ torture_comment(tctx, "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 torture_context *tctx,
+ struct smbcli_state *cli,
+ 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(tctx, cli, admin_creds, wks_name);
+ talloc_free(wks_name);
+ return ret;
+}
+
+/*
+ * Test the Samba3 DC code a bit. Join, do some schan netlogon ops, leave
+ */
+
+static bool torture_netlogon_samba3(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct smbcli_state *cli;
+ struct cli_credentials *wks_creds;
+ const char *wks_name;
+ int i;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+
+ wks_name = torture_setting_string(torture, "wksname", NULL);
+ torture_assert(torture, wks_name != NULL, "wksname not set");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+ lpcfg_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(torture, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lpcfg_socket_options(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ torture_assert_ntstatus_ok(torture, status, "smbcli_full_connection failed\n");
+
+ wks_creds = cli_credentials_init(torture);
+ if (wks_creds == NULL) {
+ torture_fail(torture, "cli_credentials_init failed\n");
+ }
+
+ 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_password(wks_creds, 8, 255),
+ CRED_SPECIFIED);
+
+ torture_assert(torture,
+ join3(torture, cli, false, NULL, wks_creds),
+ "join failed");
+
+ cli_credentials_set_domain(
+ samba_cmdline_get_creds(),
+ 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;
+
+ torture_assert(torture,
+ auth2(torture, cli, wks_creds),
+ "auth2 failed");
+
+ for (j=0; j<2; j++) {
+ torture_assert(torture,
+ schan(torture, cli, wks_creds,
+ samba_cmdline_get_creds()),
+ "schan failed");
+ }
+ }
+
+ torture_assert(torture,
+ leave(torture, cli, NULL, wks_creds),
+ "leave failed");
+
+ return true;
+}
+
+/*
+ * 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;
+ struct smbcli_state *cli;
+ struct cli_credentials *wks_creds;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+
+ lpcfg_smbcli_options(tctx->lp_ctx, &options);
+ lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(tctx, &cli,
+ torture_setting_string(tctx, "host", NULL),
+ lpcfg_smb_ports(tctx->lp_ctx),
+ "IPC$", NULL, lpcfg_socket_options(tctx->lp_ctx),
+ smb_creds, lpcfg_resolve_context(tctx->lp_ctx),
+ tctx->ev, &options, &session_options,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+ torture_assert_ntstatus_ok(tctx, status,
+ "smbcli_full_connection failed");
+
+ wks_creds = cli_credentials_init(cli);
+ torture_assert(tctx, wks_creds, "cli_credentials_init failed");
+
+ 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_password(wks_creds, 8, 255),
+ CRED_SPECIFIED);
+
+ torture_assert(tctx,
+ join3(tctx, cli, use_level25, samr_creds, wks_creds),
+ "join failed");
+
+ cli_credentials_set_domain(
+ samba_cmdline_get_creds(),
+ cli_credentials_get_domain(wks_creds),
+ CRED_SPECIFIED);
+
+ torture_assert(tctx,
+ auth2(tctx, cli, wks_creds),
+ "auth2 failed");
+
+ torture_assert(tctx,
+ leave(tctx, cli, samr_creds, wks_creds),
+ "leave failed");
+
+ talloc_free(cli);
+
+ return true;
+}
+
+/*
+ * 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.
+ */
+
+static bool torture_samba3_sessionkey(struct torture_context *torture)
+{
+ struct cli_credentials *anon_creds;
+ const char *wks_name;
+
+
+ wks_name = torture_setting_string(torture, "wksname", NULL);
+ torture_assert(torture, wks_name != NULL, "wksname not set");
+
+ if (!(anon_creds = cli_credentials_init_anon(torture))) {
+ torture_fail(torture, "create_anon_creds failed\n");
+ }
+
+ cli_credentials_set_workstation(anon_creds, wks_name, CRED_SPECIFIED);
+
+
+ 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)) {
+ torture_fail(torture, "join using anonymous bind on an anonymous smb "
+ "connection succeeded -- HUH??\n");
+ }
+ }
+
+ torture_assert(torture,
+ test_join3(torture, false, samba_cmdline_get_creds(),
+ NULL, wks_name),
+ "join using anonymous bind on an authenticated smb connection failed");
+
+ /*
+ * The following two are tests for setuserinfolevel 25
+ */
+
+ torture_assert(torture,
+ test_join3(torture, true, samba_cmdline_get_creds(),
+ NULL, wks_name),
+ "join using anonymous bind on an authenticated smb connection failed");
+
+ return true;
+}
+
+/*
+ * Sane wrapper around lsa_LookupNames
+ */
+
+static struct dom_sid *name2sid(struct torture_context *tctx,
+ 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_RefDomainList *domains = NULL;
+ struct lsa_String lsa_name;
+ uint32_t count = 0;
+ struct dom_sid *result;
+ TALLOC_CTX *tmp_ctx;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "OpenPolicy2 failed - %s\n", nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "OpenPolicy2 failed - %s\n", nt_errstr(r.out.result));
+ 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;
+ l.out.domains = &domains;
+
+ status = dcerpc_lsa_LookupNames_r(b, tmp_ctx, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "LookupNames of %s failed - %s\n", lsa_name.string,
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(l.out.result)) {
+ torture_comment(tctx, "LookupNames of %s failed - %s\n", lsa_name.string,
+ nt_errstr(l.out.result));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ result = dom_sid_add_rid(mem_ctx, domains->domains[0].sid,
+ l.out.sids->sids[0].rid);
+
+ c.in.handle = &handle;
+ c.out.handle = &handle;
+
+ status = dcerpc_lsa_Close_r(b, tmp_ctx, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "dcerpc_lsa_Close failed - %s\n", nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(c.out.result)) {
+ torture_comment(tctx, "dcerpc_lsa_Close failed - %s\n", nt_errstr(c.out.result));
+ 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(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct smbcli_tree *tree)
+{
+ struct dcerpc_pipe *lsa;
+ struct dcerpc_binding_handle *lsa_handle;
+ struct lsa_GetUserName r;
+ NTSTATUS status;
+ struct lsa_String *authority_name_p = NULL;
+ struct lsa_String *account_name_p = NULL;
+ struct dom_sid *result;
+
+ status = pipe_bind_smb(tctx, mem_ctx, tree, "\\pipe\\lsarpc",
+ &ndr_table_lsarpc, &lsa);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "Could not bind to LSA: %s\n",
+ nt_errstr(status));
+ return NULL;
+ }
+ lsa_handle = lsa->binding_handle;
+
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "GetUserName failed - %s\n",
+ nt_errstr(status));
+ talloc_free(lsa);
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_warning(tctx, "GetUserName failed - %s\n",
+ nt_errstr(r.out.result));
+ talloc_free(lsa);
+ return NULL;
+ }
+
+ result = name2sid(tctx, mem_ctx, lsa, account_name_p->string,
+ authority_name_p->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
+ */
+
+static NTSTATUS secondary_tcon(struct torture_context *tctx,
+ 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 = TCONX_FLAG_EXTENDED_RESPONSE;
+ tcon.tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
+ 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)) {
+ torture_warning(tctx, "smb_raw_tcon failed: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ result->tid = tcon.tconx.out.tid;
+
+ if (tcon.tconx.out.options & SMB_EXTENDED_SIGNATURES) {
+ smb1cli_session_protect_session_key(result->session->smbXcli);
+ }
+
+ 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
+ */
+
+static bool torture_samba3_rpc_getusername(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct smbcli_state *cli;
+ 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;
+ struct smbcli_session_options session_options;
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+ lpcfg_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ if (!(anon_creds = cli_credentials_init_anon(torture))) {
+ torture_fail(torture, "create_anon_creds failed\n");
+ }
+
+ status = smbcli_full_connection(
+ torture, &cli, torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx), "IPC$", NULL,
+ lpcfg_socket_options(torture->lp_ctx), anon_creds,
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ torture_assert_ntstatus_ok(torture, status, "anon smbcli_full_connection failed\n");
+
+ if (!(user_sid = whoami(torture, torture, cli->tree))) {
+ torture_fail(torture, "whoami on anon connection failed\n");
+ }
+
+ torture_assert_sid_equal(torture, user_sid, dom_sid_parse_talloc(torture, "s-1-5-7"),
+ "Anon lsa_GetUserName returned unexpected SID");
+
+ talloc_free(cli);
+
+ status = smbcli_full_connection(
+ torture, &cli, torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$", NULL, lpcfg_socket_options(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(torture->lp_ctx), torture->ev, &options,
+ &session_options, lpcfg_gensec_settings(torture, torture->lp_ctx));
+ torture_assert_ntstatus_ok(torture, status, "smbcli_full_connection failed\n");
+
+ if (!(user_sid = whoami(torture, torture, cli->tree))) {
+ torture_fail(torture, "whoami on auth'ed connection failed\n");
+ }
+
+ if (!(user_creds = cli_credentials_init(torture))) {
+ torture_fail(torture, "cli_credentials_init failed\n");
+ }
+
+ 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_password(user_creds, 8, 255),
+ CRED_SPECIFIED);
+
+ if (!create_user(torture, torture, cli, NULL,
+ cli_credentials_get_username(user_creds),
+ cli_credentials_get_password(user_creds),
+ &domain_name, &created_sid)) {
+ torture_fail(torture, "create_user failed\n");
+ }
+
+ 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, torture, false, session_options);
+ if (session2 == NULL) {
+ torture_fail(torture, "smbcli_session_init failed\n");
+ }
+
+ setup.in.sesskey = cli->transport->negotiate.sesskey;
+ setup.in.capabilities = cli->transport->negotiate.capabilities;
+ setup.in.workgroup = "";
+ setup.in.credentials = user_creds;
+ setup.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx);
+
+ status = smb_composite_sesssetup(session2, &setup);
+ torture_assert_ntstatus_ok(torture, status, "session setup with new user failed");
+
+ session2->vuid = setup.out.vuid;
+
+ if (!NT_STATUS_IS_OK(secondary_tcon(torture, torture, session2,
+ "IPC$", &tree))) {
+ torture_fail(torture, "secondary_tcon failed\n");
+ }
+
+ if (!(user_sid = whoami(torture, torture, tree))) {
+ torture_fail_goto(torture, del, "whoami on user connection failed\n");
+ ret = false;
+ goto del;
+ }
+
+ talloc_free(tree);
+ }
+
+ torture_comment(torture, "Created %s, found %s\n",
+ dom_sid_string(torture, created_sid),
+ dom_sid_string(torture, user_sid));
+
+ if (!dom_sid_equal(created_sid, user_sid)) {
+ ret = false;
+ }
+
+ del:
+ if (!delete_user(torture, cli,
+ NULL,
+ cli_credentials_get_username(user_creds))) {
+ torture_fail(torture, "delete_user failed\n");
+ }
+
+ return ret;
+}
+
+static bool test_NetShareGetInfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *sharename)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareGetInfo r;
+ union srvsvc_NetShareInfo info;
+ uint32_t levels[] = { 0, 1, 2, 501, 502, 1004, 1005, 1006, 1007, 1501 };
+ int i;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.share_name = sharename;
+ r.out.info = &info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+
+ torture_comment(tctx, "Testing NetShareGetInfo level %u on share '%s'\n",
+ r.in.level, r.in.share_name);
+
+ status = dcerpc_srvsvc_NetShareGetInfo_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "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)) {
+ torture_warning(tctx, "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 torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char **one_sharename)
+{
+ NTSTATUS status;
+ struct srvsvc_NetShareEnum r;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr0 c0;
+ struct srvsvc_NetShareCtr1 c1;
+ struct srvsvc_NetShareCtr2 c2;
+ struct srvsvc_NetShareCtr501 c501;
+ struct srvsvc_NetShareCtr502 c502;
+ struct srvsvc_NetShareCtr1004 c1004;
+ struct srvsvc_NetShareCtr1005 c1005;
+ struct srvsvc_NetShareCtr1006 c1006;
+ struct srvsvc_NetShareCtr1007 c1007;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = { 0, 1, 2, 501, 502, 1004, 1005, 1006, 1007 };
+ int i;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(info_ctr);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 501:
+ ZERO_STRUCT(c501);
+ info_ctr.ctr.ctr501 = &c501;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ case 1004:
+ ZERO_STRUCT(c1004);
+ info_ctr.ctr.ctr1004 = &c1004;
+ break;
+ case 1005:
+ ZERO_STRUCT(c1005);
+ info_ctr.ctr.ctr1005 = &c1005;
+ break;
+ case 1006:
+ ZERO_STRUCT(c1006);
+ info_ctr.ctr.ctr1006 = &c1006;
+ break;
+ case 1007:
+ ZERO_STRUCT(c1007);
+ info_ctr.ctr.ctr1007 = &c1007;
+ break;
+ }
+
+ torture_comment(tctx, "Testing NetShareEnum level %u\n", info_ctr.level);
+
+ status = dcerpc_srvsvc_NetShareEnum_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "NetShareEnum level %u failed - %s\n",
+ info_ctr.level, nt_errstr(status));
+ ret = false;
+ continue;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_warning(tctx, "NetShareEnum level %u failed - %s\n",
+ info_ctr.level, win_errstr(r.out.result));
+ continue;
+ }
+ if (info_ctr.level == 0) {
+ struct srvsvc_NetShareCtr0 *ctr = r.out.info_ctr->ctr.ctr0;
+ if (ctr->count > 0) {
+ *one_sharename = ctr->array[0].name;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool torture_samba3_rpc_srvsvc(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p;
+ const char *sharename = NULL;
+ bool ret = true;
+
+ torture_assert_ntstatus_ok(torture,
+ torture_rpc_connection(torture, &p, &ndr_table_srvsvc),
+ "failed to open srvsvc");
+
+ ret &= test_NetShareEnum(torture, p, &sharename);
+ if (sharename == NULL) {
+ torture_comment(torture, "did not get sharename\n");
+ } else {
+ ret &= test_NetShareGetInfo(torture, p, sharename);
+ }
+
+ return ret;
+}
+
+/*
+ * Do a ReqChallenge/Auth2 with a random wks name, make sure it returns
+ * NT_STATUS_NO_SAM_ACCOUNT
+ */
+
+static bool torture_samba3_rpc_randomauth2(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ struct dcerpc_pipe *net_pipe;
+ struct dcerpc_binding_handle *net_handle;
+ 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 netlogon_creds_CredentialState *creds_state;
+ struct netr_Credential netr_cred;
+ struct samr_Password mach_pw;
+ struct smbcli_state *cli;
+
+ if (!(mem_ctx = talloc_new(torture))) {
+ torture_comment(torture, "talloc_new failed\n");
+ return false;
+ }
+
+ if (!(wksname = generate_random_str_list(
+ mem_ctx, 14, "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"))) {
+ torture_comment(torture, "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))) {
+ torture_comment(torture, "IPC$ connection failed\n");
+ goto done;
+ }
+
+ status = pipe_bind_smb(torture, mem_ctx, cli->tree, "\\netlogon",
+ &ndr_table_netlogon, &net_pipe);
+ torture_assert_ntstatus_ok_goto(torture, status, result, done,
+ "pipe_bind_smb failed");
+ net_handle = net_pipe->binding_handle;
+
+ 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) {
+ torture_comment(torture, "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.return_credentials = &netr_srv_creds;
+
+ status = dcerpc_netr_ServerReqChallenge_r(net_handle, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(torture, "netr_ServerReqChallenge failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(torture, "netr_ServerReqChallenge failed: %s\n",
+ nt_errstr(r.out.result));
+ goto done;
+ }
+
+ negotiate_flags = NETLOGON_NEG_AUTH2_FLAGS;
+ E_md4hash("foobar", mach_pw.hash);
+
+ 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.return_credentials = &netr_cred;
+
+ creds_state = netlogon_creds_client_init(mem_ctx,
+ a.in.account_name,
+ a.in.computer_name,
+ a.in.secure_channel_type,
+ r.in.credentials,
+ r.out.return_credentials, &mach_pw,
+ &netr_cred, negotiate_flags);
+ torture_assert(torture, (creds_state != NULL), "memory allocation failed");
+
+ status = dcerpc_netr_ServerAuthenticate2_r(net_handle, mem_ctx, &a);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_EQUAL(a.out.result, NT_STATUS_NO_TRUST_SAM_ACCOUNT)) {
+ torture_comment(torture, "dcerpc_netr_ServerAuthenticate2 returned %s, "
+ "expected NT_STATUS_NO_TRUST_SAM_ACCOUNT\n",
+ nt_errstr(a.out.result));
+ goto done;
+ }
+
+ result = true;
+ done:
+ talloc_free(mem_ctx);
+ return result;
+}
+
+static struct security_descriptor *get_sharesec(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct smbcli_session *sess,
+ const char *sharename)
+{
+ struct smbcli_tree *tree;
+ TALLOC_CTX *tmp_ctx;
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding_handle *b;
+ NTSTATUS status;
+ struct srvsvc_NetShareGetInfo r;
+ union srvsvc_NetShareInfo info;
+ struct security_descriptor *result;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ torture_comment(tctx, "talloc_new failed\n");
+ return NULL;
+ }
+
+ if (!NT_STATUS_IS_OK(secondary_tcon(tctx, tmp_ctx, sess, "IPC$", &tree))) {
+ torture_comment(tctx, "secondary_tcon failed\n");
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ status = pipe_bind_smb(tctx, mem_ctx, tree, "\\pipe\\srvsvc",
+ &ndr_table_srvsvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "could not bind to srvsvc pipe: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ b = p->binding_handle;
+
+#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;
+ r.out.info = &info;
+
+ status = dcerpc_srvsvc_NetShareGetInfo_r(b, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "srvsvc_NetShareGetInfo failed: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "srvsvc_NetShareGetInfo failed: %s\n",
+ win_errstr(r.out.result));
+ talloc_free(tmp_ctx);
+ return NULL;
+ }
+
+ result = talloc_steal(mem_ctx, info.info502->sd_buf.sd);
+ talloc_free(tmp_ctx);
+ return result;
+}
+
+static NTSTATUS set_sharesec(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct smbcli_session *sess,
+ const char *sharename,
+ struct security_descriptor *sd)
+{
+ struct smbcli_tree *tree;
+ TALLOC_CTX *tmp_ctx;
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding_handle *b;
+ NTSTATUS status;
+ struct sec_desc_buf i;
+ struct srvsvc_NetShareSetInfo r;
+ union srvsvc_NetShareInfo info;
+ uint32_t error = 0;
+
+ if (!(tmp_ctx = talloc_new(mem_ctx))) {
+ torture_comment(tctx, "talloc_new failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!NT_STATUS_IS_OK(secondary_tcon(tctx, tmp_ctx, sess, "IPC$", &tree))) {
+ torture_comment(tctx, "secondary_tcon failed\n");
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = pipe_bind_smb(tctx, mem_ctx, tree, "\\pipe\\srvsvc",
+ &ndr_table_srvsvc, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_warning(tctx, "could not bind to srvsvc pipe: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ b = p->binding_handle;
+
+#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;
+ info.info1501 = &i;
+ r.in.info = &info;
+ r.in.parm_error = &error;
+
+ status = dcerpc_srvsvc_NetShareSetInfo_r(b, tmp_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "srvsvc_NetShareSetInfo failed: %s\n",
+ nt_errstr(status));
+ }
+ if (!W_ERROR_IS_OK(r.out.result)) {
+ torture_comment(tctx, "srvsvc_NetShareSetInfo failed: %s\n",
+ win_errstr(r.out.result));
+ status = werror_to_ntstatus(r.out.result);
+ }
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+bool try_tcon(struct torture_context *tctx,
+ TALLOC_CTX *mem_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))) {
+ torture_comment(tctx, "talloc_new failed\n");
+ return false;
+ }
+
+ status = secondary_tcon(tctx, tmp_ctx, session, sharename, &rmdir_tree);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "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))) {
+ torture_comment(tctx, "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) {
+ torture_comment(tctx, "security_descriptor_dacl_create failed\n");
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ status = set_sharesec(tctx, mem_ctx, session, sharename, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "custom set_sharesec failed: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ status = secondary_tcon(tctx, tmp_ctx, session, sharename, &tree);
+ if (!NT_STATUS_EQUAL(status, expected_tcon)) {
+ torture_comment(tctx, "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)) {
+ torture_warning(tctx, "Expected %s, got %s\n",
+ nt_errstr(expected_mkdir), nt_errstr(status));
+ ret = false;
+ }
+
+ done:
+ smbcli_rmdir(rmdir_tree, "sharesec_testdir");
+
+ status = set_sharesec(tctx, mem_ctx, session, sharename, orig_sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "custom set_sharesec failed: %s\n",
+ nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static bool torture_samba3_rpc_sharesec(struct torture_context *torture)
+{
+ struct smbcli_state *cli = NULL;
+ struct security_descriptor *sd = NULL;
+ struct dom_sid *user_sid = NULL;
+ const char *testuser_passwd = NULL;
+ struct cli_credentials *test_credentials = NULL;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+ NTSTATUS status;
+ struct test_join *tj = NULL;
+ struct dcerpc_pipe *lsa_pipe = NULL;
+ const char *priv_array[1];
+
+ /* Create a new user. The normal user has SeBackup and SeRestore
+ privs so we can't lock them out with a share security descriptor. */
+ tj = torture_create_testuser(torture,
+ "sharesec_user",
+ torture_setting_string(torture, "workgroup", NULL),
+ ACB_NORMAL,
+ &testuser_passwd);
+ if (!tj) {
+ torture_fail(torture, "Creating sharesec_user failed\n");
+ }
+
+ /* Give them SeDiskOperatorPrivilege but no other privs. */
+ status = torture_rpc_connection(torture, &lsa_pipe, &ndr_table_lsarpc);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_delete_testuser(torture, tj, "sharesec_user");
+ talloc_free(tj);
+ torture_fail(torture, "Error connecting to LSA pipe");
+ }
+
+ priv_array[0] = "SeDiskOperatorPrivilege";
+ if (!torture_setup_privs(torture,
+ lsa_pipe,
+ 1,
+ priv_array,
+ torture_join_user_sid(tj))) {
+ talloc_free(lsa_pipe);
+ torture_delete_testuser(torture, tj, "sharesec_user");
+ talloc_free(tj);
+ torture_fail(torture, "Failed to setup privs\n");
+ }
+ talloc_free(lsa_pipe);
+
+ test_credentials = cli_credentials_init(torture);
+ cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED);
+ cli_credentials_set_domain(test_credentials, lpcfg_workgroup(torture->lp_ctx),
+ CRED_SPECIFIED);
+ cli_credentials_set_username(test_credentials, "sharesec_user", CRED_SPECIFIED);
+ cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED);
+
+ ZERO_STRUCT(options);
+ ZERO_STRUCT(session_options);
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+ lpcfg_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(torture,
+ &cli,
+ torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$",
+ NULL,
+ lpcfg_socket_options(torture->lp_ctx),
+ test_credentials,
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev,
+ &options,
+ &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(cli);
+ torture_delete_testuser(torture, tj, "sharesec_user");
+ talloc_free(tj);
+ torture_fail(torture, "Failed to open connection\n");
+ }
+
+ if (!(user_sid = whoami(torture, torture, cli->tree))) {
+ talloc_free(cli);
+ torture_delete_testuser(torture, tj, "sharesec_user");
+ talloc_free(tj);
+ torture_fail(torture, "whoami failed\n");
+ }
+
+ sd = get_sharesec(torture, torture, cli->session,
+ torture_setting_string(torture, "share", NULL));
+
+ if (!try_tcon(torture, torture, sd, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ user_sid, 0, NT_STATUS_ACCESS_DENIED, NT_STATUS_OK)) {
+ talloc_free(cli);
+ torture_delete_testuser(torture, tj, "sharesec_user");
+ talloc_free(tj);
+ torture_fail(torture, "failed to test tcon with 0 access_mask");
+ }
+
+ if (!try_tcon(torture, torture, sd, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ user_sid, SEC_FILE_READ_DATA, NT_STATUS_OK,
+ NT_STATUS_MEDIA_WRITE_PROTECTED)) {
+ talloc_free(cli);
+ torture_delete_testuser(torture, tj, "sharesec_user");
+ talloc_free(tj);
+ torture_fail(torture, "failed to test tcon with SEC_FILE_READ_DATA access_mask");
+ }
+
+ /* sharesec_user doesn't have any rights on the underlying file system.
+ Go back to the normal user. */
+
+ talloc_free(cli);
+ cli = NULL;
+ torture_delete_testuser(torture, tj, "sharesec_user");
+ talloc_free(tj);
+ tj = NULL;
+
+ if (!(torture_open_connection_share(
+ torture, &cli, torture, torture_setting_string(torture, "host", NULL),
+ "IPC$", torture->ev))) {
+ torture_fail(torture, "IPC$ connection failed\n");
+ }
+
+ if (!(user_sid = whoami(torture, torture, cli->tree))) {
+ torture_fail(torture, "whoami failed\n");
+ }
+ torture_assert(torture, try_tcon(
+ torture, torture, sd, cli->session,
+ torture_setting_string(torture, "share", NULL),
+ user_sid, SEC_FILE_ALL, NT_STATUS_OK, NT_STATUS_OK),
+ "failed to test tcon with SEC_FILE_ALL access_mask");
+
+ return true;
+}
+
+static bool torture_samba3_rpc_lsa(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding_handle *b;
+ struct policy_handle lsa_handle;
+
+ torture_assert_ntstatus_ok(torture,
+ torture_rpc_connection(torture, &p, &ndr_table_lsarpc),
+ "failed to setup lsarpc");
+
+ b = p->binding_handle;
+
+ {
+ struct lsa_ObjectAttribute attr;
+ struct lsa_OpenPolicy2 o;
+ o.in.system_name = talloc_asprintf(
+ torture, "\\\\%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;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_lsa_OpenPolicy2_r(b, torture, &o),
+ "dcerpc_lsa_OpenPolicy2 failed");
+ torture_assert_ntstatus_ok(torture, o.out.result,
+ "dcerpc_lsa_OpenPolicy2 failed");
+ }
+
+ {
+ int i;
+ int levels[] = { 2,3,5,6 };
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+ struct lsa_QueryInfoPolicy r;
+ union lsa_PolicyInformation *info = NULL;
+ r.in.handle = &lsa_handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_lsa_QueryInfoPolicy_r(b, torture, &r),
+ talloc_asprintf(torture, "dcerpc_lsa_QueryInfoPolicy level %d failed", levels[i]));
+ torture_assert_ntstatus_ok(torture, r.out.result,
+ talloc_asprintf(torture, "dcerpc_lsa_QueryInfoPolicy level %d failed", levels[i]));
+ }
+ }
+
+ return true;
+}
+
+static NTSTATUS get_servername(TALLOC_CTX *mem_ctx, struct smbcli_tree *tree,
+ char **name)
+{
+ struct rap_WserverGetInfo r;
+ NTSTATUS status;
+ char servername[17];
+ size_t converted_size;
+
+ r.in.level = 0;
+ r.in.bufsize = 0xffff;
+
+ status = smbcli_rap_netservergetinfo(tree, 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, name, servername, &converted_size)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static bool rap_get_servername(struct torture_context *tctx,
+ char **servername)
+{
+ struct smbcli_state *cli;
+
+ torture_assert(tctx,
+ torture_open_connection_share(tctx, &cli, tctx, torture_setting_string(tctx, "host", NULL),
+ "IPC$", tctx->ev),
+ "IPC$ connection failed");
+
+ torture_assert_ntstatus_ok(tctx,
+ get_servername(tctx, cli->tree, servername),
+ "get_servername failed");
+
+ talloc_free(cli);
+
+ return true;
+}
+
+static bool find_printers(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char ***printers,
+ size_t *num_printers)
+{
+ struct srvsvc_NetShareEnum r;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr1 c1_in;
+ struct srvsvc_NetShareCtr1 *c1;
+ uint32_t totalentries = 0;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(c1_in);
+ info_ctr.level = 1;
+ info_ctr.ctr.ctr1 = &c1_in;
+
+ r.in.server_unc = talloc_asprintf(
+ tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_srvsvc_NetShareEnum_r(b, tctx, &r),
+ "NetShareEnum level 1 failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "NetShareEnum level 1 failed");
+
+ *printers = NULL;
+ *num_printers = 0;
+ c1 = r.out.info_ctr->ctr.ctr1;
+ for (i=0; i<c1->count; i++) {
+ if (c1->array[i].type != STYPE_PRINTQ) {
+ continue;
+ }
+ if (!add_string_to_array(tctx, c1->array[i].name,
+ printers, num_printers)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool enumprinters(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername, int level, int *num_printers)
+{
+ struct spoolss_EnumPrinters r;
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = talloc_asprintf(tctx, "\\\\%s", servername);
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+ "dcerpc_spoolss_EnumPrinters failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "EnumPrinters unexpected return code should be WERR_INSUFFICIENT_BUFFER");
+
+ blob = data_blob_talloc_zero(tctx, needed);
+ if (blob.data == NULL) {
+ return false;
+ }
+
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+ "dcerpc_spoolss_EnumPrinters failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "dcerpc_spoolss_EnumPrinters failed");
+
+ *num_printers = count;
+
+ return true;
+}
+
+static bool getprinterinfo(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle, int level,
+ union spoolss_PrinterInfo **res)
+{
+ struct spoolss_GetPrinter r;
+ DATA_BLOB blob;
+ uint32_t needed;
+
+ r.in.handle = handle;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinter_r(b, tctx, &r),
+ "dcerpc_spoolss_GetPrinter failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "GetPrinter unexpected return code should be WERR_INSUFFICIENT_BUFFER");
+
+ r.in.handle = handle;
+ r.in.level = level;
+ blob = data_blob_talloc_zero(tctx, needed);
+ if (blob.data == NULL) {
+ return false;
+ }
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinter_r(b, tctx, &r),
+ "dcerpc_spoolss_GetPrinter failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "dcerpc_spoolss_GetPrinter failed");
+
+ if (res != NULL) {
+ *res = talloc_steal(tctx, r.out.info);
+ }
+
+ return true;
+}
+
+static bool torture_samba3_rpc_spoolss(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p, *p2;
+ struct dcerpc_binding_handle *b;
+ struct policy_handle server_handle, printer_handle;
+ const char **printers;
+ size_t num_printers;
+ struct spoolss_UserLevel1 userlevel1;
+ char *servername;
+
+ torture_assert(torture,
+ rap_get_servername(torture, &servername),
+ "failed to rap servername");
+
+ torture_assert_ntstatus_ok(torture,
+ torture_rpc_connection(torture, &p2, &ndr_table_srvsvc),
+ "failed to setup srvsvc");
+
+ torture_assert(torture,
+ find_printers(torture, p2, &printers, &num_printers),
+ "failed to find printers via srvsvc");
+
+ talloc_free(p2);
+
+ if (num_printers == 0) {
+ torture_skip(torture, "Did not find printers\n");
+ return true;
+ }
+
+ torture_assert_ntstatus_ok(torture,
+ torture_rpc_connection(torture, &p, &ndr_table_spoolss),
+ "failed to setup spoolss");
+
+ b = p->binding_handle;
+
+ ZERO_STRUCT(userlevel1);
+ userlevel1.client = talloc_asprintf(
+ torture, "\\\\%s", lpcfg_netbios_name(torture->lp_ctx));
+ userlevel1.user = cli_credentials_get_username(
+ samba_cmdline_get_creds());
+ userlevel1.build = 2600;
+ userlevel1.major = 3;
+ userlevel1.minor = 0;
+ userlevel1.processor = 0;
+
+ {
+ struct spoolss_OpenPrinterEx r;
+
+ ZERO_STRUCT(r);
+ r.in.printername = talloc_asprintf(torture, "\\\\%s",
+ servername);
+ r.in.datatype = NULL;
+ r.in.access_mask = 0;
+ r.in.userlevel_ctr.level = 1;
+ r.in.userlevel_ctr.user_info.level1 = &userlevel1;
+ r.out.handle = &server_handle;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_spoolss_OpenPrinterEx_r(b, torture, &r),
+ "dcerpc_spoolss_OpenPrinterEx failed");
+ torture_assert_werr_ok(torture, r.out.result,
+ "dcerpc_spoolss_OpenPrinterEx failed");
+ }
+
+ {
+ struct spoolss_ClosePrinter r;
+
+ r.in.handle = &server_handle;
+ r.out.handle = &server_handle;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_spoolss_ClosePrinter_r(b, torture, &r),
+ "dcerpc_spoolss_ClosePrinter failed");
+ torture_assert_werr_ok(torture, r.out.result,
+ "dcerpc_spoolss_ClosePrinter failed");
+ }
+
+ {
+ struct spoolss_OpenPrinterEx r;
+
+ ZERO_STRUCT(r);
+ r.in.printername = talloc_asprintf(
+ torture, "\\\\%s\\%s", servername, printers[0]);
+ r.in.datatype = NULL;
+ r.in.access_mask = 0;
+ r.in.userlevel_ctr.level = 1;
+ r.in.userlevel_ctr.user_info.level1 = &userlevel1;
+ r.out.handle = &printer_handle;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_spoolss_OpenPrinterEx_r(b, torture, &r),
+ "dcerpc_spoolss_OpenPrinterEx failed");
+ torture_assert_werr_ok(torture, r.out.result,
+ "dcerpc_spoolss_OpenPrinterEx failed");
+ }
+
+ {
+ int i;
+
+ for (i=0; i<8; i++) {
+ torture_assert(torture,
+ getprinterinfo(torture, b, &printer_handle, i, NULL),
+ talloc_asprintf(torture, "getprinterinfo %d failed", i));
+ }
+ }
+
+ {
+ struct spoolss_ClosePrinter r;
+
+ r.in.handle = &printer_handle;
+ r.out.handle = &printer_handle;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_spoolss_ClosePrinter_r(b, torture, &r),
+ "dcerpc_spoolss_ClosePrinter failed");
+ torture_assert_werr_ok(torture, r.out.result,
+ "dcerpc_spoolss_ClosePrinter failed");
+ }
+
+ {
+ int num_enumerated;
+
+ torture_assert(torture,
+ enumprinters(torture, b, servername, 1, &num_enumerated),
+ "enumprinters failed");
+
+ torture_assert_int_equal(torture, num_printers, num_enumerated,
+ "netshareenum / enumprinters lvl 1 numprinter mismatch");
+ }
+
+ {
+ int num_enumerated;
+
+ torture_assert(torture,
+ enumprinters(torture, b, servername, 2, &num_enumerated),
+ "enumprinters failed");
+
+ torture_assert_int_equal(torture, num_printers, num_enumerated,
+ "netshareenum / enumprinters lvl 2 numprinter mismatch");
+ }
+
+ return true;
+}
+
+static bool torture_samba3_rpc_wkssvc(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding_handle *b;
+ char *servername;
+
+ torture_assert(torture,
+ rap_get_servername(torture, &servername),
+ "failed to rap servername");
+
+ torture_assert_ntstatus_ok(torture,
+ torture_rpc_connection(torture, &p, &ndr_table_wkssvc),
+ "failed to setup wkssvc");
+
+ b = p->binding_handle;
+
+ {
+ 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;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_wkssvc_NetWkstaGetInfo_r(b, torture, &r),
+ "dcerpc_wkssvc_NetWksGetInfo failed");
+ torture_assert_werr_ok(torture, r.out.result,
+ "dcerpc_wkssvc_NetWksGetInfo failed");
+
+ torture_assert_str_equal(torture, servername, r.out.info->info100->server_name,
+ "servername RAP / DCERPC inconsistency");
+ }
+
+ return true;
+}
+
+static bool winreg_close(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct winreg_CloseKey c;
+
+ c.in.handle = c.out.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_CloseKey_r(b, tctx, &c),
+ "winreg_CloseKey failed");
+ torture_assert_werr_ok(tctx, c.out.result,
+ "winreg_CloseKey failed");
+
+ return true;
+}
+
+static bool enumvalues(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ uint32_t enum_index = 0;
+
+ while (1) {
+ struct winreg_EnumValue r;
+ struct winreg_ValNameBuf name;
+ enum winreg_Type type = 0;
+ uint8_t buf8[1024];
+ NTSTATUS status;
+ uint32_t size, length;
+
+ ZERO_STRUCT(buf8);
+ 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_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ return true;
+ }
+ enum_index += 1;
+ }
+}
+
+static bool enumkeys(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ int depth)
+{
+ struct winreg_EnumKey r;
+ struct winreg_StringBuf kclass, name;
+ NTSTATUS status;
+ NTTIME t = 0;
+
+ if (depth <= 0) {
+ return true;
+ }
+
+ kclass.name = "";
+ kclass.size = 1024;
+
+ r.in.handle = handle;
+ r.in.enum_index = 0;
+ r.in.name = &name;
+ r.in.keyclass = &kclass;
+ r.out.name = &name;
+ r.in.last_changed_time = &t;
+
+ do {
+ struct winreg_OpenKey o;
+ struct policy_handle key_handle;
+ int i;
+
+ name.name = NULL;
+ name.size = 1024;
+
+ status = dcerpc_winreg_EnumKey_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ /* We're done enumerating */
+ return true;
+ }
+
+ for (i=0; i<10-depth; i++) {
+ torture_comment(tctx, " ");
+ }
+ torture_comment(tctx, "%s\n", r.out.name->name);
+
+ o.in.parent_handle = handle;
+ o.in.keyname.name = r.out.name->name;
+ o.in.options = 0;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.handle = &key_handle;
+
+ status = dcerpc_winreg_OpenKey_r(b, tctx, &o);
+ if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(o.out.result)) {
+ enumkeys(tctx, b, &key_handle, depth-1);
+ enumvalues(tctx, b, &key_handle);
+ torture_assert(tctx, winreg_close(tctx, b, &key_handle), "");
+ }
+
+ r.in.enum_index += 1;
+ } while(true);
+
+ return true;
+}
+
+typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_binding_handle *, TALLOC_CTX *, void *);
+
+static bool test_Open3(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *name, winreg_open_fn open_fn)
+{
+ struct policy_handle handle;
+ struct winreg_OpenHKLM r;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ open_fn(b, tctx, &r),
+ talloc_asprintf(tctx, "%s failed", name));
+ torture_assert_werr_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "%s failed", name));
+
+ enumkeys(tctx, b, &handle, 4);
+
+ torture_assert(tctx,
+ winreg_close(tctx, b, &handle),
+ "dcerpc_CloseKey failed");
+
+ return true;
+}
+
+static bool torture_samba3_rpc_winreg(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding_handle *b;
+ bool ret = true;
+ struct {
+ const char *name;
+ winreg_open_fn fn;
+ } open_fns[] = {
+ {"OpenHKLM", (winreg_open_fn)dcerpc_winreg_OpenHKLM_r },
+ {"OpenHKU", (winreg_open_fn)dcerpc_winreg_OpenHKU_r },
+ {"OpenHKPD", (winreg_open_fn)dcerpc_winreg_OpenHKPD_r },
+ {"OpenHKPT", (winreg_open_fn)dcerpc_winreg_OpenHKPT_r },
+ {"OpenHKCR", (winreg_open_fn)dcerpc_winreg_OpenHKCR_r }};
+#if 0
+ int i;
+#endif
+
+ torture_assert_ntstatus_ok(torture,
+ torture_rpc_connection(torture, &p, &ndr_table_winreg),
+ "failed to setup winreg");
+
+ b = p->binding_handle;
+
+#if 1
+ ret = test_Open3(torture, b, open_fns[0].name, open_fns[0].fn);
+#else
+ for (i = 0; i < ARRAY_SIZE(open_fns); i++) {
+ if (!test_Open3(torture, b, open_fns[i].name, open_fns[i].fn))
+ ret = false;
+ }
+#endif
+ return ret;
+}
+
+static bool get_shareinfo(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ const char *share,
+ struct srvsvc_NetShareInfo502 **info502)
+{
+ struct srvsvc_NetShareGetInfo r;
+ union srvsvc_NetShareInfo info;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", servername);
+ r.in.share_name = share;
+ r.in.level = 502;
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_srvsvc_NetShareGetInfo_r(b, tctx, &r),
+ "srvsvc_NetShareGetInfo failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "srvsvc_NetShareGetInfo failed");
+
+ *info502 = talloc_move(tctx, &info.info502);
+
+ return true;
+}
+
+/*
+ * Get us a handle on HKLM\
+ */
+
+static bool get_hklm_handle(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct winreg_OpenHKLM r;
+ struct policy_handle result;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = &result;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_OpenHKLM_r(b, tctx, &r),
+ "OpenHKLM failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "OpenHKLM failed");
+
+ *handle = result;
+
+ return true;
+}
+
+static bool torture_samba3_createshare(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *sharename)
+{
+ struct policy_handle hklm;
+ struct policy_handle new_handle;
+ struct winreg_CreateKey c;
+ struct winreg_CloseKey cl;
+ enum winreg_CreateAction action_taken = REG_ACTION_NONE;
+
+ ZERO_STRUCT(c);
+ ZERO_STRUCT(cl);
+ ZERO_STRUCT(hklm);
+ ZERO_STRUCT(new_handle);
+
+ c.in.handle = &hklm;
+ c.in.name.name = talloc_asprintf(
+ tctx, "software\\samba\\smbconf\\%s", sharename);
+ torture_assert(tctx, c.in.name.name, "talloc_asprintf failed");
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_CreateKey_r(b, tctx, &c),
+ "OpenKey failed");
+ torture_assert_werr_ok(tctx, c.out.result,
+ "OpenKey failed");
+
+ cl.in.handle = &new_handle;
+ cl.out.handle = &new_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_CloseKey_r(b, tctx, &cl),
+ "CloseKey failed");
+ torture_assert_werr_ok(tctx, cl.out.result,
+ "CloseKey failed");
+
+ return true;
+}
+
+static bool torture_samba3_deleteshare(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *sharename)
+{
+ struct policy_handle hklm;
+ struct winreg_DeleteKey d;
+
+ torture_assert(tctx,
+ get_hklm_handle(tctx, b, &hklm),
+ "get_hklm_handle failed");
+
+ d.in.handle = &hklm;
+ d.in.key.name = talloc_asprintf(
+ tctx, "software\\samba\\smbconf\\%s", sharename);
+ torture_assert(tctx, d.in.key.name, "talloc_asprintf failed");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_DeleteKey_r(b, tctx, &d),
+ "DeleteKey failed");
+ torture_assert_werr_ok(tctx, d.out.result,
+ "DeleteKey failed");
+
+ return true;
+}
+
+static bool torture_samba3_setconfig(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *sharename,
+ const char *parameter,
+ const char *value)
+{
+ struct policy_handle hklm, key_handle;
+ struct winreg_OpenKey o;
+ struct winreg_SetValue s;
+ uint32_t type;
+ DATA_BLOB val;
+
+ torture_assert(tctx,
+ get_hklm_handle(tctx, b, &hklm),
+ "get_hklm_handle failed");
+
+ o.in.parent_handle = &hklm;
+ o.in.keyname.name = talloc_asprintf(
+ tctx, "software\\samba\\smbconf\\%s", sharename);
+ torture_assert(tctx, o.in.keyname.name, "talloc_asprintf failed");
+
+ o.in.options = 0;
+ o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ o.out.handle = &key_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_OpenKey_r(b, tctx, &o),
+ "OpenKey failed");
+ torture_assert_werr_ok(tctx, o.out.result,
+ "OpenKey failed");
+
+ torture_assert(tctx,
+ reg_string_to_val(tctx, "REG_SZ", value, &type, &val),
+ "reg_string_to_val failed");
+
+ s.in.handle = &key_handle;
+ s.in.name.name = parameter;
+ s.in.type = type;
+ s.in.data = val.data;
+ s.in.size = val.length;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_SetValue_r(b, tctx, &s),
+ "SetValue failed");
+ torture_assert_werr_ok(tctx, s.out.result,
+ "SetValue failed");
+
+ return true;
+}
+
+static bool torture_samba3_regconfig(struct torture_context *torture)
+{
+ struct srvsvc_NetShareInfo502 *i = NULL;
+ const char *comment = "Dummer Kommentar";
+ struct dcerpc_pipe *srvsvc_pipe, *winreg_pipe;
+
+ torture_assert_ntstatus_ok(torture,
+ torture_rpc_connection(torture, &srvsvc_pipe, &ndr_table_srvsvc),
+ "failed to setup srvsvc");
+
+ torture_assert_ntstatus_ok(torture,
+ torture_rpc_connection(torture, &winreg_pipe, &ndr_table_winreg),
+ "failed to setup winreg");
+
+ torture_assert(torture,
+ torture_samba3_createshare(torture, winreg_pipe->binding_handle, "blubber"),
+ "torture_samba3_createshare failed");
+
+ torture_assert(torture,
+ torture_samba3_setconfig(torture, winreg_pipe->binding_handle, "blubber", "comment", comment),
+ "torture_samba3_setconfig failed");
+
+ torture_assert(torture,
+ get_shareinfo(torture, srvsvc_pipe->binding_handle, dcerpc_server_name(srvsvc_pipe), "blubber", &i),
+ "get_shareinfo failed");
+
+ torture_assert_str_equal(torture, comment, i->comment,
+ "got unexpected comment");
+
+ torture_assert(torture,
+ torture_samba3_deleteshare(torture, winreg_pipe->binding_handle, "blubber"),
+ "torture_samba3_deleteshare failed");
+
+ return true;
+}
+
+/*
+ * Test that even with a result of 0 rids the array is returned as a
+ * non-NULL pointer. Yes, XP does notice.
+ */
+
+bool torture_samba3_getaliasmembership_0(struct torture_context *torture)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding_handle *b;
+ struct samr_Connect2 c;
+ struct samr_OpenDomain o;
+ struct dom_sid sid;
+ struct lsa_SidPtr ptr;
+ struct lsa_SidArray sids;
+ struct samr_GetAliasMembership g;
+ struct samr_Ids rids;
+ struct policy_handle samr, domain;
+
+ torture_assert_ntstatus_ok(torture,
+ torture_rpc_connection(torture, &p, &ndr_table_samr),
+ "failed to setup samr");
+
+ b = p->binding_handle;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = SAMR_ACCESS_LOOKUP_DOMAIN;
+ c.out.connect_handle = &samr;
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_samr_Connect2_r(b, torture, &c),
+ "");
+ torture_assert_ntstatus_ok(torture, c.out.result,
+ "");
+ dom_sid_parse("S-1-5-32", &sid);
+ o.in.connect_handle = &samr;
+ o.in.access_mask = SAMR_DOMAIN_ACCESS_LOOKUP_ALIAS;
+ o.in.sid = &sid;
+ o.out.domain_handle = &domain;
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_samr_OpenDomain_r(b, torture, &o),
+ "");
+ torture_assert_ntstatus_ok(torture, o.out.result,
+ "");
+ dom_sid_parse("S-1-2-3-4-5", &sid);
+ ptr.sid = &sid;
+ sids.num_sids = 1;
+ sids.sids = &ptr;
+ g.in.domain_handle = &domain;
+ g.in.sids = &sids;
+ g.out.rids = &rids;
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_samr_GetAliasMembership_r(b, torture, &g),
+ "");
+ torture_assert_ntstatus_ok(torture, g.out.result,
+ "");
+ if (rids.ids == NULL) {
+ /* This is the piece to test here */
+ torture_fail(torture,
+ "torture_samba3_getaliasmembership_0: "
+ "Server returns NULL rids array\n");
+ }
+
+ return true;
+}
+
+/**
+ * Test smb reauthentication while rpc pipe is in use.
+ */
+static bool torture_rpc_smb_reauth1(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_state *cli;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+
+ struct dcerpc_pipe *lsa_pipe;
+ struct dcerpc_binding_handle *lsa_handle;
+ struct lsa_GetUserName r;
+ struct lsa_String *authority_name_p = NULL;
+ char *authority_name_saved = NULL;
+ struct lsa_String *account_name_p = NULL;
+ char *account_name_saved = NULL;
+ struct cli_credentials *anon_creds = NULL;
+ struct smb_composite_sesssetup io;
+
+ mem_ctx = talloc_init("torture_samba3_reauth");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+ lpcfg_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lpcfg_socket_options(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smbcli_full_connection failed");
+
+ status = pipe_bind_smb(torture, mem_ctx, cli->tree, "\\lsarpc",
+ &ndr_table_lsarpc, &lsa_pipe);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "pipe_bind_smb failed");
+ lsa_handle = lsa_pipe->binding_handle;
+
+ /* lsa getusername */
+
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_comment(torture, "lsa_GetUserName gave '%s\\%s'\n",
+ authority_name_p->string,
+ account_name_p->string);
+
+ account_name_saved = talloc_strdup(mem_ctx, account_name_p->string);
+ torture_assert_goto(torture, (account_name_saved != NULL), ret, done,
+ "talloc failed");
+ authority_name_saved = talloc_strdup(mem_ctx, authority_name_p->string);
+ torture_assert_goto(torture, (authority_name_saved != NULL), ret, done,
+ "talloc failed");
+
+ /* smb re-authenticate as anonymous */
+
+ anon_creds = cli_credentials_init_anon(mem_ctx);
+
+ ZERO_STRUCT(io);
+ io.in.sesskey = cli->transport->negotiate.sesskey;
+ io.in.capabilities = cli->transport->negotiate.capabilities;
+ io.in.credentials = anon_creds;
+ io.in.workgroup = lpcfg_workgroup(torture->lp_ctx);
+ io.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx);
+
+ status = smb_composite_sesssetup(cli->session, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "session reauth to anon failed");
+
+ /* re-do lsa getusername after reauth */
+
+ TALLOC_FREE(authority_name_p);
+ TALLOC_FREE(account_name_p);
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0),
+ ret, done, "authority_name not equal after reauth to anon");
+ torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0),
+ ret, done, "account_name not equal after reauth to anon");
+
+ /* smb re-auth again to the original user */
+
+ ZERO_STRUCT(io);
+ io.in.sesskey = cli->transport->negotiate.sesskey;
+ io.in.capabilities = cli->transport->negotiate.capabilities;
+ io.in.credentials = samba_cmdline_get_creds();
+ io.in.workgroup = lpcfg_workgroup(torture->lp_ctx);
+ io.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx);
+
+ status = smb_composite_sesssetup(cli->session, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "session reauth to anon failed");
+
+ /* re-do lsa getusername */
+
+ TALLOC_FREE(authority_name_p);
+ TALLOC_FREE(account_name_p);
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0),
+ ret, done, "authority_name not equal after reauth to anon");
+ torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0),
+ ret, done, "account_name not equal after reauth to anon");
+
+ ret = true;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * Test smb reauthentication while rpc pipe is in use.
+ * Open a second lsa bind after reauth to anon.
+ * Do lsa getusername on that second bind.
+ */
+static bool torture_rpc_smb_reauth2(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_state *cli;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+
+ struct dcerpc_pipe *lsa_pipe;
+ struct dcerpc_binding_handle *lsa_handle;
+ struct lsa_GetUserName r;
+ struct lsa_String *authority_name_p = NULL;
+ char *authority_name_saved = NULL;
+ struct lsa_String *account_name_p = NULL;
+ char *account_name_saved = NULL;
+ struct cli_credentials *anon_creds = NULL;
+ struct smb_composite_sesssetup io;
+
+ mem_ctx = talloc_init("torture_samba3_reauth");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+ lpcfg_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lpcfg_socket_options(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smbcli_full_connection failed");
+
+ /* smb re-authenticate as anonymous */
+
+ anon_creds = cli_credentials_init_anon(mem_ctx);
+
+ ZERO_STRUCT(io);
+ io.in.sesskey = cli->transport->negotiate.sesskey;
+ io.in.capabilities = cli->transport->negotiate.capabilities;
+ io.in.credentials = anon_creds;
+ io.in.workgroup = lpcfg_workgroup(torture->lp_ctx);
+ io.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx);
+
+ status = smb_composite_sesssetup(cli->session, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "session reauth to anon failed");
+
+ /* open the lsa pipe */
+
+ status = pipe_bind_smb(torture, mem_ctx, cli->tree, "\\lsarpc",
+ &ndr_table_lsarpc, &lsa_pipe);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "pipe_bind_smb failed");
+ lsa_handle = lsa_pipe->binding_handle;
+
+ /* lsa getusername */
+
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_comment(torture, "lsa_GetUserName gave '%s\\%s'\n",
+ authority_name_p->string,
+ account_name_p->string);
+
+ account_name_saved = talloc_strdup(mem_ctx, account_name_p->string);
+ torture_assert_goto(torture, (account_name_saved != NULL), ret, done,
+ "talloc failed");
+ authority_name_saved = talloc_strdup(mem_ctx, authority_name_p->string);
+ torture_assert_goto(torture, (authority_name_saved != NULL), ret, done,
+ "talloc failed");
+
+ /* smb re-auth again to the original user */
+
+ ZERO_STRUCT(io);
+ io.in.sesskey = cli->transport->negotiate.sesskey;
+ io.in.capabilities = cli->transport->negotiate.capabilities;
+ io.in.credentials = samba_cmdline_get_creds();
+ io.in.workgroup = lpcfg_workgroup(torture->lp_ctx);
+ io.in.gensec_settings = lpcfg_gensec_settings(torture, torture->lp_ctx);
+
+ status = smb_composite_sesssetup(cli->session, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "session reauth to anon failed");
+
+ /* re-do lsa getusername after reauth */
+
+ TALLOC_FREE(authority_name_p);
+ TALLOC_FREE(account_name_p);
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0),
+ ret, done, "authority_name not equal after reauth to anon");
+ torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0),
+ ret, done, "account_name not equal after reauth to anon");
+
+ ret = true;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * Test smb2 reauthentication while rpc pipe is in use.
+ */
+static bool torture_rpc_smb2_reauth1(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_options options;
+
+ struct dcerpc_pipe *lsa_pipe;
+ struct dcerpc_binding_handle *lsa_handle;
+ struct lsa_GetUserName r;
+ struct lsa_String *authority_name_p = NULL;
+ char *authority_name_saved = NULL;
+ struct lsa_String *account_name_p = NULL;
+ char *account_name_saved = NULL;
+ struct cli_credentials *anon_creds = NULL;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ struct smb2_tree *tree;
+
+ mem_ctx = talloc_init("torture_samba3_reauth");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ host,
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$",
+ lpcfg_resolve_context(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ torture->ev,
+ &options,
+ lpcfg_socket_options(torture->lp_ctx),
+ lpcfg_gensec_settings(torture, torture->lp_ctx)
+ );
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_connect failed");
+
+ status = pipe_bind_smb2(torture, mem_ctx, tree, "lsarpc",
+ &ndr_table_lsarpc, &lsa_pipe);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "pipe_bind_smb2 failed");
+ lsa_handle = lsa_pipe->binding_handle;
+
+ /* lsa getusername */
+
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_comment(torture, "lsa_GetUserName gave '%s\\%s'\n",
+ authority_name_p->string,
+ account_name_p->string);
+
+ account_name_saved = talloc_strdup(mem_ctx, account_name_p->string);
+ torture_assert_goto(torture, (account_name_saved != NULL), ret, done,
+ "talloc failed");
+ authority_name_saved = talloc_strdup(mem_ctx, authority_name_p->string);
+ torture_assert_goto(torture, (authority_name_saved != NULL), ret, done,
+ "talloc failed");
+
+ /* smb re-authenticate as anonymous */
+
+ anon_creds = cli_credentials_init_anon(mem_ctx);
+
+ status = smb2_session_setup_spnego(tree->session,
+ anon_creds,
+ 0 /* previous_session_id */);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "session reauth to anon failed");
+
+ /* re-do lsa getusername after reauth */
+
+ TALLOC_FREE(authority_name_p);
+ TALLOC_FREE(account_name_p);
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0),
+ ret, done, "authority_name not equal after reauth to anon");
+ torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0),
+ ret, done, "account_name not equal after reauth to anon");
+
+ /* smb re-auth again to the original user */
+
+ status = smb2_session_setup_spnego(tree->session,
+ samba_cmdline_get_creds(),
+ 0 /* previous_session_id */);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "session reauth to anon failed");
+
+ /* re-do lsa getusername */
+
+ TALLOC_FREE(authority_name_p);
+ TALLOC_FREE(account_name_p);
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0),
+ ret, done, "authority_name not equal after reauth to anon");
+ torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0),
+ ret, done, "account_name not equal after reauth to anon");
+
+ ret = true;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * Test smb2 reauthentication while rpc pipe is in use.
+ * Open a second lsa bind after reauth to anon.
+ * Do lsa getusername on that second bind.
+ */
+static bool torture_rpc_smb2_reauth2(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_options options;
+
+ struct dcerpc_pipe *lsa_pipe;
+ struct dcerpc_binding_handle *lsa_handle;
+ struct lsa_GetUserName r;
+ struct lsa_String *authority_name_p = NULL;
+ char *authority_name_saved = NULL;
+ struct lsa_String *account_name_p = NULL;
+ char *account_name_saved = NULL;
+ struct cli_credentials *anon_creds = NULL;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ struct smb2_tree *tree;
+
+ mem_ctx = talloc_init("torture_samba3_reauth");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ host,
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$",
+ lpcfg_resolve_context(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ torture->ev,
+ &options,
+ lpcfg_socket_options(torture->lp_ctx),
+ lpcfg_gensec_settings(torture, torture->lp_ctx)
+ );
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_connect failed");
+
+ /* smb re-authenticate as anonymous */
+
+ anon_creds = cli_credentials_init_anon(mem_ctx);
+
+ status = smb2_session_setup_spnego(tree->session,
+ anon_creds,
+ 0 /* previous_session_id */);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "session reauth to anon failed");
+
+ /* open the lsa pipe */
+
+ status = pipe_bind_smb2(torture, mem_ctx, tree, "lsarpc",
+ &ndr_table_lsarpc, &lsa_pipe);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "pipe_bind_smb2 failed");
+ lsa_handle = lsa_pipe->binding_handle;
+
+ /* lsa getusername */
+
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_comment(torture, "lsa_GetUserName gave '%s\\%s'\n",
+ authority_name_p->string,
+ account_name_p->string);
+
+ account_name_saved = talloc_strdup(mem_ctx, account_name_p->string);
+ torture_assert_goto(torture, (account_name_saved != NULL), ret, done,
+ "talloc failed");
+ authority_name_saved = talloc_strdup(mem_ctx, authority_name_p->string);
+ torture_assert_goto(torture, (authority_name_saved != NULL), ret, done,
+ "talloc failed");
+
+ /* smb re-auth again to the original user */
+
+ status = smb2_session_setup_spnego(tree->session,
+ samba_cmdline_get_creds(),
+ 0 /* previous_session_id */);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "session reauth to anon failed");
+
+ /* re-do lsa getusername */
+
+ TALLOC_FREE(authority_name_p);
+ TALLOC_FREE(account_name_p);
+ ZERO_STRUCT(r);
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ status = dcerpc_lsa_GetUserName_r(lsa_handle, mem_ctx, &r);
+
+ authority_name_p = *r.out.authority_name;
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "GetUserName failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result, ret, done,
+ "GetUserName failed");
+
+ torture_assert_goto(torture, (strcmp(authority_name_p->string, authority_name_saved) == 0),
+ ret, done, "authority_name not equal after reauth to anon");
+ torture_assert_goto(torture, (strcmp(account_name_p->string, account_name_saved) == 0),
+ ret, done, "account_name not equal after reauth to anon");
+
+ ret = true;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool torture_rpc_smb1_pipe_name(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_state *cli;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+ union smb_open io;
+ union smb_close cl;
+ uint16_t fnum;
+
+ mem_ctx = talloc_init("torture_samba3_smb1_pipe_name");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+ lpcfg_smbcli_session_options(torture->lp_ctx, &session_options);
+
+ status = smbcli_full_connection(mem_ctx, &cli,
+ torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$", NULL,
+ lpcfg_socket_options(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(torture->lp_ctx),
+ torture->ev, &options, &session_options,
+ lpcfg_gensec_settings(torture, torture->lp_ctx));
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smbcli_full_connection failed");
+
+ ZERO_STRUCT(io);
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.access_mask = DESIRED_ACCESS_PIPE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION;
+ io.ntcreatex.in.security_flags = 0;
+
+ io.ntcreatex.in.fname = "__none__";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb_raw_open for '__none__'");
+
+ io.ntcreatex.in.fname = "pipe\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb_raw_open for 'pipe\\srvsvc'");
+
+ io.ntcreatex.in.fname = "\\pipe\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb_raw_open for '\\pipe\\srvsvc'");
+
+ io.ntcreatex.in.fname = "srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for 'srvsvc'");
+ fnum = io.ntcreatex.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+
+ io.ntcreatex.in.fname = "\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for '\\srvsvc'");
+ fnum = io.ntcreatex.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+
+ io.ntcreatex.in.fname = "\\\\\\\\\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for '\\\\\\\\\\srvsvc'");
+ fnum = io.ntcreatex.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+
+ ZERO_STRUCT(io);
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.nttrans.in.access_mask = DESIRED_ACCESS_PIPE;
+ io.nttrans.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.nttrans.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.nttrans.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION;
+ io.nttrans.in.security_flags = 0;
+
+ io.nttrans.in.fname = "__none__";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb_raw_open for '__none__'");
+
+ io.nttrans.in.fname = "pipe\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb_raw_open for 'pipe\\srvsvc'");
+
+ io.nttrans.in.fname = "\\pipe\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb_raw_open for '\\pipe\\srvsvc'");
+
+ io.nttrans.in.fname = "srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for 'srvsvc'");
+ fnum = io.nttrans.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+
+ io.nttrans.in.fname = "\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for '\\srvsvc'");
+ fnum = io.nttrans.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+
+ io.nttrans.in.fname = "\\\\\\\\\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for '\\\\\\\\\\srvsvc'");
+ fnum = io.nttrans.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+
+ ZERO_STRUCT(io);
+ io.generic.level = RAW_OPEN_OPENX;
+ io.openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
+ io.openx.in.open_func = OPENX_OPEN_FUNC_OPEN;
+
+ io.openx.in.fname = "__none__";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
+ ret, done,
+ "smb_raw_open for '__none__'");
+
+ io.openx.in.fname = "srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
+ ret, done,
+ "smb_raw_open for 'srvsvc'");
+
+ io.openx.in.fname = "\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
+ ret, done,
+ "smb_raw_open for '\\srvsvc'");
+
+ io.openx.in.fname = "\\pipesrvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
+ ret, done,
+ "smb_raw_open for '\\pipesrvsvc'");
+
+ io.openx.in.fname = "pipe\\__none__";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb_raw_open for 'pipe\\__none__'");
+
+ io.openx.in.fname = "\\pipe\\__none__";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb_raw_open for '\\pipe\\__none__'");
+
+ io.openx.in.fname = "pipe\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for 'pipe\\srvsvc'");
+ fnum = io.openx.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+
+ io.openx.in.fname = "\\pipe\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for '\\pipe\\srvsvc'");
+ fnum = io.openx.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+
+ io.openx.in.fname = "\\\\\\\\\\pipe\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for '\\\\\\\\\\pipe\\srvsvc'");
+ fnum = io.openx.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+
+ io.openx.in.fname = "\\\\\\\\\\pipe\\\\\\\\\\srvsvc";
+ status = smb_raw_open(cli->tree, torture, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for '\\\\\\\\\\pipe\\\\\\\\\\srvsvc'");
+ fnum = io.openx.out.file.fnum;
+ ZERO_STRUCT(cl);
+ cl.generic.level = RAW_CLOSE_CLOSE;
+ cl.close.in.file.fnum = fnum;
+ status = smb_raw_close(cli->tree, &cl);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb_raw_close failed");
+ ret = true;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool torture_rpc_smb2_pipe_name(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_options options;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ struct smb2_tree *tree;
+ struct smb2_handle h;
+ struct smb2_create io;
+
+ mem_ctx = talloc_init("torture_samba3_smb2_pipe_name");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ host,
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$",
+ lpcfg_resolve_context(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ torture->ev,
+ &options,
+ lpcfg_socket_options(torture->lp_ctx),
+ lpcfg_gensec_settings(torture, torture->lp_ctx)
+ );
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_connect failed");
+
+ ZERO_STRUCT(io);
+ io.in.oplock_level = 0;
+ io.in.desired_access = DESIRED_ACCESS_PIPE;
+ io.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+ io.in.file_attributes = 0;
+ io.in.create_disposition = NTCREATEX_DISP_OPEN;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = 0;
+
+ io.in.fname = "__none__";
+ status = smb2_create(tree, tree, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb2_create for '__none__'");
+
+ io.in.fname = "\\srvsvc";
+ status = smb2_create(tree, tree, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb2_create for '\\srvsvc'");
+
+ io.in.fname = "\\pipe\\srvsvc";
+ status = smb2_create(tree, tree, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb2_create for '\\pipe\\srvsvc'");
+
+ io.in.fname = "pipe\\srvsvc";
+ status = smb2_create(tree, tree, &io);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ ret, done,
+ "smb2_create for 'pipe\\srvsvc'");
+
+ io.in.fname = "srvsvc";
+ status = smb2_create(tree, tree, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for 'srvsvc'");
+
+ h = io.out.file.handle;
+
+ status = smb2_util_close(tree, h);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_util_close failed");
+
+ ret = true;
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * Test behaviour of a waiting read call on a pipe when
+ * the pipe handle is closed:
+ * - open a pipe via smb2
+ * - trigger a read which hangs since there is nothing to read
+ * - close the pipe file handle
+ * - wait for the read to return and check the status
+ * (STATUS_PIPE_BROKEN)
+ */
+static bool torture_rpc_smb2_pipe_read_close(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_options options;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ struct smb2_tree *tree;
+ struct smb2_handle h;
+ struct smb2_request *smb2req;
+ struct smb2_create io;
+ struct smb2_read rd;
+
+ mem_ctx = talloc_init("torture_samba3_pipe_read_close");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ host,
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$",
+ lpcfg_resolve_context(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ torture->ev,
+ &options,
+ lpcfg_socket_options(torture->lp_ctx),
+ lpcfg_gensec_settings(torture, torture->lp_ctx)
+ );
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_connect failed");
+
+ ZERO_STRUCT(io);
+ io.in.oplock_level = 0;
+ io.in.desired_access = DESIRED_ACCESS_PIPE;
+ io.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+ io.in.file_attributes = 0;
+ io.in.create_disposition = NTCREATEX_DISP_OPEN;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = 0;
+ io.in.fname = "lsarpc";
+
+ status = smb2_create(tree, tree, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for 'lsarpc'");
+
+ h = io.out.file.handle;
+
+ ZERO_STRUCT(rd);
+ rd.in.file.handle = h;
+ rd.in.length = 1024;
+ rd.in.offset = 0;
+ rd.in.min_count = 0;
+
+ smb2req = smb2_read_send(tree, &rd);
+ torture_assert_goto(torture, (smb2req != NULL), ret, done,
+ "smb2_read_send failed");
+
+ status = smb2_util_close(tree, h);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_util_close failed");
+
+ status = smb2_read_recv(smb2req, mem_ctx, &rd);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_PIPE_BROKEN, ret, done,
+ "smb2_read_recv: unexpected return code");
+
+ ret = true;
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * Test behaviour of a waiting read call on a pipe when
+ * the tree is disconnected.
+ * - open a pipe via smb2
+ * - trigger a read which hangs since there is nothing to read
+ * - do a tree disconnect
+ * - wait for the read to return and check the status
+ * (STATUS_PIPE_BROKEN)
+ */
+static bool torture_rpc_smb2_pipe_read_tdis(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_options options;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ struct smb2_tree *tree;
+ struct smb2_handle h;
+ struct smb2_request *smb2req;
+ struct smb2_create io;
+ struct smb2_read rd;
+
+ mem_ctx = talloc_init("torture_samba3_pipe_read_tdis");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ host,
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$",
+ lpcfg_resolve_context(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ torture->ev,
+ &options,
+ lpcfg_socket_options(torture->lp_ctx),
+ lpcfg_gensec_settings(torture, torture->lp_ctx)
+ );
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_connect failed");
+
+ ZERO_STRUCT(io);
+ io.in.oplock_level = 0;
+ io.in.desired_access = DESIRED_ACCESS_PIPE;
+ io.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+ io.in.file_attributes = 0;
+ io.in.create_disposition = NTCREATEX_DISP_OPEN;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = 0;
+ io.in.fname = "lsarpc";
+
+ status = smb2_create(tree, tree, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for 'lsarpc'");
+
+ h = io.out.file.handle;
+
+ ZERO_STRUCT(rd);
+ rd.in.file.handle = h;
+ rd.in.length = 1024;
+ rd.in.offset = 0;
+ rd.in.min_count = 0;
+
+ smb2req = smb2_read_send(tree, &rd);
+ torture_assert_goto(torture, (smb2req != NULL), ret, done,
+ "smb2_read_send failed");
+
+ status = smb2_tdis(tree);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_tdis failed");
+
+ status = smb2_read_recv(smb2req, mem_ctx, &rd);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_PIPE_BROKEN, ret, done,
+ "smb2_read_recv: unexpected return code");
+
+ ret = true;
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/**
+ * Test behaviour of a waiting read call on a pipe when
+ * the user logs off
+ * - open a pipe via smb2
+ * - trigger a read which hangs since there is nothing to read
+ * - do a logoff
+ * - wait for the read to return and check the status
+ * (STATUS_PIPE_BROKEN)
+ */
+static bool torture_rpc_smb2_pipe_read_logoff(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_options options;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ struct smb2_tree *tree;
+ struct smb2_handle h;
+ struct smb2_request *smb2req;
+ struct smb2_create io;
+ struct smb2_read rd;
+
+ mem_ctx = talloc_init("torture_samba3_pipe_read_tdis");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ host,
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$",
+ lpcfg_resolve_context(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ torture->ev,
+ &options,
+ lpcfg_socket_options(torture->lp_ctx),
+ lpcfg_gensec_settings(torture, torture->lp_ctx)
+ );
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_connect failed");
+
+ ZERO_STRUCT(io);
+ io.in.oplock_level = 0;
+ io.in.desired_access = DESIRED_ACCESS_PIPE;
+ io.in.impersonation_level = SMB2_IMPERSONATION_IMPERSONATION;
+ io.in.file_attributes = 0;
+ io.in.create_disposition = NTCREATEX_DISP_OPEN;
+ io.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.in.create_options = 0;
+ io.in.fname = "lsarpc";
+
+ status = smb2_create(tree, tree, &io);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_create failed for 'lsarpc'");
+
+ h = io.out.file.handle;
+
+ ZERO_STRUCT(rd);
+ rd.in.file.handle = h;
+ rd.in.length = 1024;
+ rd.in.offset = 0;
+ rd.in.min_count = 0;
+
+ smb2req = smb2_read_send(tree, &rd);
+ torture_assert_goto(torture, (smb2req != NULL), ret, done,
+ "smb2_read_send failed");
+
+ status = smb2_logoff(tree->session);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_logoff failed");
+
+ status = smb2_read_recv(smb2req, mem_ctx, &rd);
+ torture_assert_ntstatus_equal_goto(torture, status, NT_STATUS_PIPE_BROKEN, ret, done,
+ "smb2_read_recv: unexpected return code");
+
+ ret = true;
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool torture_rpc_lsa_over_netlogon(struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_options options;
+ struct smb2_tree *tree;
+ struct dcerpc_pipe *netlogon_pipe;
+ struct dcerpc_binding_handle *lsa_handle;
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 o;
+ struct policy_handle handle;
+
+ torture_comment(torture, "Testing if we can access LSA server over "
+ "\\\\pipe\\netlogon rather than \\\\pipe\\lsarpc\n");
+
+ mem_ctx = talloc_init("torture_samba3_rpc_lsa_over_netlogon");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$",
+ lpcfg_resolve_context(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ torture->ev,
+ &options,
+ lpcfg_socket_options(torture->lp_ctx),
+ lpcfg_gensec_settings(torture, torture->lp_ctx)
+ );
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_connect failed");
+
+ status = pipe_bind_smb2(torture, mem_ctx, tree, "netlogon",
+ &ndr_table_lsarpc, &netlogon_pipe);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "pipe_bind_smb2 failed");
+ lsa_handle = netlogon_pipe->binding_handle;
+
+ 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 = &handle;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_lsa_OpenPolicy2_r(lsa_handle, torture, &o),
+ "OpenPolicy2 failed");
+ torture_assert_ntstatus_ok(torture,
+ o.out.result,
+ "OpenPolicy2 failed");
+
+ ret = true;
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool torture_rpc_pipes_supported_interfaces(
+ struct torture_context *torture)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ bool ret = false;
+ struct smbcli_options options;
+ struct smb2_tree *tree;
+ struct dcerpc_pipe *pipe1;
+ struct dcerpc_pipe *pipe2;
+ struct dcerpc_pipe *pipe3;
+
+ torture_comment(torture, "Testing only appropriate interfaces are "
+ "available in smb pipes\n");
+
+ mem_ctx = talloc_init("torture_samba3_rpc_pipes_supported_interfaces");
+ torture_assert(torture, (mem_ctx != NULL), "talloc_init failed");
+
+ lpcfg_smbcli_options(torture->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ torture_setting_string(torture, "host", NULL),
+ lpcfg_smb_ports(torture->lp_ctx),
+ "IPC$",
+ lpcfg_resolve_context(torture->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ torture->ev,
+ &options,
+ lpcfg_socket_options(torture->lp_ctx),
+ lpcfg_gensec_settings(torture, torture->lp_ctx)
+ );
+ torture_assert_ntstatus_ok_goto(torture, status, ret, done,
+ "smb2_connect failed");
+
+ /* Test embedded services pipes. The svcctl interface is
+ * not available if we open the winreg pipe. */
+ status = pipe_bind_smb2(torture, mem_ctx, tree, "winreg",
+ &ndr_table_svcctl, &pipe1);
+ torture_assert_ntstatus_equal(torture,
+ status,
+ NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX,
+ "svcctl interface not supported in winreg pipe");
+
+ /* Test it is not possible to bind to S4 server provided services */
+ status = pipe_bind_smb2(torture, mem_ctx, tree, "srvsvc",
+ &ndr_table_samr, &pipe2);
+ torture_assert_ntstatus_equal(torture,
+ status,
+ NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX,
+ "samr interface not supported in srvsvc pipe");
+
+ /* Test pipes in forked daemons like lsassd. The lsarpc interface is
+ * not available if we open the SAMR pipe. */
+ status = pipe_bind_smb2(torture, mem_ctx, tree, "samr",
+ &ndr_table_lsarpc, &pipe3);
+ torture_assert_ntstatus_equal(torture,
+ status,
+ NT_STATUS_RPC_UNSUPPORTED_NAME_SYNTAX,
+ "lsarpc interface not supported in samr pipe");
+
+ ret = true;
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+struct torture_suite *torture_rpc_samba3(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "samba3");
+
+ torture_suite_add_simple_test(suite, "bind", torture_bind_samba3);
+ torture_suite_add_simple_test(suite, "netlogon", torture_netlogon_samba3);
+ torture_suite_add_simple_test(suite, "sessionkey", torture_samba3_sessionkey);
+ torture_suite_add_simple_test(suite, "srvsvc", torture_samba3_rpc_srvsvc);
+ torture_suite_add_simple_test(suite, "sharesec", torture_samba3_rpc_sharesec);
+ torture_suite_add_simple_test(suite, "getusername", torture_samba3_rpc_getusername);
+ torture_suite_add_simple_test(suite, "randomauth2", torture_samba3_rpc_randomauth2);
+ torture_suite_add_simple_test(suite, "lsa", torture_samba3_rpc_lsa);
+ torture_suite_add_simple_test(suite, "spoolss", torture_samba3_rpc_spoolss);
+ torture_suite_add_simple_test(suite, "wkssvc", torture_samba3_rpc_wkssvc);
+ torture_suite_add_simple_test(suite, "winreg", torture_samba3_rpc_winreg);
+ torture_suite_add_simple_test(suite, "getaliasmembership-0", torture_samba3_getaliasmembership_0);
+ torture_suite_add_simple_test(suite, "regconfig", torture_samba3_regconfig);
+ torture_suite_add_simple_test(suite, "smb-reauth1", torture_rpc_smb_reauth1);
+ torture_suite_add_simple_test(suite, "smb-reauth2", torture_rpc_smb_reauth2);
+ torture_suite_add_simple_test(suite, "smb2-reauth1", torture_rpc_smb2_reauth1);
+ torture_suite_add_simple_test(suite, "smb2-reauth2", torture_rpc_smb2_reauth2);
+ torture_suite_add_simple_test(suite, "smb1-pipe-name", torture_rpc_smb1_pipe_name);
+ torture_suite_add_simple_test(suite, "smb2-pipe-name", torture_rpc_smb2_pipe_name);
+ torture_suite_add_simple_test(suite, "smb2-pipe-read-close", torture_rpc_smb2_pipe_read_close);
+ torture_suite_add_simple_test(suite, "smb2-pipe-read-tdis", torture_rpc_smb2_pipe_read_tdis);
+ torture_suite_add_simple_test(suite, "smb2-pipe-read-logoff", torture_rpc_smb2_pipe_read_logoff);
+ torture_suite_add_simple_test(suite,
+ "lsa_over_netlogon",
+ torture_rpc_lsa_over_netlogon);
+ torture_suite_add_simple_test(suite,
+ "pipes_supported_interfaces",
+ torture_rpc_pipes_supported_interfaces);
+
+ suite->description = talloc_strdup(suite, "samba3 DCERPC interface tests");
+
+ return suite;
+}
diff --git a/source4/torture/rpc/samlogon.c b/source4/torture/rpc/samlogon.c
new file mode 100644
index 0000000..00a87ba
--- /dev/null
+++ b/source4/torture/rpc/samlogon.c
@@ -0,0 +1,2121 @@
+/*
+ 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 "lib/cmdline/cmdline.h"
+#include "torture/rpc/torture_rpc.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/auth/libcli_auth.h"
+#include "param/param.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.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;
+ struct torture_context *tctx;
+ const char *comment;
+ const char *account_name;
+ const char *account_domain;
+ const char *netbios_name;
+ const char *password;
+ const char *workgroup;
+ 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 netlogon_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;
+};
+
+/*
+ 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 = 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);
+ netlogon_creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth);
+
+ r->out.return_authenticator = NULL;
+ status = dcerpc_netr_LogonSamLogon_r(samlogon_state->p->binding_handle,
+ samlogon_state->mem_ctx, r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+ if (!r->out.return_authenticator ||
+ !netlogon_creds_client_check(samlogon_state->creds, &r->out.return_authenticator->cred)) {
+ torture_comment(samlogon_state->tctx, "Credential chaining failed\n");
+ }
+ if (!NT_STATUS_IS_OK(r->out.result)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(r->out.result));
+ }
+ return r->out.result;
+ }
+
+ validation_level = r->in.validation_level;
+
+ status = netlogon_creds_decrypt_samlogon_validation(samlogon_state->creds,
+ validation_level,
+ r->out.validation);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+
+ 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_r(samlogon_state->p->binding_handle,
+ samlogon_state->mem_ctx, r_ex);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(r_ex->out.result)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(r_ex->out.result));
+ }
+ return r_ex->out.result;
+ }
+
+ validation_level = r_ex->in.validation_level;
+
+ status = netlogon_creds_decrypt_samlogon_validation(samlogon_state->creds,
+ validation_level,
+ r_ex->out.validation);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+
+ 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);
+ netlogon_creds_client_authenticator(samlogon_state->creds, &samlogon_state->auth);
+
+ r_flags->out.return_authenticator = NULL;
+ status = dcerpc_netr_LogonSamLogonWithFlags_r(samlogon_state->p->binding_handle,
+ samlogon_state->mem_ctx, r_flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+ if (!r_flags->out.return_authenticator ||
+ !netlogon_creds_client_check(samlogon_state->creds, &r_flags->out.return_authenticator->cred)) {
+ torture_comment(samlogon_state->tctx, "Credential chaining failed\n");
+ }
+ if (!NT_STATUS_IS_OK(r_flags->out.result)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(r_flags->out.result));
+ }
+ return r_flags->out.result;
+ }
+
+ validation_level = r_flags->in.validation_level;
+
+ status = netlogon_creds_decrypt_samlogon_validation(samlogon_state->creds,
+ validation_level,
+ r_flags->out.validation);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (error_string) {
+ *error_string = strdup(nt_errstr(status));
+ }
+ return status;
+ }
+
+ 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) {
+ torture_comment(samlogon_state->tctx, "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 modern servers, the LM password is invalid */
+ if (break_which == NO_NT
+ && !torture_setting_bool(samlogon_state->tctx, "samba3", false)) {
+ 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)) {
+ int ret;
+
+ SAFE_FREE(*error_string);
+ ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ if (ret == -1) {
+ *error_string = NULL;
+ }
+ 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;
+ }
+
+ /* for modern servers, the LM password is invalid */
+ if (break_which == NO_NT
+ && !torture_setting_bool(samlogon_state->tctx, "samba3", false)) {
+ *error_string = strdup("LM password is OK but should have failed against a modern server");
+ return false;
+ }
+
+ if (!all_zero(lm_key, sizeof(lm_key)) != 0) {
+ torture_comment(samlogon_state->tctx, "LM Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "lm_key:\n");
+ dump_data(1, lm_key, 8);
+ torture_comment(samlogon_state->tctx, "expected (all zeros):\n");
+ pass = false;
+ }
+
+ switch (break_which) {
+ case NO_NT:
+ {
+ uint8_t lm_key_expected[16];
+ memcpy(lm_key_expected, session_key.data, 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 session key)!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ torture_comment(samlogon_state->tctx, "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");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ torture_comment(samlogon_state->tctx, "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)) {
+ int ret;
+
+ SAFE_FREE(*error_string);
+ ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ if (ret == -1) {
+ *error_string = NULL;
+ }
+ 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 (torture_setting_bool(samlogon_state->tctx, "samba4", false)) {
+ if (!all_zero(lm_key, sizeof(lm_key)) != 0) {
+ torture_comment(samlogon_state->tctx, "LM Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "lm_key:\n");
+ dump_data(1, lm_key, 8);
+ torture_comment(samlogon_state->tctx, "expected (all zeros):\n");
+ pass = false;
+ }
+
+
+ if (!all_zero(user_session_key, sizeof(user_session_key)) != 0) {
+ torture_comment(samlogon_state->tctx, "NT Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ torture_comment(samlogon_state->tctx, "expected (all zeros):\n");
+ pass = false;
+ }
+ } else {
+ if (lm_good) {
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ torture_comment(samlogon_state->tctx, "LM Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "lm_key:\n");
+ dump_data(1, lm_key, 8);
+ torture_comment(samlogon_state->tctx, "expected:\n");
+ dump_data(1, lm_hash, 8);
+ pass = false;
+ }
+#if 0
+ } else {
+ if (memcmp(session_key.data, lm_key,
+ sizeof(lm_key)) != 0) {
+ torture_comment(samlogon_state->tctx, "LM Key does not match expectations (first 8 session key)!\n");
+ torture_comment(samlogon_state->tctx, "lm_key:\n");
+ dump_data(1, lm_key, 8);
+ torture_comment(samlogon_state->tctx, "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) {
+ torture_comment(samlogon_state->tctx, "NT Session Key does not match expectations (should be first-8 LM hash)!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ torture_comment(samlogon_state->tctx, "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)) {
+ int ret;
+
+ SAFE_FREE(*error_string);
+ ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ if (ret == -1) {
+ *error_string = NULL;
+ }
+ 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 (!all_zero(lm_key,
+ sizeof(lm_key)) != 0) {
+ torture_comment(samlogon_state->tctx, "LM Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "lm_key:\n");
+ dump_data(1, lm_key, 8);
+ torture_comment(samlogon_state->tctx, "expected (all zero)\n");
+ pass = false;
+ }
+ if (memcmp(session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ torture_comment(samlogon_state->tctx, "NT Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ torture_comment(samlogon_state->tctx, "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, TEST_MACHINE_NAME, samlogon_state->workgroup);
+
+ 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)) {
+ int ret;
+
+ SAFE_FREE(*error_string);
+ ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ if (ret == -1) {
+ *error_string = NULL;
+ }
+ 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) {
+ torture_comment(samlogon_state->tctx, "USER (LMv2) Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ torture_comment(samlogon_state->tctx, "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) {
+ torture_comment(samlogon_state->tctx, "LM (LMv2) Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ torture_comment(samlogon_state->tctx, "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) {
+ torture_comment(samlogon_state->tctx, "USER (NTLMv2) Session Key expected, got LMv2 sessesion key instead:\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ torture_comment(samlogon_state->tctx, "expected:\n");
+ dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length);
+ pass = false;
+
+ } else {
+ torture_comment(samlogon_state->tctx, "USER (NTLMv2) Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ torture_comment(samlogon_state->tctx, "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) {
+ torture_comment(samlogon_state->tctx, "LM (NTLMv2) Session Key expected, got LMv2 sessesion key instead:\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ torture_comment(samlogon_state->tctx, "expected:\n");
+ dump_data(1, ntlmv2_session_key.data, 8);
+ pass = false;
+ } else {
+ torture_comment(samlogon_state->tctx, "LM (NTLMv2) Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ torture_comment(samlogon_state->tctx, "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->netbios_name, samlogon_state->workgroup);
+
+ 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)) {
+ int ret;
+
+ SAFE_FREE(*error_string);
+ ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ if (ret == -1) {
+ *error_string = NULL;
+ }
+ 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) {
+ torture_comment(samlogon_state->tctx, "USER (LMv2) Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ torture_comment(samlogon_state->tctx, "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) {
+ torture_comment(samlogon_state->tctx, "LM (LMv2) Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ torture_comment(samlogon_state->tctx, "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) {
+ torture_comment(samlogon_state->tctx, "USER (NTLMv2) Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ torture_comment(samlogon_state->tctx, "expected:\n");
+ dump_data(1, ntlm_session_key.data, ntlm_session_key.length);
+ pass = false;
+ }
+ if (!all_zero(lm_session_key,
+ sizeof(lm_session_key))) {
+ torture_comment(samlogon_state->tctx, "LM Session Key does not match expectations (zeros)!\n");
+ torture_comment(samlogon_state->tctx, "lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ pass = false;
+ }
+ break;
+ default:
+ if (memcmp(ntlm_session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ torture_comment(samlogon_state->tctx, "USER (NTLMv2) Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, 16);
+ torture_comment(samlogon_state->tctx, "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) {
+ torture_comment(samlogon_state->tctx, "LM (NTLMv2) Session Key does not match expectations!\n");
+ torture_comment(samlogon_state->tctx, "lm_session_key:\n");
+ dump_data(1, lm_session_key, 8);
+ torture_comment(samlogon_state->tctx, "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);
+
+ 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];
+
+ gnutls_hmac_hd_t hmac_hnd;
+ gnutls_hash_hd_t hash_hnd;
+
+ ZERO_STRUCT(user_session_key);
+ ZERO_STRUCT(lm_key);
+ generate_random_buffer(client_chall, 8);
+
+ gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
+ gnutls_hash(hash_hnd, samlogon_state->chall.data, 8);
+ gnutls_hash(hash_hnd, client_chall, 8);
+ gnutls_hash_deinit(hash_hnd, session_nonce_hash);
+
+ E_md4hash(samlogon_state->password, (uint8_t *)nt_hash);
+ 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);
+
+ gnutls_hmac_init(&hmac_hnd,
+ GNUTLS_MAC_MD5,
+ nt_key,
+ 16);
+ gnutls_hmac(hmac_hnd, samlogon_state->chall.data, 8);
+ gnutls_hmac(hmac_hnd, client_chall, 8);
+ gnutls_hmac_deinit(hmac_hnd, expected_user_session_key);
+
+ 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)) {
+ int ret;
+
+ SAFE_FREE(*error_string);
+ ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ if (ret == -1) {
+ *error_string = NULL;
+ }
+ 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 (!all_zero(lm_key, sizeof(lm_key))) {
+ torture_comment(samlogon_state->tctx, "LM Session Key does not match expectations (zeros)!\n");
+ torture_comment(samlogon_state->tctx, "lm_key:\n");
+ dump_data(1, lm_key, 8);
+ pass = false;
+ }
+ if (memcmp(nt_key, user_session_key, 16) != 0) {
+ torture_comment(samlogon_state->tctx, "NT Session Key does not match expectations (should be NT Key)!\n");
+ torture_comment(samlogon_state->tctx, "user_session_key:\n");
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ torture_comment(samlogon_state->tctx, "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;
+ smb_ucs2_t *unicodepw;
+ size_t converted_size = 0;
+ uint8_t user_session_key[16];
+ uint8_t lm_key[16];
+ uint8_t lm_hash[16];
+ DATA_BLOB chall = data_blob_talloc_zero(samlogon_state->mem_ctx, 8);
+ bool lm_good = E_deshash(samlogon_state->password, lm_hash);
+
+ ZERO_STRUCT(user_session_key);
+
+ if (!push_ucs2_talloc(samlogon_state->mem_ctx,
+ &unicodepw, samlogon_state->password, &converted_size)) {
+ 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,
+ CH_UNIX, CH_DOS,
+ password, strlen(password)+1,
+ (void**)&dospw, &converted_size)) {
+ 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;
+ }
+ /* for modern servers, the LM password is invalid */
+ if (break_which == NO_NT
+ && !torture_setting_bool(samlogon_state->tctx, "samba3", false)) {
+ 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)) {
+ int ret;
+
+ SAFE_FREE(*error_string);
+ ret = asprintf(error_string, "Expected error: %s, got %s", nt_errstr(samlogon_state->expected_error), nt_errstr(nt_status));
+ if (ret == -1) {
+ *error_string = NULL;
+ }
+ 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;
+ }
+
+ /* for modern servers, the LM password is invalid */
+ if (break_which == NO_NT
+ && !torture_setting_bool(samlogon_state->tctx, "samba3", false)) {
+ *error_string = strdup("LM password is OK but should have failed against a modern server");
+ 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},
+ { .name = NULL, }
+};
+
+/*
+ try a netlogon SamLogon
+*/
+static bool test_SamLogon(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
+ struct netlogon_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[] = { NetlogonNetworkInformation, NetlogonNetworkTransitiveInformation };
+ int function_levels[] = {
+ NDR_NETR_LOGONSAMLOGON,
+ NDR_NETR_LOGONSAMLOGONEX,
+ NDR_NETR_LOGONSAMLOGONWITHFLAGS };
+ struct samlogon_state samlogon_state;
+
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative = 1;
+ uint32_t flags = 0;
+
+ ZERO_STRUCT(logon);
+
+ torture_comment(tctx, "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.workgroup = lpcfg_workgroup(tctx->lp_ctx);
+ samlogon_state.netbios_name = lpcfg_netbios_name(tctx->lp_ctx);
+ 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.tctx = tctx;
+
+ 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 = &flags;
+ samlogon_state.r_flags.in.logon = &logon;
+ samlogon_state.r_flags.out.validation = &validation;
+ samlogon_state.r_flags.out.authoritative = &authoritative;
+ samlogon_state.r_flags.out.flags = &flags;
+
+ 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 = &flags;
+ samlogon_state.r_ex.in.logon = &logon;
+ samlogon_state.r_ex.out.validation = &validation;
+ samlogon_state.r_ex.out.authoritative = &authoritative;
+ samlogon_state.r_ex.out.flags = &flags;
+
+ 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;
+ samlogon_state.r.in.logon = &logon;
+ samlogon_state.r.out.validation = &validation;
+ samlogon_state.r.out.authoritative = &authoritative;
+
+
+ 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)) {
+ torture_comment(tctx, "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) {
+ torture_comment(tctx, " failed (expected, test incomplete): %s\n", error_string);
+ } else {
+ torture_comment(tctx, " 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 torture_context *tctx,
+ struct netlogon_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");
+ bool ret = true;
+ struct netr_LogonSamLogonWithFlags r;
+ struct netr_Authenticator a, ra;
+ struct netr_PasswordInfo pinfo;
+ uint32_t flags = 0;
+
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative = 1;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(a);
+ ZERO_STRUCT(r);
+ ZERO_STRUCT(ra);
+
+ ZERO_STRUCT(logon);
+ ZERO_STRUCT(validation);
+
+ netlogon_creds_client_authenticator(creds, &a);
+
+ logon.password = &pinfo;
+
+ 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 = NetlogonInteractiveTransitiveInformation;
+ r.in.logon = &logon;
+ r.in.validation_level = 6;
+ r.in.flags = &flags;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+ r.out.flags = &flags;
+
+ pinfo.identity_info.domain_name.string = account_domain;
+ pinfo.identity_info.parameter_control = parameter_control;
+ pinfo.identity_info.logon_id = 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) {
+ netlogon_creds_arcfour_crypt(creds, pinfo.lmpassword.hash, 16);
+ netlogon_creds_arcfour_crypt(creds, pinfo.ntpassword.hash, 16);
+ } else {
+ netlogon_creds_des_encrypt(creds, &pinfo.lmpassword);
+ netlogon_creds_des_encrypt(creds, &pinfo.ntpassword);
+ }
+
+ torture_comment(tctx, "Testing netr_LogonSamLogonWithFlags '%s' (Interactive Logon)\n", comment);
+
+ status = dcerpc_netr_LogonSamLogonWithFlags_r(b, fn_ctx, &r);
+ torture_assert_ntstatus_ok_goto(tctx,
+ status,
+ ret, failed,
+ talloc_asprintf(tctx, "%s: netr_LogonSamLogonWithFlags - %s\n",
+ __location__, nt_errstr(status)));
+
+ if (!r.out.return_authenticator) {
+ talloc_free(fn_ctx);
+ torture_fail(tctx, "no authenticator returned");
+ }
+
+ torture_assert_goto(tctx,
+ netlogon_creds_client_check(creds, &r.out.return_authenticator->cred),
+ ret, failed,
+ "Credential chaining failed\n");
+
+ torture_assert_ntstatus_equal(tctx, r.out.result, expected_error,
+ talloc_asprintf(tctx, "[%s]\\[%s] netr_LogonSamLogonWithFlags - expected %s got %s\n",
+ account_domain, account_name, nt_errstr(expected_error), nt_errstr(r.out.result)));
+
+ ret = true;
+ failed:
+ talloc_free(fn_ctx);
+
+ return ret;
+}
+
+/* This sets and resets the "minPwdAge" (in order to allow immediate user
+ * password changes). The behaviour is controlled by the "set" boolean. */
+static bool handle_minPwdAge(struct torture_context *torture,
+ TALLOC_CTX *mem_ctx, bool set)
+{
+ struct dcerpc_pipe *p;
+ struct policy_handle connect_handle, domain_handle;
+ struct samr_Connect c_r;
+ struct samr_LookupDomain ld_r;
+ struct samr_OpenDomain od_r;
+ struct samr_QueryDomainInfo qdi_r;
+ struct samr_SetDomainInfo sdi_r;
+ struct samr_Close cl_r;
+ struct lsa_String domName;
+ struct dom_sid *domSid = NULL;
+ union samr_DomainInfo *domInfo = NULL;
+ static int64_t old_minPwdAge = 0;
+ NTSTATUS status;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ c_r.in.system_name = 0;
+ c_r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ c_r.out.connect_handle = &connect_handle;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_samr_Connect_r(p->binding_handle, mem_ctx, &c_r),
+ "Connect failed");
+ torture_assert_ntstatus_ok(torture, c_r.out.result, "Connect failed");
+
+ ld_r.in.connect_handle = &connect_handle;
+ ld_r.in.domain_name = &domName;
+ ld_r.in.domain_name->string = lpcfg_workgroup(torture->lp_ctx);
+ ld_r.out.sid = &domSid;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_samr_LookupDomain_r(p->binding_handle, mem_ctx, &ld_r),
+ "LookupDomain failed");
+ torture_assert_ntstatus_ok(torture, ld_r.out.result,
+ "LookupDomain failed");
+
+ od_r.in.connect_handle = &connect_handle;
+ od_r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ od_r.in.sid = *ld_r.out.sid;
+ od_r.out.domain_handle = &domain_handle;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_samr_OpenDomain_r(p->binding_handle, mem_ctx, &od_r),
+ "OpenDomain failed");
+ torture_assert_ntstatus_ok(torture, od_r.out.result,
+ "OpenDomain failed");
+
+ qdi_r.in.domain_handle = &domain_handle;
+ qdi_r.in.level = DomainPasswordInformation;
+ qdi_r.out.info = &domInfo;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_samr_QueryDomainInfo_r(p->binding_handle, mem_ctx, &qdi_r),
+ "QueryDomainInfo failed");
+ torture_assert_ntstatus_ok(torture, qdi_r.out.result,
+ "QueryDomainInfo failed");
+
+ if (set) {
+ old_minPwdAge = domInfo->info1.min_password_age;
+ domInfo->info1.min_password_age = 0;
+ } else {
+ domInfo->info1.min_password_age = old_minPwdAge;
+ }
+
+ sdi_r.in.domain_handle = &domain_handle;
+ sdi_r.in.level = DomainPasswordInformation;
+ sdi_r.in.info = domInfo;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_samr_SetDomainInfo_r(p->binding_handle, mem_ctx, &sdi_r),
+ "SetDomainInfo failed");
+ torture_assert_ntstatus_ok(torture, sdi_r.out.result,
+ "SetDomainInfo failed");
+
+ cl_r.in.handle = &connect_handle;
+ cl_r.out.handle = &connect_handle;
+
+ torture_assert_ntstatus_ok(torture,
+ dcerpc_samr_Close_r(p->binding_handle, mem_ctx, &cl_r),
+ "Close failed");
+ torture_assert_ntstatus_ok(torture, cl_r.out.result, "Close failed");
+
+ 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;
+ const char *old_user_password, *user_password_wrong_wks, *user_password_wrong_time;
+ char *user_password;
+ 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 netlogon_creds_CredentialState *creds;
+ struct dcerpc_pipe *tmp_p = NULL;
+
+ torture_assert(torture, handle_minPwdAge(torture, mem_ctx, true),
+ "handle_minPwdAge error!");
+
+ /* 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);
+ torture_assert(torture, join_ctx, "Failed to join as Workstation\n");
+
+ userdomain = torture_setting_string(torture, "userdomain", lpcfg_workgroup(torture->lp_ctx));
+
+ user_ctx = torture_create_testuser(torture,
+ TEST_USER_NAME,
+ userdomain,
+ ACB_NORMAL,
+ &old_user_password);
+ torture_assert(torture, user_ctx, "Failed to create a test user\n");
+
+ user_password = talloc_strdup(torture, old_user_password);
+ torture_assert(torture, user_password != NULL, "Failed to copy old_user_password\n");
+
+ tmp_p = torture_join_samr_pipe(user_ctx);
+ torture_assert(torture, tmp_p, "torture_join_samr_pipe failed\n");
+ test_ChangePasswordUser3(tmp_p, torture,
+ 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,
+ &user_password_wrong_wks);
+ torture_assert(torture, user_ctx_wrong_wks,
+ "Failed to create a test user (wrong workstation test)\n");
+
+ 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;
+
+ tmp_p = torture_join_samr_pipe(user_ctx_wrong_wks);
+ status = dcerpc_samr_SetUserInfo_r(tmp_p->binding_handle, mem_ctx, &s);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, failed,
+ talloc_asprintf(torture, "SetUserInfo (list of workstations) failed - %s\n", nt_errstr(status)));
+ torture_assert_ntstatus_ok_goto(torture, s.out.result, ret, failed,
+ talloc_asprintf(torture, "SetUserInfo (list of workstations) failed - %s\n", nt_errstr(s.out.result)));
+
+ user_ctx_wrong_time
+ = torture_create_testuser(torture, TEST_USER_NAME_WRONG_TIME,
+ userdomain,
+ ACB_NORMAL,
+ &user_password_wrong_time);
+ torture_assert(torture, user_ctx_wrong_time,
+ "Failed to create a test user (wrong workstation test)\n");
+
+ 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);
+
+ tmp_p = torture_join_samr_pipe(user_ctx_wrong_time);
+ status = dcerpc_samr_SetUserInfo_r(tmp_p->binding_handle, mem_ctx, &s);
+ torture_assert_ntstatus_ok_goto(torture, status, ret, failed,
+ talloc_asprintf(torture, "SetUserInfo (logon times and list of workstations) failed - %s\n", nt_errstr(status)));
+ torture_assert_ntstatus_ok_goto(torture, s.out.result, ret, failed,
+ talloc_asprintf(torture, "SetUserInfo (list of workstations) failed - %s\n", nt_errstr(s.out.result)));
+
+
+ torture_assert_ntstatus_ok_goto(torture,
+ torture_rpc_binding(torture, &b),
+ ret,
+ failed,
+ "Obtaining binding");
+
+ /* We have to use schannel, otherwise the SamLogonEx fails
+ * with INTERNAL_ERROR */
+
+ status = dcerpc_binding_set_flags(b,
+ DCERPC_SCHANNEL |
+ DCERPC_SIGN | DCERPC_SEAL |
+ DCERPC_SCHANNEL_128,
+ DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(torture, status, "set flags");
+
+ status = dcerpc_pipe_connect_b(mem_ctx, &p, b,
+ &ndr_table_netlogon,
+ machine_credentials, torture->ev, torture->lp_ctx);
+
+ torture_assert_ntstatus_ok_goto(torture, status, ret, failed,
+ talloc_asprintf(torture, "RPC pipe connect as domain member failed: %s\n", nt_errstr(status)));
+
+ torture_assert_not_null_goto(torture,
+ creds = cli_credentials_get_netlogon_creds(machine_credentials),
+ ret,
+ failed,
+ "obtaining credentails");
+
+ {
+
+ 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(
+ samba_cmdline_get_creds()),
+ .username = cli_credentials_get_username(
+ samba_cmdline_get_creds()),
+ .password = cli_credentials_get_password(
+ samba_cmdline_get_creds()),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK,
+ .parameter_control = 0,
+ },
+ {
+ .comment = "realm\\user",
+ .domain = cli_credentials_get_realm(
+ samba_cmdline_get_creds()),
+ .username = cli_credentials_get_username(
+ samba_cmdline_get_creds()),
+ .password = cli_credentials_get_password(
+ samba_cmdline_get_creds()),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK,
+ .parameter_control = 0,
+ },
+ {
+ .comment = "user@domain",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ cli_credentials_get_username(
+ samba_cmdline_get_creds()),
+ cli_credentials_get_domain(
+ samba_cmdline_get_creds())
+ ),
+ .password = cli_credentials_get_password(
+ samba_cmdline_get_creds()),
+ .network_login = false, /* works for some things, but not NTLMv2. Odd */
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK,
+ .parameter_control = 0,
+ },
+ {
+ .comment = "user@realm",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ cli_credentials_get_username(
+ samba_cmdline_get_creds()),
+ cli_credentials_get_realm(
+ samba_cmdline_get_creds())
+ ),
+ .password = cli_credentials_get_password(
+ samba_cmdline_get_creds()),
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK,
+ .parameter_control = 0,
+ },
+ {
+ .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,
+ .parameter_control = 0,
+ },
+ {
+ .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,
+ .parameter_control = 0,
+ },
+ {
+ .comment = "test user (long pw): user@realm",
+ .domain = NULL,
+ .username = talloc_asprintf(mem_ctx,
+ "%s@%s",
+ TEST_USER_NAME,
+ lpcfg_realm(torture->lp_ctx)),
+ .password = user_password,
+ .network_login = true,
+ .expected_interactive_error = NT_STATUS_OK,
+ .expected_network_error = NT_STATUS_OK,
+ .parameter_control = 0,
+ },
+ {
+ .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,
+ .parameter_control = 0,
+ },
+ /* 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 (wrong 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,
+ .parameter_control = 0,
+ }
+ };
+
+ /* Try all the tests for different username forms */
+ for (ci = 0; ci < ARRAY_SIZE(usercreds); ci++) {
+
+ torture_assert_goto(torture,
+ test_InteractiveLogon(p, mem_ctx, torture, 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,
+ failed,
+ talloc_asprintf(mem_ctx, "InteractiveLogon: %s",
+ usercreds[ci].comment));
+
+ if (usercreds[ci].network_login) {
+ torture_assert_goto(torture,
+ 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,
+ failed,
+ talloc_asprintf(mem_ctx, "SamLogon: %s",
+ usercreds[ci].comment));
+ }
+ }
+
+ /* 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! */
+
+ torture_comment(torture,
+ "Testing with flags: 0x%08x\n",
+ credential_flags[i]);
+
+ torture_assert_goto(torture,
+ test_InteractiveLogon(p, mem_ctx, torture, 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,
+ failed,
+ talloc_asprintf(mem_ctx,
+ "Testing InteractiveLogon with flags: 0x%08x\n",
+ credential_flags[i]));
+
+ if (usercreds[0].network_login) {
+ torture_assert_goto(torture,
+ 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,
+ failed,
+ talloc_asprintf(mem_ctx,
+ "Testing SamLogon with flags: 0x%08x\n",
+ credential_flags[i]));
+ }
+ }
+
+ }
+failed:
+ torture_assert(torture, handle_minPwdAge(torture, mem_ctx, false),
+ "handle_minPwdAge error!");
+
+ 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 0000000..0b1880e
--- /dev/null
+++ b/source4/torture/rpc/samr.c
@@ -0,0 +1,9468 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for samr rpc operations
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
+ Copyright (C) Jelmer Vernooij 2005-2007
+ Copyright (C) Guenther Deschner 2008-2010
+
+ 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 <tevent.h>
+#include "system/time.h"
+#include "system/network.h"
+#include "librpc/gen_ndr/lsa.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 "librpc/gen_ndr/ndr_lsa_c.h"
+#include "lib/crypto/crypto.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/security/security.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_proto.h"
+#include "../libcli/auth/schannel.h"
+#include "torture/util.h"
+#include "source4/librpc/rpc/dcerpc.h"
+#include "librpc/rpc/dcerpc_samr.h"
+#include "source3/rpc_client/init_samr.h"
+#include "lib/crypto/gnutls_helpers.h"
+
+#undef strcasecmp
+
+#define TEST_ACCOUNT_NAME "samrtorturetest"
+#define TEST_ACCOUNT_NAME_PWD "samrpwdlastset"
+#define TEST_ALIASNAME "samrtorturetestalias"
+#define TEST_GROUPNAME "samrtorturetestgroup"
+#define TEST_MACHINENAME "samrtestmach$"
+#define TEST_DOMAINNAME "samrtestdom$"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+enum torture_samr_choice {
+ TORTURE_SAMR_PASSWORDS,
+ TORTURE_SAMR_PASSWORDS_PWDLASTSET,
+ TORTURE_SAMR_PASSWORDS_BADPWDCOUNT,
+ TORTURE_SAMR_PASSWORDS_LOCKOUT,
+ TORTURE_SAMR_USER_ATTRIBUTES,
+ TORTURE_SAMR_USER_PRIVILEGES,
+ TORTURE_SAMR_OTHER,
+ TORTURE_SAMR_MANY_ACCOUNTS,
+ TORTURE_SAMR_MANY_GROUPS,
+ TORTURE_SAMR_MANY_ALIASES
+};
+
+struct torture_samr_context {
+ struct policy_handle handle;
+ struct cli_credentials *machine_credentials;
+ enum torture_samr_choice choice;
+ uint32_t num_objects_large_dc;
+};
+
+static bool test_QueryUserInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle);
+
+static bool test_QueryUserInfo2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle);
+
+static bool test_QueryAliasInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle);
+
+static bool test_ChangePassword(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ 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;
+}
+
+static void init_lsa_StringLarge(struct lsa_StringLarge *string, const char *s)
+{
+ string->string = s;
+}
+
+static void init_lsa_BinaryString(struct lsa_BinaryString *string, const char *s, uint32_t length)
+{
+ string->length = length;
+ string->size = length;
+ string->array = (uint16_t *)discard_const(s);
+}
+
+bool test_samr_handle_Close(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_Close r;
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_Close_r(b, tctx, &r),
+ "Close failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "Close failed");
+
+ return true;
+}
+
+static bool test_Shutdown(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_Shutdown r;
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ torture_skip(tctx, "samr_Shutdown disabled - enable dangerous tests to use\n");
+ return true;
+ }
+
+ r.in.connect_handle = handle;
+
+ torture_comment(tctx, "Testing samr_Shutdown\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_Shutdown_r(b, tctx, &r),
+ "Shutdown failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "Shutdown failed");
+
+ return true;
+}
+
+static bool test_SetDsrmPassword(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_SetDsrmPassword r;
+ struct lsa_String string;
+ struct samr_Password hash;
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ torture_skip(tctx, "samr_SetDsrmPassword disabled - enable dangerous tests to use");
+ }
+
+ E_md4hash("TeSTDSRM123", hash.hash);
+
+ init_lsa_String(&string, "Administrator");
+
+ r.in.name = &string;
+ r.in.unknown = 0;
+ r.in.hash = &hash;
+
+ torture_comment(tctx, "Testing samr_SetDsrmPassword\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDsrmPassword_r(b, tctx, &r),
+ "SetDsrmPassword failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_SUPPORTED, "SetDsrmPassword failed");
+
+ return true;
+}
+
+
+static bool test_QuerySecurity(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QuerySecurity r;
+ struct samr_SetSecurity s;
+ struct sec_desc_buf *sdbuf = NULL;
+
+ r.in.handle = handle;
+ r.in.sec_info = 7;
+ r.out.sdbuf = &sdbuf;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &r),
+ "QuerySecurity failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "QuerySecurity failed");
+
+ torture_assert(tctx, sdbuf != NULL, "sdbuf is NULL");
+
+ s.in.handle = handle;
+ s.in.sec_info = 7;
+ s.in.sdbuf = sdbuf;
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_skip(tctx, "skipping SetSecurity test against Samba4\n");
+ }
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetSecurity_r(b, tctx, &s),
+ "SetSecurity failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "SetSecurity failed");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &r),
+ "QuerySecurity failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "QuerySecurity failed");
+
+ return true;
+}
+
+
+static bool test_SetUserInfo(struct dcerpc_binding_handle *b, struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t base_acct_flags,
+ const char *base_account_name)
+{
+ struct samr_SetUserInfo s;
+ struct samr_SetUserInfo2 s2;
+ struct samr_QueryUserInfo q;
+ struct samr_QueryUserInfo q0;
+ union samr_UserInfo u;
+ union samr_UserInfo *info;
+ bool ret = true;
+ const char *test_account_name;
+
+ uint32_t user_extra_flags = 0;
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ 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 = &info;
+ q0 = q;
+
+#define TESTCALL(call, r) \
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ ##call## _r(b, tctx, &r),\
+ #call " failed"); \
+ if (!NT_STATUS_IS_OK(r.out.result)) { \
+ torture_result(tctx, TORTURE_FAIL, #call " level %u failed - %s (%s)\n", \
+ r.in.level, nt_errstr(r.out.result), __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define STRING_EQUAL(s1, s2, field) \
+ torture_assert_str_equal(tctx, s1, s2, "Failed to set " #field)
+
+#define MEM_EQUAL(s1, s2, length, field) \
+ torture_assert_mem_equal(tctx, s1, s2, length, "Failed to set " #field)
+
+#define INT_EQUAL(i1, i2, field) \
+ torture_assert_int_equal(tctx, i1, i2, "Failed to set " #field)
+
+#define TEST_USERINFO_STRING(lvl1, field1, lvl2, field2, value, fpval) do { \
+ torture_comment(tctx, "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 = *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 = *info; \
+ STRING_EQUAL(u.info ## lvl1.field1.string, value, field1); \
+ q.in.level = lvl2; \
+ TESTCALL(QueryUserInfo, q) \
+ u = *info; \
+ STRING_EQUAL(u.info ## lvl2.field2.string, value, field2); \
+ } while (0)
+
+#define TEST_USERINFO_BINARYSTRING(lvl1, field1, lvl2, field2, value, fpval) do { \
+ torture_comment(tctx, "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 = *info; \
+ if (lvl1 == 21) { \
+ ZERO_STRUCT(u.info21); \
+ u.info21.fields_present = fpval; \
+ } \
+ init_lsa_BinaryString(&u.info ## lvl1.field1, value, strlen(value)); \
+ TESTCALL(SetUserInfo, s) \
+ TESTCALL(SetUserInfo2, s2) \
+ init_lsa_BinaryString(&u.info ## lvl1.field1, "", 1); \
+ TESTCALL(QueryUserInfo, q); \
+ u = *info; \
+ MEM_EQUAL(u.info ## lvl1.field1.array, value, strlen(value), field1); \
+ q.in.level = lvl2; \
+ TESTCALL(QueryUserInfo, q) \
+ u = *info; \
+ MEM_EQUAL(u.info ## lvl2.field2.array, value, strlen(value), field2); \
+ } while (0)
+
+#define TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value, fpval) do { \
+ torture_comment(tctx, "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 = *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 = *info; \
+ INT_EQUAL(u.info ## lvl1.field1, exp_value, field1); \
+ q.in.level = lvl2; \
+ TESTCALL(QueryUserInfo, q) \
+ u = *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, test_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-3", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 3, account_name, test_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-5", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 5, account_name, test_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-6", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 6, account_name, test_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-7", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 7, account_name, test_account_name, 0);
+ test_account_name = talloc_asprintf(tctx, "%sxx7-21", base_account_name);
+ TEST_USERINFO_STRING(7, account_name, 21, account_name, test_account_name, 0);
+ test_account_name = base_account_name;
+ TEST_USERINFO_STRING(21, account_name, 21, account_name, test_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_BINARYSTRING(20, parameters, 21, parameters, "xx20-21 parameters", 0);
+ TEST_USERINFO_BINARYSTRING(21, parameters, 21, parameters, "xx21-21 parameters",
+ SAMR_FIELD_PARAMETERS);
+ TEST_USERINFO_BINARYSTRING(21, parameters, 20, parameters, "xx21-20 parameters",
+ SAMR_FIELD_PARAMETERS);
+ /* also empty user parameters are allowed */
+ TEST_USERINFO_BINARYSTRING(20, parameters, 21, parameters, "", 0);
+ TEST_USERINFO_BINARYSTRING(21, parameters, 21, parameters, "",
+ SAMR_FIELD_PARAMETERS);
+ TEST_USERINFO_BINARYSTRING(21, parameters, 20, parameters, "",
+ SAMR_FIELD_PARAMETERS);
+
+ /* Samba 3 cannot store country_code and code_page atm. - gd */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ 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);
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ 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);
+ } else {
+ /* Samba 3 can only store seconds / time_t in passdb - gd */
+ NTTIME nt;
+ unix_to_nt_time(&nt, time(NULL) + __LINE__);
+ TEST_USERINFO_INT(17, acct_expiry, 21, acct_expiry, nt, 0);
+ unix_to_nt_time(&nt, time(NULL) + __LINE__);
+ TEST_USERINFO_INT(17, acct_expiry, 5, acct_expiry, nt, 0);
+ unix_to_nt_time(&nt, time(NULL) + __LINE__);
+ TEST_USERINFO_INT(21, acct_expiry, 21, acct_expiry, nt, SAMR_FIELD_ACCT_EXPIRY);
+ unix_to_nt_time(&nt, time(NULL) + __LINE__);
+ TEST_USERINFO_INT(21, acct_expiry, 5, acct_expiry, nt, SAMR_FIELD_ACCT_EXPIRY);
+ unix_to_nt_time(&nt, time(NULL) + __LINE__);
+ TEST_USERINFO_INT(21, acct_expiry, 17, acct_expiry, nt, 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
+
+ /* Samba3 cannot store these atm */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ /* 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_silent(TALLOC_CTX *mem_ctx, int min_len)
+{
+ size_t len = MAX(8, min_len);
+ char *s = generate_random_password(mem_ctx, len, len+6);
+ return s;
+}
+
+static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len)
+{
+ char *s = samr_rand_pass_silent(mem_ctx, min_len);
+ printf("Generated password '%s'\n", s);
+ return s;
+
+}
+
+/*
+ generate a random password for password change tests
+*/
+static DATA_BLOB samr_very_rand_pass(TALLOC_CTX *mem_ctx, int len)
+{
+ int i;
+ DATA_BLOB password = data_blob_talloc(mem_ctx, NULL, len * 2 /* number of unicode chars */);
+ generate_random_buffer(password.data, password.length);
+
+ for (i=0; i < len; i++) {
+ if (((uint16_t *)password.data)[i] == 0) {
+ ((uint16_t *)password.data)[i] = 1;
+ }
+ }
+
+ return password;
+}
+
+/*
+ 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_password(mem_ctx, len, len);
+ printf("Generated password '%s'\n", s);
+ return s;
+}
+
+static bool test_SetUserPass(struct dcerpc_pipe *p, struct torture_context *tctx,
+ 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 dcerpc_binding_handle *b = p->binding_handle;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 24;
+
+ u.info24.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ status = init_samr_CryptPassword(newpass,
+ &session_key,
+ &u.info24.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword failed");
+
+ torture_comment(tctx, "Testing SetUserInfo level 24 (set password)\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+
+static bool test_SetUserPass_23(struct dcerpc_pipe *p, struct torture_context *tctx,
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ char *newpass;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ newpass = samr_rand_pass(tctx, 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;
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ status = init_samr_CryptPassword(newpass,
+ &session_key,
+ &u.info23.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword failed");
+
+ torture_comment(tctx, "Testing SetUserInfo level 23 (set password)\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ /* This should break the key nicely */
+ session_key.data[0]++;
+
+ status = init_samr_CryptPassword(newpass,
+ &session_key,
+ &u.info23.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword failed");
+
+ /* Reset the session key */
+ session_key.data[0]--;
+
+ torture_comment(tctx, "Testing SetUserInfo level 23 (set password) with wrong password\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_SetUserPass_32(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t fields_present,
+ char **password)
+{
+ NTSTATUS status;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ DATA_BLOB session_key;
+ uint8_t salt_data[16];
+ DATA_BLOB salt = {
+ .data = salt_data,
+ .length = sizeof(salt_data),
+ };
+ char *newpass = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+ bool ret = true;
+
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 32;
+
+ ZERO_STRUCT(u);
+
+ u.info32.info.fields_present = fields_present;
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx,
+ TORTURE_FAIL,
+ "SetUserInfo level %u - no session key - %s\n",
+ s.in.level,
+ nt_errstr(status));
+ return false;
+ }
+
+ generate_nonce_buffer(salt.data, salt.length);
+
+ status = init_samr_CryptPasswordAES(tctx,
+ newpass,
+ &salt,
+ &session_key,
+ &u.info32.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordAES failed");
+
+ torture_comment(tctx,
+ "Testing SetUserInfo level 32 (set password aes)\n");
+
+ status = dcerpc_samr_SetUserInfo_r(b, tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__,
+ __FUNCTION__,
+ newpass,
+ nt_errstr(s.out.result));
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx,
+ TORTURE_FAIL,
+ "SetUserInfo level %u failed - %s\n",
+ s.in.level,
+ nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* This should break the key nicely */
+ session_key.data[0]++;
+
+ status = init_samr_CryptPasswordAES(tctx,
+ newpass,
+ &salt,
+ &session_key,
+ &u.info32.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordEx failed");
+
+ /* Reset the key */
+ session_key.data[0]--;
+
+ torture_comment(tctx,
+ "Testing SetUserInfo level 32 (set password aes) with "
+ "wrong session key\n");
+
+ status = dcerpc_samr_SetUserInfo_r(b, tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "SetUserInfo failed");
+ torture_comment(tctx,
+ "(%s:%s) new_password[%s] status[%s]\n",
+ __location__,
+ __FUNCTION__,
+ newpass,
+ nt_errstr(s.out.result));
+ if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx,
+ TORTURE_FAIL,
+ "SetUserInfo level %u should have failed with "
+ "WRONG_PASSWORD- %s\n",
+ s.in.level,
+ nt_errstr(s.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_SetUserPass_31(struct dcerpc_pipe *p, struct torture_context *tctx,
+ 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;
+ uint8_t salt_data[16];
+ DATA_BLOB salt = {
+ .data = salt_data,
+ .length = sizeof(salt_data),
+ };
+ char *newpass;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ if (makeshort && policy_min_pw_len) {
+ newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len - 1);
+ } else {
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+ }
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 31;
+
+ ZERO_STRUCT(u);
+
+ u.info31.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_nonce_buffer(salt.data, salt.length);
+
+ status = init_samr_CryptPasswordAES(tctx,
+ newpass,
+ &salt,
+ &session_key,
+ &u.info31.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordEx failed");
+
+ torture_comment(tctx, "Testing SetUserInfo level 31 (set password aes)\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* This should break the key nicely */
+ session_key.data[0]++;
+
+ status = init_samr_CryptPasswordAES(tctx,
+ newpass,
+ &salt,
+ &session_key,
+ &u.info31.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordEx failed");
+
+ /* Reset the key */
+ session_key.data[0]--;
+
+ torture_comment(tctx, "Testing SetUserInfo level 31 (set password aes) with wrong session key\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+
+static bool test_SetUserPassEx(struct dcerpc_pipe *p, struct torture_context *tctx,
+ 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;
+ char *newpass;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ if (makeshort && policy_min_pw_len) {
+ newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len - 1);
+ } else {
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+ }
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 26;
+
+ u.info26.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ status = init_samr_CryptPasswordEx(newpass,
+ &session_key,
+ &u.info26.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordEx failed");
+
+ torture_comment(tctx, "Testing SetUserInfo level 26 (set password ex)\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* This should break the key nicely */
+ session_key.data[0]++;
+
+ status = init_samr_CryptPasswordEx(newpass,
+ &session_key,
+ &u.info26.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordEx failed");
+
+ /* Reset the key */
+ session_key.data[0]--;
+
+ torture_comment(tctx, "Testing SetUserInfo level 26 (set password ex) with wrong session key\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD: %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+static bool test_SetUserPass_25(struct dcerpc_pipe *p, struct torture_context *tctx,
+ 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 dcerpc_binding_handle *b = p->binding_handle;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ newpass = samr_rand_pass(tctx, 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;
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ status = init_samr_CryptPasswordEx(newpass,
+ &session_key,
+ &u.info25.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordEx failed");
+
+ torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex)\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* This should break the key nicely */
+ session_key.data[0]++;
+
+ status = init_samr_CryptPasswordEx(newpass,
+ &session_key,
+ &u.info25.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordEx failed");
+
+ /* Reset the key */
+ session_key.data[0]--;
+
+ torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex) with wrong session key\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with WRONG_PASSWORD- %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_SetUserPass_18(struct dcerpc_pipe *p, struct torture_context *tctx,
+ 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 dcerpc_binding_handle *b = p->binding_handle;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+ uint8_t lm_hash[16], nt_hash[16];
+
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 18;
+
+ ZERO_STRUCT(u);
+
+ u.info18.nt_pwd_active = true;
+ u.info18.lm_pwd_active = true;
+
+ E_md4hash(newpass, nt_hash);
+ E_deshash(newpass, lm_hash);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ {
+ DATA_BLOB in,out;
+ in = data_blob_const(nt_hash, 16);
+ out = data_blob_talloc_zero(tctx, 16);
+ sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT);
+ memcpy(u.info18.nt_pwd.hash, out.data, out.length);
+ }
+ {
+ DATA_BLOB in,out;
+ in = data_blob_const(lm_hash, 16);
+ out = data_blob_talloc_zero(tctx, 16);
+ sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT);
+ memcpy(u.info18.lm_pwd.hash, out.data, out.length);
+ }
+
+ torture_comment(tctx, "Testing SetUserInfo level 18 (set password hash)\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+static bool test_SetUserPass_21(struct dcerpc_pipe *p, struct torture_context *tctx,
+ 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 dcerpc_binding_handle *b = p->binding_handle;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+ uint8_t lm_hash[16], nt_hash[16];
+
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 21;
+
+ E_md4hash(newpass, nt_hash);
+ E_deshash(newpass, lm_hash);
+
+ ZERO_STRUCT(u);
+
+ u.info21.fields_present = fields_present;
+
+ if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ u.info21.lm_owf_password.length = 16;
+ u.info21.lm_owf_password.size = 16;
+ u.info21.lm_owf_password.array = (uint16_t *)lm_hash;
+ u.info21.lm_password_set = true;
+ }
+
+ if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) {
+ u.info21.nt_owf_password.length = 16;
+ u.info21.nt_owf_password.size = 16;
+ u.info21.nt_owf_password.array = (uint16_t *)nt_hash;
+ u.info21.nt_password_set = true;
+ }
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ DATA_BLOB in,out;
+ in = data_blob_const(u.info21.lm_owf_password.array,
+ u.info21.lm_owf_password.length);
+ out = data_blob_talloc_zero(tctx, 16);
+ sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT);
+ u.info21.lm_owf_password.array = (uint16_t *)out.data;
+ }
+
+ if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) {
+ DATA_BLOB in,out;
+ in = data_blob_const(u.info21.nt_owf_password.array,
+ u.info21.nt_owf_password.length);
+ out = data_blob_talloc_zero(tctx, 16);
+ sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT);
+ u.info21.nt_owf_password.array = (uint16_t *)out.data;
+ }
+
+ torture_comment(tctx, "Testing SetUserInfo level 21 (set password hash)\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ /* try invalid length */
+ if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) {
+
+ u.info21.nt_owf_password.length++;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with NT_STATUS_INVALID_PARAMETER - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ }
+ }
+
+ if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) {
+
+ u.info21.lm_owf_password.length++;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ if (!NT_STATUS_EQUAL(s.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u should have failed with NT_STATUS_INVALID_PARAMETER - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_SetUserPass_level_ex(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint16_t level,
+ uint32_t fields_present,
+ char **password, uint8_t password_expired,
+ bool use_setinfo2,
+ bool *matched_expected_error)
+{
+ NTSTATUS status;
+ NTSTATUS expected_error = NT_STATUS_OK;
+ struct samr_SetUserInfo s;
+ struct samr_SetUserInfo2 s2;
+ union samr_UserInfo u;
+ bool ret = true;
+ DATA_BLOB session_key;
+ uint8_t salt_data[16];
+ DATA_BLOB salt = {
+ .data = salt_data,
+ .length = sizeof(salt_data),
+ };
+ char *newpass;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+ const char *comment = NULL;
+ uint8_t lm_hash[16], nt_hash[16];
+
+ pwp.in.user_handle = handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ newpass = samr_rand_pass_silent(tctx, policy_min_pw_len);
+
+ if (use_setinfo2) {
+ s2.in.user_handle = handle;
+ s2.in.info = &u;
+ s2.in.level = level;
+ } else {
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = level;
+ }
+
+ if (fields_present & SAMR_FIELD_COMMENT) {
+ comment = talloc_asprintf(tctx, "comment: %ld\n", (long int) time(NULL));
+ }
+
+ ZERO_STRUCT(u);
+
+ switch (level) {
+ case 18:
+ E_md4hash(newpass, nt_hash);
+ E_deshash(newpass, lm_hash);
+
+ u.info18.nt_pwd_active = true;
+ u.info18.lm_pwd_active = true;
+ u.info18.password_expired = password_expired;
+
+ memcpy(u.info18.lm_pwd.hash, lm_hash, 16);
+ memcpy(u.info18.nt_pwd.hash, nt_hash, 16);
+
+ break;
+ case 21:
+ E_md4hash(newpass, nt_hash);
+ E_deshash(newpass, lm_hash);
+
+ u.info21.fields_present = fields_present;
+ u.info21.password_expired = password_expired;
+ u.info21.comment.string = comment;
+
+ if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ u.info21.lm_owf_password.length = 16;
+ u.info21.lm_owf_password.size = 16;
+ u.info21.lm_owf_password.array = (uint16_t *)lm_hash;
+ u.info21.lm_password_set = true;
+ }
+
+ if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) {
+ u.info21.nt_owf_password.length = 16;
+ u.info21.nt_owf_password.size = 16;
+ u.info21.nt_owf_password.array = (uint16_t *)nt_hash;
+ u.info21.nt_password_set = true;
+ }
+
+ break;
+ case 23:
+ u.info23.info.fields_present = fields_present;
+ u.info23.info.password_expired = password_expired;
+ u.info23.info.comment.string = comment;
+
+ break;
+ case 24:
+ u.info24.password_expired = password_expired;
+
+ break;
+ case 25:
+ u.info25.info.fields_present = fields_present;
+ u.info25.info.password_expired = password_expired;
+ u.info25.info.comment.string = comment;
+
+ break;
+ case 26:
+ u.info26.password_expired = password_expired;
+
+ break;
+ case 31:
+ u.info31.password_expired = password_expired;
+
+ break;
+ case 28:
+ u.info25.info.fields_present = fields_present;
+ u.info25.info.password_expired = password_expired;
+ u.info25.info.comment.string = comment;
+
+ break;
+ }
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_nonce_buffer(salt.data, salt.length);
+
+ switch (level) {
+ case 18:
+ {
+ DATA_BLOB in,out;
+ in = data_blob_const(u.info18.nt_pwd.hash, 16);
+ out = data_blob_talloc_zero(tctx, 16);
+ sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT);
+ memcpy(u.info18.nt_pwd.hash, out.data, out.length);
+ }
+ {
+ DATA_BLOB in,out;
+ in = data_blob_const(u.info18.lm_pwd.hash, 16);
+ out = data_blob_talloc_zero(tctx, 16);
+ sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT);
+ memcpy(u.info18.lm_pwd.hash, out.data, out.length);
+ }
+
+ break;
+ case 21:
+ if (fields_present & SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ DATA_BLOB in,out;
+ in = data_blob_const(u.info21.lm_owf_password.array,
+ u.info21.lm_owf_password.length);
+ out = data_blob_talloc_zero(tctx, 16);
+ sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT);
+ u.info21.lm_owf_password.array = (uint16_t *)out.data;
+ }
+ if (fields_present & SAMR_FIELD_NT_PASSWORD_PRESENT) {
+ DATA_BLOB in,out;
+ in = data_blob_const(u.info21.nt_owf_password.array,
+ u.info21.nt_owf_password.length);
+ out = data_blob_talloc_zero(tctx, 16);
+ sess_crypt_blob(&out, &in, &session_key, SAMBA_GNUTLS_ENCRYPT);
+ u.info21.nt_owf_password.array = (uint16_t *)out.data;
+ }
+ break;
+ case 23:
+ status = init_samr_CryptPassword(newpass,
+ &session_key,
+ &u.info23.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword failed");
+ break;
+ case 24:
+ status = init_samr_CryptPassword(newpass,
+ &session_key,
+ &u.info24.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword failed");
+ break;
+ case 25:
+ status = init_samr_CryptPasswordEx(newpass,
+ &session_key,
+ &u.info25.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordEx failed");
+ break;
+ case 26:
+ status = init_samr_CryptPasswordEx(newpass,
+ &session_key,
+ &u.info26.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPasswordEx failed");
+ break;
+ case 31:
+ status = init_samr_CryptPasswordAES(tctx,
+ newpass,
+ &salt,
+ &session_key,
+ &u.info31.password);
+
+ break;
+ case 32:
+ status = init_samr_CryptPasswordAES(tctx,
+ newpass,
+ &salt,
+ &session_key,
+ &u.info32.password);
+
+ break;
+ }
+
+ if (use_setinfo2) {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo2_r(b, tctx, &s2),
+ "SetUserInfo2 failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s2.out.result));
+ status = s2.out.result;
+ } else {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ newpass, nt_errstr(s.out.result));
+ status = s.out.result;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (fields_present == 0) {
+ expected_error = NT_STATUS_INVALID_PARAMETER;
+ }
+ if (fields_present & SAMR_FIELD_LAST_PWD_CHANGE) {
+ expected_error = NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(expected_error)) {
+ if (use_setinfo2) {
+ torture_assert_ntstatus_equal(tctx,
+ s2.out.result,
+ expected_error, "SetUserInfo2 failed");
+ } else {
+ torture_assert_ntstatus_equal(tctx,
+ s.out.result,
+ expected_error, "SetUserInfo failed");
+ }
+ *matched_expected_error = true;
+ return true;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo%s level %u failed - %s\n",
+ use_setinfo2 ? "2":"", level, nt_errstr(status));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+static bool test_SetAliasInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_SetAliasInfo r;
+ struct samr_QueryAliasInfo q;
+ union samr_AliasInfo *info;
+ 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++) {
+ torture_comment(tctx, "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: torture_comment(tctx, "ALIASINFOALL ignored\n"); break;
+ }
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetAliasInfo_r(b, tctx, &r),
+ "SetAliasInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetAliasInfo level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ q.in.alias_handle = handle;
+ q.in.level = levels[i];
+ q.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryAliasInfo_r(b, tctx, &q),
+ "QueryAliasInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryAliasInfo level %u failed - %s\n",
+ levels[i], nt_errstr(q.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetGroupsForUser(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *user_handle)
+{
+ struct samr_GetGroupsForUser r;
+ struct samr_RidWithAttributeArray *rids = NULL;
+
+ torture_comment(tctx, "Testing GetGroupsForUser\n");
+
+ r.in.user_handle = user_handle;
+ r.out.rids = &rids;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetGroupsForUser_r(b, tctx, &r),
+ "GetGroupsForUser failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "GetGroupsForUser failed");
+
+ return true;
+
+}
+
+static bool test_GetDomPwInfo(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct lsa_String *domain_name)
+{
+ struct samr_GetDomPwInfo r;
+ struct samr_PwInfo info;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.domain_name = domain_name;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r),
+ "GetDomPwInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed");
+
+ r.in.domain_name->string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r),
+ "GetDomPwInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed");
+
+ r.in.domain_name->string = "\\\\__NONAME__";
+ torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r),
+ "GetDomPwInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed");
+
+ r.in.domain_name->string = "\\\\Builtin";
+ torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r),
+ "GetDomPwInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "GetDomPwInfo failed");
+
+ return true;
+}
+
+static bool test_GetUserPwInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_GetUserPwInfo r;
+ struct samr_PwInfo info;
+
+ torture_comment(tctx, "Testing GetUserPwInfo\n");
+
+ r.in.user_handle = handle;
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &r),
+ "GetUserPwInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "GetUserPwInfo");
+
+ return true;
+}
+
+static NTSTATUS test_LookupName(struct dcerpc_binding_handle *b,
+ 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];
+ struct samr_Ids rids, types;
+
+ init_lsa_String(&sname[0], name);
+
+ n.in.domain_handle = domain_handle;
+ n.in.num_names = 1;
+ n.in.names = sname;
+ n.out.rids = &rids;
+ n.out.types = &types;
+ status = dcerpc_samr_LookupNames_r(b, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (NT_STATUS_IS_OK(n.out.result)) {
+ *rid = n.out.rids->ids[0];
+ } else {
+ return n.out.result;
+ }
+
+ init_lsa_String(&sname[1], "xxNONAMExx");
+ n.in.num_names = 2;
+ status = dcerpc_samr_LookupNames_r(b, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_EQUAL(n.out.result, STATUS_SOME_UNMAPPED)) {
+ torture_result(tctx, TORTURE_FAIL, "LookupNames[2] failed - %s\n", nt_errstr(n.out.result));
+ if (NT_STATUS_IS_OK(n.out.result)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return n.out.result;
+ }
+
+ n.in.num_names = 0;
+ status = dcerpc_samr_LookupNames_r(b, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(n.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "LookupNames[0] failed - %s\n", nt_errstr(status));
+ return n.out.result;
+ }
+
+ init_lsa_String(&sname[0], "xxNONAMExx");
+ n.in.num_names = 1;
+ status = dcerpc_samr_LookupNames_r(b, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_EQUAL(n.out.result, NT_STATUS_NONE_MAPPED)) {
+ torture_result(tctx, TORTURE_FAIL, "LookupNames[1 bad name] failed - %s\n", nt_errstr(n.out.result));
+ if (NT_STATUS_IS_OK(n.out.result)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return n.out.result;
+ }
+
+ init_lsa_String(&sname[0], "xxNONAMExx");
+ init_lsa_String(&sname[1], "xxNONAME2xx");
+ n.in.num_names = 2;
+ status = dcerpc_samr_LookupNames_r(b, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_EQUAL(n.out.result, NT_STATUS_NONE_MAPPED)) {
+ torture_result(tctx, TORTURE_FAIL, "LookupNames[2 bad names] failed - %s\n", nt_errstr(n.out.result));
+ if (NT_STATUS_IS_OK(n.out.result)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ return n.out.result;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS test_OpenUser_byname(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ 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(b, tctx, 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_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "OpenUser_byname(%s -> %d) failed - %s\n", name, rid, nt_errstr(r.out.result));
+ }
+
+ return r.out.result;
+}
+
+#if 0
+static bool test_ChangePasswordNT3(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ 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, tctx, handle, "testuser", &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ torture_comment(tctx, "Testing ChangePasswordUser for user 'testuser'\n");
+
+ torture_comment(tctx, "old password: %s\n", oldpass);
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r),
+ "ChangePasswordUser failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(p, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+#endif
+
+static bool test_ChangePasswordUser(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ 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;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+
+ status = test_OpenUser_byname(b, tctx, handle, acct_name, &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ pwp.in.user_handle = &user_handle;
+ pwp.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetUserPwInfo_r(b, tctx, &pwp),
+ "GetUserPwInfo failed");
+ if (NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+
+ torture_comment(tctx, "Testing ChangePasswordUser\n");
+
+ torture_assert(tctx, *password != NULL,
+ "Failing ChangePasswordUser as old password was NULL. Previous test failed?");
+
+ 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 NT hash */
+ hash3.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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r),
+ "ChangePasswordUser failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ /* Do not proceed if this call has been removed */
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_NOT_IMPLEMENTED)) {
+ torture_skip(tctx, "ValidatePassword not supported by server\n");
+ }
+
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_WRONG_PASSWORD,
+ "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we broke the LM hash");
+ }
+
+ /* 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;
+ /* Break the LM hash */
+ hash1.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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r),
+ "ChangePasswordUser failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_WRONG_PASSWORD,
+ "expected NT_STATUS_WRONG_PASSWORD because we broke the NT hash");
+ }
+
+ /* 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r),
+ "ChangePasswordUser failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD) &&
+ !NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION))
+ {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD or NT_STATUS_PASSWORD_RESTRICTION because we broke the LM cross-hash, got %s\n", nt_errstr(r.out.result));
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r),
+ "ChangePasswordUser failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD) &&
+ !NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION))
+ {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD or NT_STATUS_PASSWORD_RESTRICTION because we broke the NT cross-hash, got %s\n", nt_errstr(r.out.result));
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r),
+ "ChangePasswordUser failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (NT_STATUS_IS_OK(r.out.result)) {
+ changed = true;
+ *password = newpass;
+ } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ oldpass = newpass;
+ newpass = samr_rand_pass(tctx, 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r),
+ "ChangePasswordUser failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (NT_STATUS_IS_OK(r.out.result)) {
+ changed = true;
+ *password = newpass;
+ } else if (!NT_STATUS_EQUAL(NT_STATUS_PASSWORD_RESTRICTION, r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_OK, or at least NT_STATUS_PASSWORD_RESTRICTION, got %s\n", nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ oldpass = newpass;
+ newpass = samr_rand_pass(tctx, 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r),
+ "ChangePasswordUser failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ torture_comment(tctx, "ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result));
+ } else if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed - %s\n", nt_errstr(r.out.result));
+ 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) {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser_r(b, tctx, &r),
+ "ChangePasswordUser failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ torture_comment(tctx, "ChangePasswordUser returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result));
+ } else if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser failed: expected NT_STATUS_WRONG_PASSWORD because we already changed the password, got %s\n", nt_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+
+ if (!test_samr_handle_Close(b, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_OemChangePasswordUser2(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ const char *acct_name,
+ struct policy_handle *handle, char **password)
+{
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t session_key = {
+ .data = old_lm_hash,
+ .size = 16
+ };
+
+ struct samr_GetDomPwInfo dom_pw_info;
+ struct samr_PwInfo info;
+ int policy_min_pw_len = 0;
+
+ struct lsa_String domain_name;
+
+ domain_name.string = "";
+ dom_pw_info.in.domain_name = &domain_name;
+ dom_pw_info.out.info = &info;
+
+ torture_comment(tctx, "Testing OemChangePasswordUser2\n");
+
+ torture_assert(tctx, *password != NULL,
+ "Failing OemChangePasswordUser2 as old password was NULL. Previous test failed?");
+
+ oldpass = *password;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &dom_pw_info),
+ "GetDomPwInfo failed");
+ if (NT_STATUS_IS_OK(dom_pw_info.out.result)) {
+ policy_min_pw_len = dom_pw_info.out.info->min_password_length;
+ }
+
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+
+ server.string = talloc_asprintf(tctx, "\\\\%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);
+
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &session_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ 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]++;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r),
+ "OemChangePasswordUser2 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_assert_ntstatus_equal(tctx,
+ r.out.result,
+ NT_STATUS_NOT_IMPLEMENTED,
+ "Samba4 should refuse LM password change");
+ /*
+ * No point continuing, once we have checked this is not
+ * implemented
+ */
+ return true;
+ }
+
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)
+ && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n",
+ nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ /* Break the old password */
+ old_lm_hash[0]++;
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &session_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ /* 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r),
+ "OemChangePasswordUser2 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)
+ && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrypted password - %s\n",
+ nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &session_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516);
+ gnutls_cipher_deinit(cipher_hnd);
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.password = &lm_pass;
+ r.in.hash = NULL;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r),
+ "OemChangePasswordUser2 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)
+ && !NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER (or at least 'PASSWORD_RESTRICTON') for no supplied validation hash - %s\n",
+ nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ /* This shouldn't be a valid name */
+ account_bad.string = TEST_ACCOUNT_NAME "XX";
+ r.in.account = &account_bad;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r),
+ "OemChangePasswordUser2 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER for no supplied validation hash and invalid user - %s\n",
+ nt_errstr(r.out.result));
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r),
+ "OemChangePasswordUser2 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned WRONG_PASSWORD for invalid user - %s\n",
+ nt_errstr(r.out.result));
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r),
+ "OemChangePasswordUser2 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed, should have returned INVALID_PARAMETER for no supplied password and invalid user - %s\n",
+ nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ E_deshash(oldpass, old_lm_hash);
+ E_deshash(newpass, new_lm_hash);
+
+ encode_pw_buffer(lm_pass.data, newpass, STR_ASCII);
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &session_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd, lm_pass.data, 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OemChangePasswordUser2_r(b, tctx, &r),
+ "OemChangePasswordUser2 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ torture_comment(tctx, "OemChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result));
+ } else if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "OemChangePasswordUser2 failed - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+
+static bool test_ChangePasswordUser2(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *acct_name,
+ char **password,
+ char *newpass, bool allow_password_restriction)
+{
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+ DATA_BLOB old_nt_hash_blob
+ = data_blob_const(old_nt_hash, sizeof(old_nt_hash));
+ struct samr_GetDomPwInfo dom_pw_info;
+ struct samr_PwInfo info;
+
+ struct lsa_String domain_name;
+ NTSTATUS status;
+
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t old_lm_key = {
+ .data = old_lm_hash,
+ .size = sizeof(old_lm_hash),
+ };
+
+ domain_name.string = "";
+ dom_pw_info.in.domain_name = &domain_name;
+ dom_pw_info.out.info = &info;
+
+ torture_comment(tctx, "Testing ChangePasswordUser2 on %s\n", acct_name);
+
+ torture_assert(tctx, *password != NULL,
+ "Failing ChangePasswordUser2 as old password was NULL. Previous test failed?");
+ oldpass = *password;
+
+ if (!newpass) {
+ int policy_min_pw_len = 0;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &dom_pw_info),
+ "GetDomPwInfo failed");
+ if (NT_STATUS_IS_OK(dom_pw_info.out.result)) {
+ policy_min_pw_len = dom_pw_info.out.info->min_password_length;
+ }
+
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+ }
+
+ server.string = talloc_asprintf(tctx, "\\\\%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);
+
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &old_lm_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd,
+ lm_pass.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ status = init_samr_CryptPassword(newpass,
+ &old_nt_hash_blob,
+ &nt_pass);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword failed");
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser2_r(b, tctx, &r),
+ "ChangePasswordUser2 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (allow_password_restriction && NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ torture_comment(tctx, "ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result));
+ } else if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser2 failed - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ } else {
+ *password = newpass;
+ }
+
+ return ret;
+}
+
+
+static bool test_ChangePasswordUser2_ntstatus(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *acct_name,
+ const char *password, NTSTATUS status)
+{
+ struct samr_ChangePasswordUser2 r;
+ struct lsa_String server, account;
+ struct samr_CryptPassword nt_pass, lm_pass;
+ struct samr_Password nt_verifier, lm_verifier;
+ const char *oldpass;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+ DATA_BLOB old_nt_hash_blob
+ = data_blob_const(old_nt_hash, sizeof(old_nt_hash));
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t old_lm_key = {
+ .data = old_lm_hash,
+ .size = sizeof(old_lm_hash),
+ };
+
+ struct samr_GetDomPwInfo dom_pw_info;
+ struct samr_PwInfo info;
+
+ struct lsa_String domain_name;
+ NTSTATUS crypt_status;
+
+ char *newpass;
+ int policy_min_pw_len = 0;
+
+ domain_name.string = "";
+ dom_pw_info.in.domain_name = &domain_name;
+ dom_pw_info.out.info = &info;
+
+ torture_comment(tctx, "Testing ChangePasswordUser2 on %s\n", acct_name);
+
+ oldpass = password;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &dom_pw_info),
+ "GetDomPwInfo failed");
+ if (NT_STATUS_IS_OK(dom_pw_info.out.result)) {
+ policy_min_pw_len = dom_pw_info.out.info->min_password_length;
+ }
+
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+
+ server.string = talloc_asprintf(tctx, "\\\\%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);
+
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &old_lm_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd,
+ lm_pass.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ crypt_status = init_samr_CryptPassword(newpass,
+ &old_nt_hash_blob,
+ &nt_pass);
+ torture_assert_ntstatus_ok(tctx,
+ crypt_status,
+ "init_samr_CryptPassword failed");
+
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser2_r(b, tctx, &r),
+ "ChangePasswordUser2 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ torture_comment(tctx, "ChangePasswordUser2 returned: %s perhaps min password age? (not fatal)\n", nt_errstr(r.out.result));
+ } else {
+ torture_assert_ntstatus_equal(tctx, r.out.result, status, "ChangePasswordUser2 returned unexpected value");
+ }
+
+ return true;
+}
+
+
+bool test_ChangePasswordUser3(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *account_string,
+ int policy_min_pw_len,
+ char **password,
+ const char *newpass,
+ NTTIME last_password_change,
+ bool handle_reject_reason)
+{
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16];
+ uint8_t old_lm_hash[16], new_lm_hash[16];
+ NTTIME t;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct userPwdChangeFailureInformation *reject = NULL;
+ DATA_BLOB old_nt_hash_blob = data_blob_const(old_nt_hash, 16);
+ NTSTATUS status;
+
+ torture_comment(tctx, "Testing ChangePasswordUser3\n");
+
+ if (newpass == NULL) {
+ do {
+ if (policy_min_pw_len == 0) {
+ newpass = samr_rand_pass(tctx, policy_min_pw_len);
+ } else {
+ newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len);
+ }
+ } while (check_password_quality(newpass) == false);
+ } else {
+ torture_comment(tctx, "Using password '%s'\n", newpass);
+ }
+
+ torture_assert(tctx, *password != NULL,
+ "Failing ChangePasswordUser3 as old password was NULL. Previous test failed?");
+
+ oldpass = *password;
+ server.string = talloc_asprintf(tctx, "\\\\%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);
+
+ /*
+ * The new plaintext password is encrypted using RC4 with the
+ * old NT password hash (directly, with no confounder). The
+ * password is at the end of the random padded buffer,
+ * offering a little protection.
+ *
+ * This is almost certainly wrong, it should be the old LM
+ * hash, it was switched in an unrelated commit
+ * 579c13da43d5b40ac6d6c1436399fbc1d8dfd054 in 2004.
+ */
+ status = init_samr_CryptPassword(newpass,
+ &old_nt_hash_blob,
+ &lm_pass);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword");
+
+ /*
+ * Now we prepare a DES cross-hash of the old LM and new NT
+ * passwords to link the two buffers
+ */
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ /*
+ * The new plaintext password is also encrypted using RC4 with
+ * the old NT password hash (directly, with no confounder).
+ * The password is at the end of the random padded buffer,
+ * offering a little protection.
+ */
+ status = init_samr_CryptPassword(newpass,
+ &old_nt_hash_blob,
+ &nt_pass);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword");
+
+ /*
+ * Another DES based cross-hash
+ */
+ 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;
+ r.out.dominfo = &dominfo;
+ r.out.reject = &reject;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r),
+ "ChangePasswordUser3 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) &&
+ (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD))) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalid password verifier - %s\n",
+ nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ status = init_samr_CryptPassword(newpass,
+ &old_nt_hash_blob,
+ &lm_pass);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword");
+
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ /* Break the NT Hash */
+ old_nt_hash[0]++;
+
+ status = init_samr_CryptPassword(newpass,
+ &old_nt_hash_blob,
+ &nt_pass);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword");
+
+ /* 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;
+ r.out.dominfo = &dominfo;
+ r.out.reject = &reject;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r),
+ "ChangePasswordUser3 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION) &&
+ (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD))) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed, should have returned WRONG_PASSWORD (or at least 'PASSWORD_RESTRICTON') for invalidly encrypted password - %s\n",
+ nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ /* This shouldn't be a valid name */
+ init_lsa_String(&account_bad, talloc_asprintf(tctx, "%sXX", account_string));
+
+ r.in.account = &account_bad;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r),
+ "ChangePasswordUser3 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_WRONG_PASSWORD)) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed, should have returned WRONG_PASSWORD for invalid username - %s\n",
+ nt_errstr(r.out.result));
+ 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);
+
+ status = init_samr_CryptPassword(newpass,
+ &old_nt_hash_blob,
+ &lm_pass);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword");
+
+ E_old_pw_hash(new_nt_hash, old_lm_hash, lm_verifier.hash);
+
+ status = init_samr_CryptPassword(newpass,
+ &old_nt_hash_blob,
+ &nt_pass);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword");
+
+ 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;
+ r.out.dominfo = &dominfo;
+ r.out.reject = &reject;
+
+ unix_to_nt_time(&t, time(NULL));
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r),
+ "ChangePasswordUser3 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ torture_comment(tctx, "(%s): dominfo[%s], reject[%s], handle_reject_reason[%s], "
+ "last_password_change[%s], dominfo->min_password_age[%lld]\n",
+ __location__,
+ (dominfo == NULL)? "NULL" : "present",
+ reject ? "true" : "false",
+ handle_reject_reason ? "true" : "false",
+ null_nttime(last_password_change) ? "null" : "not null",
+ dominfo ? (long long)dominfo->min_password_age : (long long)0);
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)
+ && dominfo
+ && reject
+ && handle_reject_reason
+ && (!null_nttime(last_password_change) || !dominfo->min_password_age)) {
+ if (dominfo->password_properties & DOMAIN_REFUSE_PASSWORD_CHANGE ) {
+
+ if (reject && (reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR)) {
+ torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n",
+ SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason);
+ return false;
+ }
+ }
+
+ /* We tested the order of precendence which is as follows:
+
+ * pwd min_age
+ * pwd length
+ * pwd complexity
+ * pwd history
+
+ Guenther */
+
+ if ((dominfo->min_password_age < 0) && !null_nttime(last_password_change) &&
+ (last_password_change - dominfo->min_password_age > t)) {
+
+ if (reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) {
+ torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n",
+ SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason);
+ return false;
+ }
+
+ } else if ((dominfo->min_password_length > 0) &&
+ (strlen(newpass) < dominfo->min_password_length)) {
+
+ if (reject->extendedFailureReason != SAM_PWD_CHANGE_PASSWORD_TOO_SHORT) {
+ torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_PASSWORD_TOO_SHORT (%d), got %d\n",
+ SAM_PWD_CHANGE_PASSWORD_TOO_SHORT, reject->extendedFailureReason);
+ return false;
+ }
+
+ } else if ((dominfo->password_history_length > 0) &&
+ strequal(oldpass, newpass)) {
+
+ if (reject->extendedFailureReason != SAM_PWD_CHANGE_PWD_IN_HISTORY) {
+ torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_PWD_IN_HISTORY (%d), got %d\n",
+ SAM_PWD_CHANGE_PWD_IN_HISTORY, reject->extendedFailureReason);
+ return false;
+ }
+ } else if (dominfo->password_properties & DOMAIN_PASSWORD_COMPLEX) {
+
+ if (reject->extendedFailureReason != SAM_PWD_CHANGE_NOT_COMPLEX) {
+ torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NOT_COMPLEX (%d), got %d\n",
+ SAM_PWD_CHANGE_NOT_COMPLEX, reject->extendedFailureReason);
+ return false;
+ }
+
+ }
+
+ if (reject->extendedFailureReason == SAM_PWD_CHANGE_PASSWORD_TOO_SHORT) {
+ /* retry with adjusted size */
+ return test_ChangePasswordUser3(p, tctx, account_string,
+ dominfo->min_password_length,
+ password, NULL, 0, false);
+
+ }
+
+ } else if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ if (reject && reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) {
+ torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n",
+ SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason);
+ return false;
+ }
+ /* Perhaps the server has a 'min password age' set? */
+
+ } else {
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ChangePasswordUser3");
+
+ *password = talloc_strdup(tctx, newpass);
+ }
+
+ return ret;
+}
+
+bool test_ChangePasswordUser4(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ const char *account_string,
+ int policy_min_pw_len,
+ char **password,
+ const char *newpassword)
+{
+#ifdef HAVE_GNUTLS_PBKDF2
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct samr_ChangePasswordUser4 r;
+ const char *oldpassword = *password;
+ char *srv_str = NULL;
+ struct lsa_String server;
+ struct lsa_String account;
+ uint8_t old_nt_key_data[16] = {0};
+ gnutls_datum_t old_nt_key = {
+ .data = old_nt_key_data,
+ .size = sizeof(old_nt_key),
+ };
+ uint8_t cek_data[16] = {0};
+ DATA_BLOB cek = {
+ .data = cek_data,
+ .length = sizeof(cek_data),
+ };
+ uint8_t pw_data[514] = {0};
+ DATA_BLOB plaintext = {
+ .data = pw_data,
+ .length = sizeof(pw_data),
+ };
+ DATA_BLOB ciphertext = data_blob_null;
+ struct samr_EncryptedPasswordAES pwd_buf = {.cipher_len = 0};
+ DATA_BLOB iv = {
+ .data = pwd_buf.salt,
+ .length = sizeof(pwd_buf.salt),
+ };
+ gnutls_datum_t iv_datum = {
+ .data = iv.data,
+ .size = iv.length,
+ };
+ uint64_t pbkdf2_iterations = generate_random_u64_range(5000, 1000000);
+ NTSTATUS status;
+ bool ok;
+ int rc;
+
+ torture_comment(tctx, "Testing ChangePasswordUser4\n");
+
+ if (newpassword == NULL) {
+ do {
+ if (policy_min_pw_len == 0) {
+ newpassword =
+ samr_rand_pass(tctx, policy_min_pw_len);
+ } else {
+ newpassword = samr_rand_pass_fixed_len(
+ tctx,
+ policy_min_pw_len);
+ }
+ } while (check_password_quality(newpassword) == false);
+ } else {
+ torture_comment(tctx, "Using password '%s'\n", newpassword);
+ }
+
+ torture_assert_not_null(tctx,
+ *password,
+ "Failing ChangePasswordUser4 as old password "
+ "was NULL. Previous test failed?");
+
+ srv_str = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ torture_assert_not_null(tctx, srv_str, "srvstr is NULL");
+ init_lsa_String(&server, srv_str);
+
+ init_lsa_String(&account, account_string);
+
+ E_md4hash(oldpassword, old_nt_key_data);
+
+ generate_nonce_buffer(iv.data, iv.length);
+
+ rc = gnutls_pbkdf2(GNUTLS_MAC_SHA512,
+ &old_nt_key,
+ &iv_datum,
+ pbkdf2_iterations,
+ cek.data,
+ cek.length);
+ torture_assert_int_equal(tctx, rc, 0, "gnutls_pbkdf2 failed");
+
+ ok = encode_pwd_buffer514_from_str(pw_data, newpassword, STR_UNICODE);
+ torture_assert(tctx, ok, "encode_aes_pw_buffer failed");
+
+ status = samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt(
+ tctx,
+ &plaintext,
+ &cek,
+ &samr_aes256_enc_key_salt,
+ &samr_aes256_mac_key_salt,
+ &iv,
+ &ciphertext,
+ pwd_buf.auth_data);
+ torture_assert_ntstatus_ok(
+ tctx,
+ status,
+ "samba_gnutls_aead_aes_256_cbc_hmac_sha512_encrypt failed");
+
+ pwd_buf.cipher_len = ciphertext.length;
+ pwd_buf.cipher = ciphertext.data;
+ pwd_buf.PBKDF2Iterations = pbkdf2_iterations;
+
+ r.in.server = &server;
+ r.in.account = &account;
+ r.in.password = &pwd_buf;
+
+ status = dcerpc_samr_ChangePasswordUser4_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ChangePasswordUser4 failed");
+
+ *password = talloc_strdup(tctx, newpassword);
+#endif /* HAVE_GNUTLS_PBKDF2 */
+ return true;
+}
+
+bool test_ChangePasswordRandomBytes(struct dcerpc_pipe *p, struct torture_context *tctx,
+ const char *account_string,
+ struct policy_handle *handle,
+ char **password)
+{
+ NTSTATUS status;
+ struct samr_ChangePasswordUser3 r;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ DATA_BLOB session_key;
+
+ bool ret = true;
+ struct lsa_String server, account;
+ struct samr_CryptPassword nt_pass;
+ struct samr_Password nt_verifier;
+ DATA_BLOB new_random_pass;
+ char *newpass;
+ char *oldpass;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint8_t old_nt_hash[16] = { 0 }, new_nt_hash[16];
+ DATA_BLOB old_nt_hash_blob
+ = data_blob_const(old_nt_hash,
+ sizeof(old_nt_hash));
+ NTTIME t;
+ struct samr_DomInfo1 *dominfo = NULL;
+ struct userPwdChangeFailureInformation *reject = NULL;
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ uint8_t _confounder[16] = {0};
+ DATA_BLOB confounder
+ = data_blob_const(_confounder,
+ sizeof(_confounder));
+ DATA_BLOB pw_data;
+ gnutls_datum_t old_nt_key = {
+ .data = old_nt_hash,
+ .size = sizeof(old_nt_hash),
+ };
+
+ new_random_pass = samr_very_rand_pass(tctx, 128);
+
+ torture_assert(tctx, *password != NULL,
+ "Failing ChangePasswordUser3 as old password was NULL. Previous test failed?");
+
+ oldpass = *password;
+ server.string = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ init_lsa_String(&account, account_string);
+
+ s.in.user_handle = handle;
+ s.in.info = &u;
+ s.in.level = 25;
+
+ ZERO_STRUCT(u);
+
+ u.info25.info.fields_present = SAMR_FIELD_NT_PASSWORD_PRESENT;
+
+ set_pw_in_buffer(u.info25.password.data, &new_random_pass);
+
+ pw_data = data_blob_const(u.info25.password.data, 516);
+
+ status = dcerpc_fetch_session_key(p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ return false;
+ }
+
+ generate_random_buffer(_confounder,
+ sizeof(_confounder));
+
+ samba_gnutls_arcfour_confounded_md5(&confounder,
+ &session_key,
+ &pw_data,
+ SAMBA_GNUTLS_ENCRYPT);
+
+ memcpy(&u.info25.password.data[516], _confounder, sizeof(_confounder));
+
+ torture_comment(tctx, "Testing SetUserInfo level 25 (set password ex) with a password made up of only random bytes\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &s),
+ "SetUserInfo failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, "RANDOM", nt_errstr(s.out.result));
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetUserInfo level %u failed - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ ret = false;
+ }
+
+ torture_comment(tctx, "Testing ChangePasswordUser3 with a password made up of only random bytes\n");
+
+ mdfour(old_nt_hash, new_random_pass.data, new_random_pass.length);
+
+ new_random_pass = samr_very_rand_pass(tctx, 128);
+
+ mdfour(new_nt_hash, new_random_pass.data, new_random_pass.length);
+
+ set_pw_in_buffer(nt_pass.data, &new_random_pass);
+
+ gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &old_nt_key,
+ NULL);
+ gnutls_cipher_encrypt(cipher_hnd,
+ nt_pass.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+
+ 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 = 0;
+ r.in.lm_password = NULL;
+ r.in.lm_verifier = NULL;
+ r.in.password3 = NULL;
+ r.out.dominfo = &dominfo;
+ r.out.reject = &reject;
+
+ unix_to_nt_time(&t, time(NULL));
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r),
+ "ChangePasswordUser3 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, "RANDOM", nt_errstr(r.out.result));
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ if (reject && reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) {
+ torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n",
+ SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason);
+ return false;
+ }
+ /* Perhaps the server has a 'min password age' set? */
+
+ } else if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "ChangePasswordUser3 failed - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ newpass = samr_rand_pass(tctx, 128);
+
+ mdfour(old_nt_hash, new_random_pass.data, new_random_pass.length);
+
+ E_md4hash(newpass, new_nt_hash);
+
+ status = init_samr_CryptPassword(newpass,
+ &old_nt_hash_blob,
+ &nt_pass);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword failed");
+
+ 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 = 0;
+ r.in.lm_password = NULL;
+ r.in.lm_verifier = NULL;
+ r.in.password3 = NULL;
+ r.out.dominfo = &dominfo;
+ r.out.reject = &reject;
+
+ unix_to_nt_time(&t, time(NULL));
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_ChangePasswordUser3_r(b, tctx, &r),
+ "ChangePasswordUser3 failed");
+ torture_comment(tctx, "(%s:%s) old_password[%s] new_password[%s] status[%s]\n",
+ __location__, __FUNCTION__,
+ oldpass, newpass, nt_errstr(r.out.result));
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_PASSWORD_RESTRICTION)) {
+ if (reject && reject->extendedFailureReason != SAM_PWD_CHANGE_NO_ERROR) {
+ torture_result(tctx, TORTURE_FAIL, "expected SAM_PWD_CHANGE_NO_ERROR (%d), got %d\n",
+ SAM_PWD_CHANGE_NO_ERROR, reject->extendedFailureReason);
+ return false;
+ }
+ /* Perhaps the server has a 'min password age' set? */
+
+ } else {
+ torture_assert_ntstatus_ok(tctx, r.out.result, "ChangePasswordUser3 (on second random password)");
+ *password = talloc_strdup(tctx, newpass);
+ }
+
+ return ret;
+}
+
+
+static bool test_GetMembersInAlias(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *alias_handle)
+{
+ struct samr_GetMembersInAlias r;
+ struct lsa_SidArray sids;
+
+ torture_comment(tctx, "Testing GetMembersInAlias\n");
+
+ r.in.alias_handle = alias_handle;
+ r.out.sids = &sids;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetMembersInAlias_r(b, tctx, &r),
+ "GetMembersInAlias failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "GetMembersInAlias failed");
+
+ return true;
+}
+
+static bool test_AddMemberToAlias(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *alias_handle,
+ const struct dom_sid *domain_sid)
+{
+ struct samr_AddAliasMember r;
+ struct samr_DeleteAliasMember d;
+ struct dom_sid *sid;
+
+ sid = dom_sid_add_rid(tctx, domain_sid, 512);
+
+ torture_comment(tctx, "Testing AddAliasMember\n");
+ r.in.alias_handle = alias_handle;
+ r.in.sid = sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddAliasMember_r(b, tctx, &r),
+ "AddAliasMember failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "AddAliasMember failed");
+
+ d.in.alias_handle = alias_handle;
+ d.in.sid = sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteAliasMember_r(b, tctx, &d),
+ "DeleteAliasMember failed");
+ torture_assert_ntstatus_ok(tctx, d.out.result, "DelAliasMember failed");
+
+ return true;
+}
+
+static bool test_AddMultipleMembersToAlias(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *alias_handle)
+{
+ struct samr_AddMultipleMembersToAlias a;
+ struct samr_RemoveMultipleMembersFromAlias r;
+ struct lsa_SidArray sids;
+
+ torture_comment(tctx, "Testing AddMultipleMembersToAlias\n");
+ a.in.alias_handle = alias_handle;
+ a.in.sids = &sids;
+
+ sids.num_sids = 3;
+ sids.sids = talloc_array(tctx, struct lsa_SidPtr, 3);
+
+ sids.sids[0].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-1");
+ sids.sids[1].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-2");
+ sids.sids[2].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-3");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddMultipleMembersToAlias_r(b, tctx, &a),
+ "AddMultipleMembersToAlias failed");
+ torture_assert_ntstatus_ok(tctx, a.out.result, "AddMultipleMembersToAlias");
+
+
+ torture_comment(tctx, "Testing RemoveMultipleMembersFromAlias\n");
+ r.in.alias_handle = alias_handle;
+ r.in.sids = &sids;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMultipleMembersFromAlias_r(b, tctx, &r),
+ "RemoveMultipleMembersFromAlias failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "RemoveMultipleMembersFromAlias failed");
+
+ /* strange! removing twice doesn't give any error */
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMultipleMembersFromAlias_r(b, tctx, &r),
+ "RemoveMultipleMembersFromAlias failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "RemoveMultipleMembersFromAlias failed");
+
+ /* but removing an alias that isn't there does */
+ sids.sids[2].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-4");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMultipleMembersFromAlias_r(b, tctx, &r),
+ "RemoveMultipleMembersFromAlias failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND, "RemoveMultipleMembersFromAlias");
+
+ return true;
+}
+
+static bool test_GetAliasMembership(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle)
+{
+ struct samr_GetAliasMembership r;
+ struct lsa_SidArray sids;
+ struct samr_Ids rids;
+
+ torture_comment(tctx, "Testing GetAliasMembership\n");
+
+ r.in.domain_handle = domain_handle;
+ r.in.sids = &sids;
+ r.out.rids = &rids;
+
+ sids.num_sids = 0;
+ sids.sids = talloc_zero_array(tctx, struct lsa_SidPtr, sids.num_sids);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetAliasMembership_r(b, tctx, &r),
+ "GetAliasMembership failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_GetAliasMembership failed");
+
+ torture_assert_int_equal(tctx, sids.num_sids, rids.count,
+ "protocol misbehaviour");
+
+ sids.num_sids = 1;
+ sids.sids = talloc_zero_array(tctx, struct lsa_SidPtr, sids.num_sids);
+ sids.sids[0].sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1-2-3-1");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetAliasMembership_r(b, tctx, &r),
+ "samr_GetAliasMembership failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_GetAliasMembership failed");
+
+#if 0
+ /* only true for w2k8 it seems
+ * win7, xp, w2k3 will return a 0 length array pointer */
+
+ if (rids.ids && (rids.count == 0)) {
+ torture_fail(tctx, "samr_GetAliasMembership returned 0 count and a rids array");
+ }
+#endif
+ if (!rids.ids && rids.count) {
+ torture_fail(tctx, "samr_GetAliasMembership returned non-0 count but no rids");
+ }
+
+ return true;
+}
+
+static bool test_TestPrivateFunctionsUser(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *user_handle)
+{
+ struct samr_TestPrivateFunctionsUser r;
+
+ torture_comment(tctx, "Testing TestPrivateFunctionsUser\n");
+
+ r.in.user_handle = user_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_TestPrivateFunctionsUser_r(b, tctx, &r),
+ "TestPrivateFunctionsUser failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_IMPLEMENTED, "TestPrivateFunctionsUser");
+
+ return true;
+}
+
+static bool test_QueryUserInfo_pwdlastset(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ bool use_info2,
+ NTTIME *pwdlastset)
+{
+ NTSTATUS status;
+ uint16_t levels[] = { /* 3, */ 5, 21 };
+ int i;
+ /* NTTIME pwdlastset3 = 0; */
+ NTTIME pwdlastset5 = 0;
+ NTTIME pwdlastset21 = 0;
+
+ torture_comment(tctx, "Testing QueryUserInfo%s level 5 and 21 call ",
+ use_info2 ? "2":"");
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ struct samr_QueryUserInfo r;
+ struct samr_QueryUserInfo2 r2;
+ union samr_UserInfo *info;
+
+ if (use_info2) {
+ r2.in.user_handle = handle;
+ r2.in.level = levels[i];
+ r2.out.info = &info;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo2_r(b, tctx, &r2),
+ "QueryUserInfo2 failed");
+ status = r2.out.result;
+
+ } else {
+ r.in.user_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
+ "QueryUserInfo failed");
+ status = r.out.result;
+ }
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo%s level %u failed - %s\n",
+ use_info2 ? "2":"", levels[i], nt_errstr(status));
+ return false;
+ }
+
+ switch (levels[i]) {
+ case 3:
+ /* pwdlastset3 = info->info3.last_password_change; */
+ break;
+ case 5:
+ pwdlastset5 = info->info5.last_password_change;
+ break;
+ case 21:
+ pwdlastset21 = info->info21.last_password_change;
+ break;
+ default:
+ return false;
+ }
+ }
+ /* torture_assert_int_equal(tctx, pwdlastset3, pwdlastset5,
+ "pwdlastset mixup"); */
+ torture_assert_int_equal(tctx, pwdlastset5, pwdlastset21,
+ "pwdlastset mixup");
+
+ *pwdlastset = pwdlastset21;
+
+ torture_comment(tctx, "(pwdlastset: %llu)\n",
+ (unsigned long long) *pwdlastset);
+
+ return true;
+}
+
+static bool test_SamLogon(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials,
+ struct cli_credentials *test_credentials,
+ NTSTATUS expected_result,
+ bool interactive)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogonEx r;
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative;
+ struct netr_IdentityInfo identity;
+ struct netr_NetworkInfo ninfo;
+ struct netr_PasswordInfo pinfo;
+ DATA_BLOB names_blob, chal, lm_resp, nt_resp;
+ int flags = CLI_CRED_NTLM_AUTH;
+ uint32_t samlogon_flags = 0;
+ struct netlogon_creds_CredentialState *creds;
+ struct netr_Authenticator a;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx, (creds = cli_credentials_get_netlogon_creds(machine_credentials)), "");
+
+ if (lpcfg_client_lanman_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lpcfg_client_ntlmv2_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ cli_credentials_get_ntlm_username_domain(test_credentials, tctx,
+ &identity.account_name.string,
+ &identity.domain_name.string);
+
+ identity.parameter_control =
+ MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT |
+ MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
+ identity.logon_id = 0;
+ identity.workstation.string = cli_credentials_get_workstation(test_credentials);
+
+ if (interactive) {
+ netlogon_creds_client_authenticator(creds, &a);
+
+ if (!E_deshash(cli_credentials_get_password(test_credentials), pinfo.lmpassword.hash)) {
+ ZERO_STRUCT(pinfo.lmpassword.hash);
+ }
+ E_md4hash(cli_credentials_get_password(test_credentials), pinfo.ntpassword.hash);
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_encrypt(creds, pinfo.lmpassword.hash, 16);
+ netlogon_creds_aes_encrypt(creds, pinfo.ntpassword.hash, 16);
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ netlogon_creds_arcfour_crypt(creds, pinfo.lmpassword.hash, 16);
+ netlogon_creds_arcfour_crypt(creds, pinfo.ntpassword.hash, 16);
+ } else {
+ netlogon_creds_des_encrypt(creds, &pinfo.lmpassword);
+ netlogon_creds_des_encrypt(creds, &pinfo.ntpassword);
+ }
+
+ pinfo.identity_info = identity;
+ logon.password = &pinfo;
+
+ r.in.logon_level = NetlogonInteractiveInformation;
+ } else {
+ generate_random_buffer(ninfo.challenge,
+ sizeof(ninfo.challenge));
+ chal = data_blob_const(ninfo.challenge,
+ sizeof(ninfo.challenge));
+
+ names_blob = NTLMv2_generate_names_blob(tctx, cli_credentials_get_workstation(test_credentials),
+ cli_credentials_get_domain(test_credentials));
+
+ status = cli_credentials_get_ntlm_response(test_credentials, tctx,
+ &flags,
+ chal,
+ NULL, /* server_timestamp */
+ 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 = identity;
+ logon.network = &ninfo;
+
+ r.in.logon_level = NetlogonNetworkInformation;
+ }
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = cli_credentials_get_workstation(test_credentials);
+ r.in.logon = &logon;
+ r.in.flags = &samlogon_flags;
+ r.out.flags = &samlogon_flags;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+
+ torture_comment(tctx, "Testing LogonSamLogon with name %s\n", identity.account_name.string);
+
+ r.in.validation_level = 6;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r),
+ "netr_LogonSamLogonEx failed");
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) {
+ r.in.validation_level = 3;
+ torture_assert_ntstatus_ok(tctx, dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r),
+ "netr_LogonSamLogonEx failed");
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_assert_ntstatus_equal(tctx, r.out.result, expected_result, "LogonSamLogonEx failed");
+ return true;
+ } else {
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LogonSamLogonEx failed");
+ }
+
+ return true;
+}
+
+static bool test_SamLogon_with_creds(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_creds,
+ const char *acct_name,
+ const char *password,
+ NTSTATUS expected_samlogon_result,
+ bool interactive)
+{
+ bool ret = true;
+ struct cli_credentials *test_credentials;
+
+ test_credentials = cli_credentials_init(tctx);
+
+ cli_credentials_set_workstation(test_credentials,
+ cli_credentials_get_workstation(machine_creds), CRED_SPECIFIED);
+ cli_credentials_set_domain(test_credentials,
+ cli_credentials_get_domain(machine_creds), CRED_SPECIFIED);
+ cli_credentials_set_username(test_credentials,
+ acct_name, CRED_SPECIFIED);
+ cli_credentials_set_password(test_credentials,
+ password, CRED_SPECIFIED);
+
+ torture_comment(tctx, "Testing samlogon (%s) as %s password: %s\n",
+ interactive ? "interactive" : "network", acct_name, password);
+
+ if (!test_SamLogon(tctx, p, machine_creds, test_credentials,
+ expected_samlogon_result, interactive)) {
+ torture_result(tctx, TORTURE_FAIL, "new password did not work\n");
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_SetPassword_level(struct dcerpc_pipe *p,
+ struct dcerpc_pipe *np,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint16_t level,
+ uint32_t fields_present,
+ uint8_t password_expired,
+ bool *matched_expected_error,
+ bool use_setinfo2,
+ const char *acct_name,
+ char **password,
+ struct cli_credentials *machine_creds,
+ bool use_queryinfo2,
+ NTTIME *pwdlastset,
+ NTSTATUS expected_samlogon_result)
+{
+ const char *fields = NULL;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ switch (level) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ fields = talloc_asprintf(tctx, "(fields_present: 0x%08x)",
+ fields_present);
+ break;
+ default:
+ break;
+ }
+
+ torture_comment(tctx, "Testing SetUserInfo%s level %d call "
+ "(password_expired: %d) %s\n",
+ use_setinfo2 ? "2":"", level, password_expired,
+ fields ? fields : "");
+
+ if (!test_SetUserPass_level_ex(p, tctx, handle, level,
+ fields_present,
+ password,
+ password_expired,
+ use_setinfo2,
+ matched_expected_error)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo_pwdlastset(b, tctx, handle,
+ use_queryinfo2,
+ pwdlastset)) {
+ ret = false;
+ }
+
+ if (*matched_expected_error == true) {
+ return ret;
+ }
+
+ if (!test_SamLogon_with_creds(tctx, np,
+ machine_creds,
+ acct_name,
+ *password,
+ expected_samlogon_result,
+ false)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool setup_schannel_netlogon_pipe(struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct dcerpc_pipe **p)
+{
+ struct dcerpc_binding *b;
+ NTSTATUS status;
+
+ torture_assert_ntstatus_ok(tctx, torture_rpc_binding(tctx, &b),
+ "failed to get rpc binding");
+
+ /* We have to use schannel, otherwise the SamLogonEx fails
+ * with INTERNAL_ERROR */
+
+ status = dcerpc_binding_set_flags(b,
+ DCERPC_SCHANNEL |
+ DCERPC_SIGN | DCERPC_SEAL |
+ DCERPC_SCHANNEL_AUTO,
+ DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(tctx, status, "set flags");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, p, b, &ndr_table_netlogon,
+ credentials, tctx->ev, tctx->lp_ctx),
+ "failed to bind to netlogon");
+
+ return true;
+}
+
+static bool test_SetPassword_pwdlastset(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ uint32_t acct_flags,
+ const char *acct_name,
+ struct policy_handle *handle,
+ char **password,
+ struct cli_credentials *machine_credentials)
+{
+ int s = 0, q = 0, f = 0, l = 0, z = 0;
+ bool ret = true;
+ int delay = 50000;
+ bool set_levels[] = { false, true };
+ bool query_levels[] = { false, true };
+ uint32_t levels[] = { 18, 21, 26, 23, 24, 25, 31 }; /* Second half only used when TEST_ALL_LEVELS defined */
+ uint32_t nonzeros[] = { 1, 24 };
+ uint32_t fields_present[] = {
+ 0,
+ SAMR_FIELD_EXPIRED_FLAG,
+ SAMR_FIELD_LAST_PWD_CHANGE,
+ SAMR_FIELD_EXPIRED_FLAG | SAMR_FIELD_LAST_PWD_CHANGE,
+ SAMR_FIELD_COMMENT,
+ SAMR_FIELD_NT_PASSWORD_PRESENT,
+ SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LAST_PWD_CHANGE,
+ SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT,
+ SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT | SAMR_FIELD_LAST_PWD_CHANGE,
+ SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_EXPIRED_FLAG,
+ SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT | SAMR_FIELD_EXPIRED_FLAG,
+ SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT | SAMR_FIELD_LAST_PWD_CHANGE | SAMR_FIELD_EXPIRED_FLAG
+ };
+ struct dcerpc_pipe *np = NULL;
+
+ if (torture_setting_bool(tctx, "samba3", false) ||
+ torture_setting_bool(tctx, "samba4", false)) {
+ delay = 999999;
+ torture_comment(tctx, "Samba3 has second granularity, setting delay to: %d\n",
+ delay);
+ }
+
+ torture_assert(tctx, setup_schannel_netlogon_pipe(tctx, machine_credentials, &np), "");
+
+ /* set to 1 to enable testing for all possible opcode
+ (SetUserInfo, SetUserInfo2, QueryUserInfo, QueryUserInfo2)
+ combinations */
+#if 0
+#define TEST_ALL_LEVELS 1
+#define TEST_SET_LEVELS 1
+#define TEST_QUERY_LEVELS 1
+#endif
+#ifdef TEST_ALL_LEVELS
+ for (l=0; l<ARRAY_SIZE(levels); l++) {
+#else
+ for (l=0; l<(ARRAY_SIZE(levels))/2; l++) {
+#endif
+ for (z=0; z<ARRAY_SIZE(nonzeros); z++) {
+ for (f=0; f<ARRAY_SIZE(fields_present); f++) {
+#ifdef TEST_SET_LEVELS
+ for (s=0; s<ARRAY_SIZE(set_levels); s++) {
+#endif
+#ifdef TEST_QUERY_LEVELS
+ for (q=0; q<ARRAY_SIZE(query_levels); q++) {
+#endif
+ NTTIME pwdlastset_old = 0;
+ NTTIME pwdlastset_new = 0;
+ bool matched_expected_error = false;
+ NTSTATUS expected_samlogon_result = NT_STATUS_ACCOUNT_DISABLED;
+
+ torture_comment(tctx, "------------------------------\n"
+ "Testing pwdLastSet attribute for flags: 0x%08x "
+ "(s: %d (l: %d), q: %d)\n",
+ acct_flags, s, levels[l], q);
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ if (!((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT))) {
+ expected_samlogon_result = NT_STATUS_WRONG_PASSWORD;
+ }
+ break;
+ }
+
+
+ /* set #1 */
+
+ /* set a password and force password change (pwdlastset 0) by
+ * setting the password expired flag to a non-0 value */
+
+ if (!test_SetPassword_level(p, np, tctx, handle,
+ levels[l],
+ fields_present[f],
+ nonzeros[z],
+ &matched_expected_error,
+ set_levels[s],
+ acct_name,
+ password,
+ machine_credentials,
+ query_levels[q],
+ &pwdlastset_new,
+ expected_samlogon_result)) {
+ ret = false;
+ }
+
+ if (matched_expected_error == true) {
+ /* skipping on expected failure */
+ continue;
+ }
+
+ /* pwdlastset must be 0 afterwards, except for a level 21, 23 and 25
+ * set without the SAMR_FIELD_EXPIRED_FLAG */
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ if ((pwdlastset_new != 0) &&
+ !(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG)) {
+ torture_comment(tctx, "not considering a non-0 "
+ "pwdLastSet as a an error as the "
+ "SAMR_FIELD_EXPIRED_FLAG has not "
+ "been set\n");
+ break;
+ }
+ break;
+ default:
+ if (pwdlastset_new != 0) {
+ torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: "
+ "expected pwdLastSet 0 but got %llu\n",
+ (unsigned long long) pwdlastset_old);
+ ret = false;
+ }
+ break;
+ }
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) &&
+ (pwdlastset_old > 0) && (pwdlastset_new > 0) &&
+ (pwdlastset_old >= pwdlastset_new)) {
+ torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n");
+ ret = false;
+ }
+ break;
+ }
+
+ pwdlastset_old = pwdlastset_new;
+
+ usleep(delay);
+
+ /* set #2 */
+
+ /* set a password, pwdlastset needs to get updated (increased
+ * value), password_expired value used here is 0 */
+
+ if (!test_SetPassword_level(p, np, tctx, handle,
+ levels[l],
+ fields_present[f],
+ 0,
+ &matched_expected_error,
+ set_levels[s],
+ acct_name,
+ password,
+ machine_credentials,
+ query_levels[q],
+ &pwdlastset_new,
+ expected_samlogon_result)) {
+ ret = false;
+ }
+
+ /* when a password has been changed, pwdlastset must not be 0 afterwards
+ * and must be larger then the old value */
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ /* SAMR_FIELD_EXPIRED_FLAG has not been set and no
+ * password has been changed, old and new pwdlastset
+ * need to be the same value */
+
+ if (!(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG) &&
+ !((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)))
+ {
+ torture_assert_int_equal(tctx, pwdlastset_old,
+ pwdlastset_new, "pwdlastset must be equal");
+ break;
+ }
+ break;
+ default:
+ if (pwdlastset_old >= pwdlastset_new) {
+ torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: "
+ "expected last pwdlastset (%llu) < new pwdlastset (%llu)\n",
+ (unsigned long long) pwdlastset_old,
+ (unsigned long long) pwdlastset_new);
+ ret = false;
+ }
+ if (pwdlastset_new == 0) {
+ torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: "
+ "expected non-0 pwdlastset, got: %llu\n",
+ (unsigned long long) pwdlastset_new);
+ ret = false;
+ }
+ break;
+ }
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) &&
+ (pwdlastset_old > 0) && (pwdlastset_new > 0) &&
+ (pwdlastset_old >= pwdlastset_new)) {
+ torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n");
+ ret = false;
+ }
+ break;
+ }
+
+ pwdlastset_old = pwdlastset_new;
+
+ usleep(delay);
+
+ /* set #2b */
+
+ /* set a password, pwdlastset needs to get updated (increased
+ * value), password_expired value used here is 0 */
+
+ if (!test_SetPassword_level(p, np, tctx, handle,
+ levels[l],
+ fields_present[f],
+ 0,
+ &matched_expected_error,
+ set_levels[s],
+ acct_name,
+ password,
+ machine_credentials,
+ query_levels[q],
+ &pwdlastset_new,
+ expected_samlogon_result)) {
+ ret = false;
+ }
+
+ /* when a password has been changed, pwdlastset must not be 0 afterwards
+ * and must be larger then the old value */
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ /* SAMR_FIELD_EXPIRED_FLAG has not been set and no
+ * password has been changed, old and new pwdlastset
+ * need to be the same value */
+
+ if (!(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG) &&
+ !((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)))
+ {
+ torture_assert_int_equal(tctx, pwdlastset_old,
+ pwdlastset_new, "pwdlastset must be equal");
+ break;
+ }
+ break;
+ default:
+ if (pwdlastset_old >= pwdlastset_new) {
+ torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: "
+ "expected last pwdlastset (%llu) < new pwdlastset (%llu)\n",
+ (unsigned long long) pwdlastset_old,
+ (unsigned long long) pwdlastset_new);
+ ret = false;
+ }
+ if (pwdlastset_new == 0) {
+ torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: "
+ "expected non-0 pwdlastset, got: %llu\n",
+ (unsigned long long) pwdlastset_new);
+ ret = false;
+ }
+ break;
+ }
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) &&
+ (pwdlastset_old > 0) && (pwdlastset_new > 0) &&
+ (pwdlastset_old >= pwdlastset_new)) {
+ torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n");
+ ret = false;
+ }
+ break;
+ }
+
+ pwdlastset_old = pwdlastset_new;
+
+ usleep(delay);
+
+ /* set #3 */
+
+ /* set a password and force password change (pwdlastset 0) by
+ * setting the password expired flag to a non-0 value */
+
+ if (!test_SetPassword_level(p, np, tctx, handle,
+ levels[l],
+ fields_present[f],
+ nonzeros[z],
+ &matched_expected_error,
+ set_levels[s],
+ acct_name,
+ password,
+ machine_credentials,
+ query_levels[q],
+ &pwdlastset_new,
+ expected_samlogon_result)) {
+ ret = false;
+ }
+
+ /* pwdlastset must be 0 afterwards, except for a level 21, 23 and 25
+ * set without the SAMR_FIELD_EXPIRED_FLAG */
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ if ((pwdlastset_new != 0) &&
+ !(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG)) {
+ torture_comment(tctx, "not considering a non-0 "
+ "pwdLastSet as a an error as the "
+ "SAMR_FIELD_EXPIRED_FLAG has not "
+ "been set\n");
+ break;
+ }
+
+ /* SAMR_FIELD_EXPIRED_FLAG has not been set and no
+ * password has been changed, old and new pwdlastset
+ * need to be the same value */
+
+ if (!(fields_present[f] & SAMR_FIELD_EXPIRED_FLAG) &&
+ !((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)))
+ {
+ torture_assert_int_equal(tctx, pwdlastset_old,
+ pwdlastset_new, "pwdlastset must be equal");
+ break;
+ }
+ break;
+ default:
+ if (pwdlastset_new != 0) {
+ torture_result(tctx, TORTURE_FAIL, "pwdLastSet test failed: "
+ "expected pwdLastSet 0, got %llu\n",
+ (unsigned long long) pwdlastset_old);
+ ret = false;
+ }
+ break;
+ }
+
+ switch (levels[l]) {
+ case 21:
+ case 23:
+ case 25:
+ case 32:
+ if (((fields_present[f] & SAMR_FIELD_NT_PASSWORD_PRESENT) ||
+ (fields_present[f] & SAMR_FIELD_LM_PASSWORD_PRESENT)) &&
+ (pwdlastset_old > 0) && (pwdlastset_new > 0) &&
+ (pwdlastset_old >= pwdlastset_new)) {
+ torture_result(tctx, TORTURE_FAIL, "pwdlastset not increasing\n");
+ ret = false;
+ }
+ break;
+ }
+
+ /* if the level we are testing does not have a fields_present
+ * field, skip all fields present tests by setting f to to
+ * arraysize */
+ switch (levels[l]) {
+ case 18:
+ case 24:
+ case 26:
+ case 31:
+ f = ARRAY_SIZE(fields_present);
+ break;
+ }
+
+#ifdef TEST_QUERY_LEVELS
+ }
+#endif
+#ifdef TEST_SET_LEVELS
+ }
+#endif
+ } /* fields present */
+ } /* nonzeros */
+ } /* levels */
+
+#undef TEST_SET_LEVELS
+#undef TEST_QUERY_LEVELS
+
+ talloc_free(np);
+
+ return ret;
+}
+
+static bool test_QueryUserInfo_badpwdcount(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t *badpwdcount)
+{
+ union samr_UserInfo *info;
+ struct samr_QueryUserInfo r;
+
+ r.in.user_handle = handle;
+ r.in.level = 3;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
+ "failed to query userinfo");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query userinfo");
+
+ *badpwdcount = info->info3.bad_password_count;
+
+ torture_comment(tctx, " (bad password count: %d)\n", *badpwdcount);
+
+ return true;
+}
+
+static bool test_SetUserInfo_acct_flags(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *user_handle,
+ uint32_t acct_flags)
+{
+ struct samr_SetUserInfo r;
+ union samr_UserInfo user_info;
+
+ torture_comment(tctx, "Testing SetUserInfo level 16\n");
+
+ user_info.info16.acct_flags = acct_flags;
+
+ r.in.user_handle = user_handle;
+ r.in.level = 16;
+ r.in.info = &user_info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetUserInfo_r(b, tctx, &r),
+ "failed to set account flags");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to set account flags");
+
+ return true;
+}
+
+static bool test_reset_badpwdcount(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *user_handle,
+ uint32_t acct_flags,
+ char **password)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password),
+ "failed to set password");
+
+ torture_comment(tctx, "Testing SetUserInfo level 16 (enable account)\n");
+
+ torture_assert(tctx,
+ test_SetUserInfo_acct_flags(b, tctx, user_handle,
+ acct_flags & ~ACB_DISABLED),
+ "failed to enable user");
+
+ torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password),
+ "failed to set password");
+
+ return true;
+}
+
+static bool test_SetDomainInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ enum samr_DomainInfoClass level,
+ union samr_DomainInfo *info)
+{
+ struct samr_SetDomainInfo r;
+
+ r.in.domain_handle = domain_handle;
+ r.in.level = level;
+ r.in.info = info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_SetDomainInfo_r(b, tctx, &r),
+ "failed to set domain info");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to set domain info");
+
+ return true;
+}
+
+static bool test_SetDomainInfo_ntstatus(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ enum samr_DomainInfoClass level,
+ union samr_DomainInfo *info,
+ NTSTATUS expected)
+{
+ struct samr_SetDomainInfo r;
+
+ r.in.domain_handle = domain_handle;
+ r.in.level = level;
+ r.in.info = info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &r),
+ "SetDomainInfo failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, expected, "");
+
+ return true;
+}
+
+static bool test_QueryDomainInfo2_level(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ enum samr_DomainInfoClass level,
+ union samr_DomainInfo **q_info)
+{
+ struct samr_QueryDomainInfo2 r;
+
+ r.in.domain_handle = domain_handle;
+ r.in.level = level;
+ r.out.info = q_info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo2_r(b, tctx, &r),
+ "failed to query domain info");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query domain info");
+
+ return true;
+}
+
+static bool test_Password_badpwdcount(struct dcerpc_pipe *p,
+ struct dcerpc_pipe *np,
+ struct torture_context *tctx,
+ uint32_t acct_flags,
+ const char *acct_name,
+ struct policy_handle *domain_handle,
+ struct policy_handle *user_handle,
+ char **password,
+ struct cli_credentials *machine_credentials,
+ const char *comment,
+ bool disable,
+ bool interactive,
+ NTSTATUS expected_success_status,
+ struct samr_DomInfo1 *info1,
+ struct samr_DomInfo12 *info12)
+{
+ union samr_DomainInfo info;
+ char **passwords;
+ int i;
+ uint32_t badpwdcount, tmp;
+ uint32_t password_history_length = 12;
+ uint32_t lockout_threshold = 15;
+ uint32_t lockout_seconds = 5;
+ uint64_t delta_time_factor = 10 * 1000 * 1000;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ lockout_seconds = 60;
+ }
+
+ torture_comment(tctx, "\nTesting bad pwd count with: %s\n", comment);
+
+ torture_assert(tctx, password_history_length < lockout_threshold,
+ "password history length needs to be smaller than account lockout threshold for this test");
+
+
+ /* set policies */
+
+ info.info1 = *info1;
+ info.info1.password_history_length = password_history_length;
+ info.info1.min_password_age = 0;
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainPasswordInformation, &info),
+ "failed to set password history length and min passwd age");
+
+ info.info12 = *info12;
+ info.info12.lockout_threshold = lockout_threshold;
+
+ /* set lockout duration of 5 seconds */
+ info.info12.lockout_duration = ~(lockout_seconds * delta_time_factor);
+ info.info12.lockout_window = ~(lockout_seconds * delta_time_factor);
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainLockoutInformation, &info),
+ "failed to set lockout threshold");
+
+ /* reset bad pwd count */
+
+ torture_assert(tctx,
+ test_reset_badpwdcount(p, tctx, user_handle, acct_flags, password), "");
+
+
+ /* enable or disable account */
+ if (disable) {
+ torture_assert(tctx,
+ test_SetUserInfo_acct_flags(b, tctx, user_handle,
+ acct_flags | ACB_DISABLED),
+ "failed to disable user");
+ } else {
+ torture_assert(tctx,
+ test_SetUserInfo_acct_flags(b, tctx, user_handle,
+ acct_flags & ~ACB_DISABLED),
+ "failed to enable user");
+ }
+
+
+ /* setup password history */
+
+ passwords = talloc_array(tctx, char *, password_history_length);
+
+ for (i=0; i < password_history_length; i++) {
+
+ torture_assert(tctx, test_SetUserPass(p, tctx, user_handle, password),
+ "failed to set password");
+ passwords[i] = talloc_strdup(tctx, *password);
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials,
+ acct_name, passwords[i],
+ expected_success_status, interactive)) {
+ torture_fail(tctx, "failed to auth with latest password");
+ }
+
+ torture_assert(tctx,
+ test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), "");
+
+ torture_assert_int_equal(tctx, badpwdcount, 0, "expected badpwdcount to be 0");
+ }
+
+
+ /* test with wrong password */
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials,
+ acct_name, "random_crap",
+ NT_STATUS_WRONG_PASSWORD, interactive)) {
+ torture_fail(tctx, "succeeded to authenticate with wrong password");
+ }
+
+ torture_assert(tctx,
+ test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), "");
+
+ torture_assert_int_equal(tctx, badpwdcount, 1, "expected badpwdcount to be 1");
+
+
+ /* test with latest good password */
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name,
+ passwords[password_history_length-1],
+ expected_success_status, interactive)) {
+ torture_fail(tctx, "succeeded to authenticate with wrong password");
+ }
+
+ torture_assert(tctx,
+ test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), "");
+
+ if (disable) {
+ torture_assert_int_equal(tctx, badpwdcount, 1, "expected badpwdcount to be 1");
+ } else {
+ /* only enabled accounts get the bad pwd count reset upon
+ * successful logon */
+ torture_assert_int_equal(tctx, badpwdcount, 0, "expected badpwdcount to be 0");
+ }
+
+ tmp = badpwdcount;
+
+
+ /* test password history */
+
+ for (i=0; i < password_history_length; i++) {
+
+ torture_comment(tctx, "Testing bad password count behavior with "
+ "password #%d of #%d\n", i, password_history_length);
+
+ /* - network samlogon will succeed auth and not
+ * increase badpwdcount for 2 last entries
+ * - interactive samlogon only for the last one */
+
+ if (i == password_history_length - 1 ||
+ (i == password_history_length - 2 && !interactive)) {
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials,
+ acct_name, passwords[i],
+ expected_success_status, interactive)) {
+ torture_fail(tctx, talloc_asprintf(tctx, "did not successfully to obtain %s for %s login with old password (#%d of #%d in history)",
+ nt_errstr(expected_success_status),
+ interactive ? "interactive" : "network", i, password_history_length));
+ }
+
+ torture_assert(tctx,
+ test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), "");
+
+ if (disable) {
+ /* torture_comment(tctx, "expecting bad pwd count to *NOT INCREASE* for pwd history entry %d\n", i); */
+ torture_assert_int_equal(tctx, badpwdcount, tmp, "unexpected badpwdcount");
+ } else {
+ /* torture_comment(tctx, "expecting bad pwd count to be 0 for pwd history entry %d\n", i); */
+ torture_assert_int_equal(tctx, badpwdcount, 0, "expected badpwdcount to be 0");
+ }
+
+ tmp = badpwdcount;
+
+ continue;
+ }
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials,
+ acct_name, passwords[i],
+ NT_STATUS_WRONG_PASSWORD, interactive)) {
+ torture_fail(tctx, talloc_asprintf(tctx, "succeeded to authenticate with old password (#%d of #%d in history)", i, password_history_length));
+ }
+
+ torture_assert(tctx,
+ test_QueryUserInfo_badpwdcount(b, tctx, user_handle, &badpwdcount), "");
+
+ /* - network samlogon will fail auth but not increase
+ * badpwdcount for 3rd last entry
+ * - interactive samlogon for 3rd and 2nd last entry */
+
+ if (i == password_history_length - 3 ||
+ (i == password_history_length - 2 && interactive)) {
+ /* torture_comment(tctx, "expecting bad pwd count to *NOT INCREASE * by one for pwd history entry %d\n", i); */
+ torture_assert_int_equal(tctx, badpwdcount, tmp, "unexpected badpwdcount");
+ } else {
+ /* torture_comment(tctx, "expecting bad pwd count to increase by one for pwd history entry %d\n", i); */
+ torture_assert_int_equal(tctx, badpwdcount, tmp + 1, "unexpected badpwdcount");
+ }
+
+ tmp = badpwdcount;
+ }
+
+ return true;
+}
+
+static bool test_Password_badpwdcount_wrap(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ uint32_t acct_flags,
+ const char *acct_name,
+ struct policy_handle *domain_handle,
+ struct policy_handle *user_handle,
+ char **password,
+ struct cli_credentials *machine_credentials)
+{
+ union samr_DomainInfo *q_info, s_info;
+ struct samr_DomInfo1 info1, _info1;
+ struct samr_DomInfo12 info12, _info12;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *np;
+ int i;
+
+ struct {
+ const char *comment;
+ bool disabled;
+ bool interactive;
+ NTSTATUS expected_success_status;
+ } creds[] = {
+ {
+ .comment = "network logon (disabled account)",
+ .disabled = true,
+ .interactive = false,
+ .expected_success_status= NT_STATUS_ACCOUNT_DISABLED
+ },
+ {
+ .comment = "network logon (enabled account)",
+ .disabled = false,
+ .interactive = false,
+ .expected_success_status= NT_STATUS_OK
+ },
+ {
+ .comment = "interactive logon (disabled account)",
+ .disabled = true,
+ .interactive = true,
+ .expected_success_status= NT_STATUS_ACCOUNT_DISABLED
+ },
+ {
+ .comment = "interactive logon (enabled account)",
+ .disabled = false,
+ .interactive = true,
+ .expected_success_status= NT_STATUS_OK
+ },
+ };
+
+ torture_assert(tctx, setup_schannel_netlogon_pipe(tctx, machine_credentials, &np), "");
+
+ /* backup old policies */
+
+ torture_assert(tctx,
+ test_QueryDomainInfo2_level(b, tctx, domain_handle,
+ DomainPasswordInformation, &q_info),
+ "failed to query domain info level 1");
+
+ info1 = q_info->info1;
+ _info1 = info1;
+
+ torture_assert(tctx,
+ test_QueryDomainInfo2_level(b, tctx, domain_handle,
+ DomainLockoutInformation, &q_info),
+ "failed to query domain info level 12");
+
+ info12 = q_info->info12;
+ _info12 = info12;
+
+ /* run tests */
+
+ for (i=0; i < ARRAY_SIZE(creds); i++) {
+
+ /* skip trust tests for now */
+ if (acct_flags & ACB_WSTRUST ||
+ acct_flags & ACB_SVRTRUST ||
+ acct_flags & ACB_DOMTRUST) {
+ continue;
+ }
+
+ if (!test_Password_badpwdcount(p, np, tctx, acct_flags, acct_name,
+ domain_handle, user_handle, password,
+ machine_credentials,
+ creds[i].comment,
+ creds[i].disabled,
+ creds[i].interactive,
+ creds[i].expected_success_status,
+ &_info1, &_info12)) {
+ torture_result(tctx, TORTURE_FAIL, "TEST #%d (%s) failed\n", i, creds[i].comment);
+ ret = false;
+ } else {
+ torture_comment(tctx, "TEST #%d (%s) succeeded\n", i, creds[i].comment);
+ }
+ }
+
+ /* restore policies */
+
+ s_info.info1 = info1;
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainPasswordInformation, &s_info),
+ "failed to set password information");
+
+ s_info.info12 = info12;
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainLockoutInformation, &s_info),
+ "failed to set lockout information");
+
+ return ret;
+}
+
+static bool test_QueryUserInfo_lockout(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ const char *acct_name,
+ uint16_t raw_bad_password_count,
+ uint16_t effective_bad_password_count,
+ uint32_t effective_acb_lockout)
+{
+ struct policy_handle user_handle;
+ union samr_UserInfo *i;
+ struct samr_QueryUserInfo r;
+
+ NTSTATUS status = test_OpenUser_byname(b, tctx, domain_handle, acct_name, &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ r.in.user_handle = &user_handle;
+ r.in.level = 3;
+ r.out.info = &i;
+ torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level);
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
+ "failed to query userinfo");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query userinfo");
+ torture_comment(tctx, " (acct_flags: 0x%08x) (raw_bad_pwd_count: %u)\n",
+ i->info3.acct_flags, i->info3.bad_password_count);
+ torture_assert_int_equal(tctx, i->info3.bad_password_count,
+ raw_bad_password_count,
+ "raw badpwdcount");
+ torture_assert_int_equal(tctx, i->info3.acct_flags & ACB_AUTOLOCK,
+ effective_acb_lockout,
+ "effective acb_lockout");
+ TALLOC_FREE(i);
+
+ r.in.user_handle = &user_handle;
+ r.in.level = 5;
+ r.out.info = &i;
+ torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level);
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
+ "failed to query userinfo");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query userinfo");
+ torture_comment(tctx, " (acct_flags: 0x%08x) (effective_bad_pwd_count: %u)\n",
+ i->info5.acct_flags, i->info5.bad_password_count);
+ torture_assert_int_equal(tctx, i->info5.bad_password_count,
+ effective_bad_password_count,
+ "effective badpwdcount");
+ torture_assert_int_equal(tctx, i->info5.acct_flags & ACB_AUTOLOCK,
+ effective_acb_lockout,
+ "effective acb_lockout");
+ TALLOC_FREE(i);
+
+ r.in.user_handle = &user_handle;
+ r.in.level = 16;
+ r.out.info = &i;
+ torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level);
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
+ "failed to query userinfo");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query userinfo");
+ torture_comment(tctx, " (acct_flags: 0x%08x)\n",
+ i->info16.acct_flags);
+ torture_assert_int_equal(tctx, i->info16.acct_flags & ACB_AUTOLOCK,
+ effective_acb_lockout,
+ "effective acb_lockout");
+ TALLOC_FREE(i);
+
+ r.in.user_handle = &user_handle;
+ r.in.level = 21;
+ r.out.info = &i;
+ torture_comment(tctx, "Testing QueryUserInfo level %d", r.in.level);
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
+ "failed to query userinfo");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query userinfo");
+ torture_comment(tctx, " (acct_flags: 0x%08x) (effective_bad_pwd_count: %u)\n",
+ i->info21.acct_flags, i->info21.bad_password_count);
+ torture_assert_int_equal(tctx, i->info21.bad_password_count,
+ effective_bad_password_count,
+ "effective badpwdcount");
+ torture_assert_int_equal(tctx, i->info21.acct_flags & ACB_AUTOLOCK,
+ effective_acb_lockout,
+ "effective acb_lockout");
+ TALLOC_FREE(i);
+
+ if (!test_samr_handle_Close(b, tctx, &user_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_Password_lockout(struct dcerpc_pipe *p,
+ struct dcerpc_pipe *np,
+ struct torture_context *tctx,
+ uint32_t acct_flags,
+ const char *acct_name,
+ struct policy_handle *domain_handle,
+ struct policy_handle *user_handle,
+ char **password,
+ struct cli_credentials *machine_credentials,
+ const char *comment,
+ bool disable,
+ bool interactive,
+ uint32_t password_history_length,
+ NTSTATUS expected_success_status,
+ struct samr_DomInfo1 *info1,
+ struct samr_DomInfo12 *info12)
+{
+ union samr_DomainInfo info;
+ uint64_t lockout_threshold = 1;
+ uint32_t lockout_seconds = 5;
+ uint64_t delta_time_factor = 10 * 1000 * 1000;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ lockout_seconds = 60;
+ }
+
+ torture_comment(tctx, "\nTesting account lockout: %s\n", comment);
+
+ /* set policies */
+
+ info.info1 = *info1;
+
+ torture_comment(tctx, "setting password history length to %d.\n", password_history_length);
+ info.info1.password_history_length = password_history_length;
+
+ torture_comment(tctx, "setting min password again.\n");
+ info.info1.min_password_age = 0;
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainPasswordInformation, &info),
+ "failed to set password history length");
+
+ info.info12 = *info12;
+ info.info12.lockout_threshold = lockout_threshold;
+
+ /* set lockout duration < lockout window: should fail */
+ info.info12.lockout_duration = ~(lockout_seconds * delta_time_factor);
+ info.info12.lockout_window = ~((lockout_seconds + 1) * delta_time_factor);
+
+ torture_assert(tctx,
+ test_SetDomainInfo_ntstatus(b, tctx, domain_handle,
+ DomainLockoutInformation, &info,
+ NT_STATUS_INVALID_PARAMETER),
+ "setting lockout duration < lockout window gave unexpected result");
+
+ info.info12.lockout_duration = 0;
+ info.info12.lockout_window = 0;
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainLockoutInformation, &info),
+ "failed to set lockout window and duration to 0");
+
+
+ /* set lockout duration of 5 seconds */
+ info.info12.lockout_duration = ~(lockout_seconds * delta_time_factor);
+ info.info12.lockout_window = ~(lockout_seconds * delta_time_factor);
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainLockoutInformation, &info),
+ "failed to set lockout window and duration to 5 seconds");
+
+ /* reset bad pwd count */
+
+ torture_assert(tctx,
+ test_reset_badpwdcount(p, tctx, user_handle, acct_flags, password), "");
+
+
+ /* enable or disable account */
+
+ if (disable) {
+ torture_assert(tctx,
+ test_SetUserInfo_acct_flags(b, tctx, user_handle,
+ acct_flags | ACB_DISABLED),
+ "failed to disable user");
+ } else {
+ torture_assert(tctx,
+ test_SetUserInfo_acct_flags(b, tctx, user_handle,
+ acct_flags & ~ACB_DISABLED),
+ "failed to enable user");
+ }
+
+
+ /* test logon with right password */
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials,
+ acct_name, *password,
+ expected_success_status, interactive)) {
+ torture_fail(tctx, "failed to auth with latest password");
+ }
+
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 0, 0, 0),
+ "expected account to not be locked");
+
+ /* test with wrong password ==> lockout */
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials,
+ acct_name, "random_crap",
+ NT_STATUS_WRONG_PASSWORD, interactive)) {
+ torture_fail(tctx, "succeeded to authenticate with wrong password");
+ }
+
+ /*
+ * curiously, windows does _not_ return fresh values of
+ * effective bad_password_count and ACB_AUTOLOCK.
+ */
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 1, ACB_AUTOLOCK),
+ "expected account to not be locked");
+
+ /* test with good password */
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name,
+ *password,
+ NT_STATUS_ACCOUNT_LOCKED_OUT, interactive))
+ {
+ torture_fail(tctx, "authenticate did not return NT_STATUS_ACCOUNT_LOCKED_OUT");
+ }
+
+ /* bad pwd count should not get updated */
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 1, ACB_AUTOLOCK),
+ "expected account to be locked");
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, *password,
+ NT_STATUS_ACCOUNT_LOCKED_OUT),
+ "got wrong status from ChangePasswordUser2");
+
+ /* bad pwd count should not get updated */
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 1, ACB_AUTOLOCK),
+ "expected account to be locked");
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_ACCOUNT_LOCKED_OUT),
+ "got wrong status from ChangePasswordUser2");
+
+ /* bad pwd count should not get updated */
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 1, ACB_AUTOLOCK),
+ "expected account to be locked");
+
+ /* with bad password */
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials,
+ acct_name, "random_crap2",
+ NT_STATUS_ACCOUNT_LOCKED_OUT, interactive))
+ {
+ torture_fail(tctx, "authenticate did not return NT_STATUS_ACCOUNT_LOCKED_OUT");
+ }
+
+ /* bad pwd count should not get updated */
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 1, ACB_AUTOLOCK),
+ "expected account to be locked");
+
+ /* let lockout duration expire ==> unlock */
+
+ torture_comment(tctx, "let lockout duration expire...\n");
+ sleep(lockout_seconds + 1);
+
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 0, 0),
+ "expected account to not be locked");
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name,
+ *password,
+ expected_success_status, interactive))
+ {
+ torture_fail(tctx, "failed to authenticate after lockout expired");
+ }
+
+ if (NT_STATUS_IS_OK(expected_success_status)) {
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 0, 0, 0),
+ "expected account to not be locked");
+ } else {
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 0, 0),
+ "expected account to not be locked");
+ }
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD),
+ "got wrong status from ChangePasswordUser2");
+
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 1, ACB_AUTOLOCK),
+ "expected account to be locked");
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, *password, NT_STATUS_ACCOUNT_LOCKED_OUT),
+ "got wrong status from ChangePasswordUser2");
+
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 1, ACB_AUTOLOCK),
+ "expected account to be locked");
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_ACCOUNT_LOCKED_OUT),
+ "got wrong status from ChangePasswordUser2");
+
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 1, ACB_AUTOLOCK),
+ "expected account to be locked");
+
+ /* let lockout duration expire ==> unlock */
+
+ torture_comment(tctx, "let lockout duration expire...\n");
+ sleep(lockout_seconds + 1);
+
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 0, 0),
+ "expected account to not be locked");
+
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name,
+ *password,
+ expected_success_status, interactive))
+ {
+ torture_fail(tctx, "failed to authenticate after lockout expired");
+ }
+
+ if (NT_STATUS_IS_OK(expected_success_status)) {
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 0, 0, 0),
+ "expected account to not be locked");
+ } else {
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 0, 0),
+ "expected account to not be locked");
+ }
+
+ /* Testing ChangePasswordUser behaviour with 3 attempts */
+ info.info12.lockout_threshold = 3;
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainLockoutInformation, &info),
+ "failed to set lockout threshold to 3");
+
+ if (NT_STATUS_IS_OK(expected_success_status)) {
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 0, 0, 0),
+ "expected account to not be locked");
+ } else {
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 0, 0),
+ "expected account to not be locked");
+ }
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD),
+ "got wrong status from ChangePasswordUser2");
+
+ /* bad pwd count will get updated */
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 1, 1, 0),
+ "expected account to not be locked");
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD),
+ "got wrong status from ChangePasswordUser2");
+
+ /* bad pwd count will get updated */
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 2, 2, 0),
+ "expected account to not be locked");
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, "random_crap", NT_STATUS_WRONG_PASSWORD),
+ "got wrong status from ChangePasswordUser2");
+
+ /* bad pwd count should get updated */
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 3, 3, ACB_AUTOLOCK),
+ "expected account to be locked");
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2_ntstatus(p, tctx, acct_name, *password, NT_STATUS_ACCOUNT_LOCKED_OUT),
+ "got wrong status from ChangePasswordUser2");
+
+ /* bad pwd count should not get updated */
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 3, 3, ACB_AUTOLOCK),
+ "expected account to be locked");
+
+ /* let lockout duration expire ==> unlock */
+
+ torture_comment(tctx, "let lockout duration expire...\n");
+ sleep(lockout_seconds + 1);
+
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 3, 0, 0),
+ "expected account to not be locked");
+
+ torture_assert(tctx,
+ test_ChangePasswordUser2(p, tctx, acct_name, password, NULL, false),
+ "got wrong status from ChangePasswordUser2");
+
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 3, 0, 0),
+ "expected account to not be locked");
+
+ /* Used to reset the badPwdCount for the other tests */
+ if (!test_SamLogon_with_creds(tctx, np, machine_credentials, acct_name,
+ *password,
+ expected_success_status, interactive))
+ {
+ torture_fail(tctx, "failed to authenticate after lockout expired");
+ }
+
+ if (NT_STATUS_IS_OK(expected_success_status)) {
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 0, 0, 0),
+ "expected account to not be locked");
+ } else {
+ torture_assert(tctx,
+ test_QueryUserInfo_lockout(b, tctx, domain_handle, acct_name,
+ 3, 0, 0),
+ "expected account to not be locked");
+ }
+
+ return true;
+}
+
+static bool test_Password_lockout_wrap(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ uint32_t acct_flags,
+ const char *acct_name,
+ struct policy_handle *domain_handle,
+ struct policy_handle *user_handle,
+ char **password,
+ struct cli_credentials *machine_credentials)
+{
+ union samr_DomainInfo *q_info, s_info;
+ struct samr_DomInfo1 info1, _info1;
+ struct samr_DomInfo12 info12, _info12;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *np;
+ int i;
+
+ struct {
+ const char *comment;
+ bool disabled;
+ bool interactive;
+ uint32_t password_history_length;
+ NTSTATUS expected_success_status;
+ } creds[] = {
+ {
+ .comment = "network logon (disabled account)",
+ .disabled = true,
+ .interactive = false,
+ .expected_success_status= NT_STATUS_ACCOUNT_DISABLED
+ },
+ {
+ .comment = "network logon (enabled account)",
+ .disabled = false,
+ .interactive = false,
+ .expected_success_status= NT_STATUS_OK
+ },
+ {
+ .comment = "network logon (enabled account, history len = 1)",
+ .disabled = false,
+ .interactive = false,
+ .expected_success_status= NT_STATUS_OK,
+ .password_history_length = 1
+ },
+ {
+ .comment = "interactive logon (disabled account)",
+ .disabled = true,
+ .interactive = true,
+ .expected_success_status= NT_STATUS_ACCOUNT_DISABLED
+ },
+ {
+ .comment = "interactive logon (enabled account)",
+ .disabled = false,
+ .interactive = true,
+ .expected_success_status= NT_STATUS_OK
+ },
+ {
+ .comment = "interactive logon (enabled account, history len = 1)",
+ .disabled = false,
+ .interactive = true,
+ .expected_success_status= NT_STATUS_OK,
+ .password_history_length = 1
+ },
+ };
+
+ torture_assert(tctx, setup_schannel_netlogon_pipe(tctx, machine_credentials, &np), "");
+
+ /* backup old policies */
+
+ torture_assert(tctx,
+ test_QueryDomainInfo2_level(b, tctx, domain_handle,
+ DomainPasswordInformation, &q_info),
+ "failed to query domain info level 1");
+
+ info1 = q_info->info1;
+ _info1 = info1;
+
+ torture_assert(tctx,
+ test_QueryDomainInfo2_level(b, tctx, domain_handle,
+ DomainLockoutInformation, &q_info),
+ "failed to query domain info level 12");
+
+ info12 = q_info->info12;
+ _info12 = info12;
+
+ /* run tests */
+
+ for (i=0; i < ARRAY_SIZE(creds); i++) {
+ bool test_passed;
+ /* skip trust tests for now */
+ if (acct_flags & ACB_WSTRUST ||
+ acct_flags & ACB_SVRTRUST ||
+ acct_flags & ACB_DOMTRUST) {
+ continue;
+ }
+
+ test_passed = test_Password_lockout(p, np, tctx, acct_flags, acct_name,
+ domain_handle, user_handle, password,
+ machine_credentials,
+ creds[i].comment,
+ creds[i].disabled,
+ creds[i].interactive,
+ creds[i].password_history_length,
+ creds[i].expected_success_status,
+ &_info1, &_info12);
+ ret &= test_passed;
+ if (!test_passed) {
+ torture_result(tctx, TORTURE_FAIL, "TEST #%d (%s) failed\n", i, creds[i].comment);
+ break;
+ } else {
+ torture_comment(tctx, "TEST #%d (%s) succeeded\n", i, creds[i].comment);
+ }
+ }
+
+ /* restore policies */
+
+ s_info.info1 = info1;
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainPasswordInformation, &s_info),
+ "failed to set password information");
+
+ s_info.info12 = info12;
+
+ torture_assert(tctx,
+ test_SetDomainInfo(b, tctx, domain_handle,
+ DomainLockoutInformation, &s_info),
+ "failed to set lockout information");
+
+ return ret;
+}
+
+static bool test_DeleteUser_with_privs(struct dcerpc_pipe *p,
+ struct dcerpc_pipe *lp,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ struct policy_handle *lsa_handle,
+ struct policy_handle *user_handle,
+ const struct dom_sid *domain_sid,
+ uint32_t rid,
+ struct cli_credentials *machine_credentials)
+{
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_binding_handle *lb = lp->binding_handle;
+
+ struct policy_handle lsa_acct_handle;
+ struct dom_sid *user_sid;
+
+ user_sid = dom_sid_add_rid(tctx, domain_sid, rid);
+
+ {
+ struct lsa_EnumAccountRights r;
+ struct lsa_RightSet rights;
+
+ torture_comment(tctx, "Testing LSA EnumAccountRights\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.out.rights = &rights;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r),
+ "lsa_EnumAccountRights failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ "Expected enum rights for account to fail");
+ }
+
+ {
+ struct lsa_RightSet rights;
+ struct lsa_StringLarge names[2];
+ struct lsa_AddAccountRights r;
+
+ torture_comment(tctx, "Testing LSA AddAccountRights\n");
+
+ init_lsa_StringLarge(&names[0], "SeMachineAccountPrivilege");
+ init_lsa_StringLarge(&names[1], NULL);
+
+ rights.count = 1;
+ rights.names = names;
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.in.rights = &rights;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_AddAccountRights_r(lb, tctx, &r),
+ "lsa_AddAccountRights failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to add privileges");
+ }
+
+ {
+ struct lsa_RightSet rights;
+ struct lsa_StringLarge names[2];
+ struct lsa_AddAccountRights r;
+
+ torture_comment(tctx, "Testing LSA AddAccountRights 1\n");
+
+ init_lsa_StringLarge(&names[0], "SeInteractiveLogonRight");
+ init_lsa_StringLarge(&names[1], NULL);
+
+ rights.count = 1;
+ rights.names = names;
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.in.rights = &rights;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_AddAccountRights_r(lb, tctx, &r),
+ "lsa_AddAccountRights 1 failed");
+
+ if (torture_setting_bool(tctx, "nt4_dc", false)) {
+ /*
+ * The NT4 DC doesn't implement Rights.
+ */
+ torture_assert_ntstatus_equal(tctx, r.out.result,
+ NT_STATUS_NO_SUCH_PRIVILEGE,
+ "Add rights failed with incorrect error");
+ } else {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to add rights");
+
+ }
+ }
+
+
+ {
+ struct lsa_EnumAccounts r;
+ uint32_t resume_handle = 0;
+ struct lsa_SidArray lsa_sid_array;
+ int i;
+ bool found_sid = false;
+
+ torture_comment(tctx, "Testing LSA EnumAccounts\n");
+
+ r.in.handle = lsa_handle;
+ r.in.num_entries = 0x1000;
+ r.in.resume_handle = &resume_handle;
+ r.out.sids = &lsa_sid_array;
+ r.out.resume_handle = &resume_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(lb, tctx, &r),
+ "lsa_EnumAccounts failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to enum accounts");
+
+ for (i=0; i < lsa_sid_array.num_sids; i++) {
+ if (dom_sid_equal(user_sid, lsa_sid_array.sids[i].sid)) {
+ found_sid = true;
+ }
+ }
+
+ torture_assert(tctx, found_sid,
+ "failed to list privileged account");
+ }
+
+ {
+ struct lsa_EnumAccountRights r;
+ struct lsa_RightSet user_rights;
+ uint32_t expected_count = 2;
+
+ if (torture_setting_bool(tctx, "nt4_dc", false)) {
+ /*
+ * NT4 DC doesn't store rights.
+ */
+ expected_count = 1;
+ }
+
+ torture_comment(tctx, "Testing LSA EnumAccountRights\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.out.rights = &user_rights;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r),
+ "lsa_EnumAccountRights failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to enum rights for account");
+
+ if (user_rights.count < expected_count) {
+ torture_result(tctx, TORTURE_FAIL, "failed to find newly added rights");
+ return false;
+ }
+ }
+
+ {
+ struct lsa_OpenAccount r;
+
+ torture_comment(tctx, "Testing LSA OpenAccount\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.acct_handle = &lsa_acct_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(lb, tctx, &r),
+ "lsa_OpenAccount failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to open lsa account");
+ }
+
+ {
+ struct lsa_GetSystemAccessAccount r;
+ uint32_t access_mask;
+
+ torture_comment(tctx, "Testing LSA GetSystemAccessAccount\n");
+
+ r.in.handle = &lsa_acct_handle;
+ r.out.access_mask = &access_mask;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetSystemAccessAccount_r(lb, tctx, &r),
+ "lsa_GetSystemAccessAccount failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to get lsa system access account");
+ }
+
+ {
+ struct lsa_Close r;
+
+ torture_comment(tctx, "Testing LSA Close\n");
+
+ r.in.handle = &lsa_acct_handle;
+ r.out.handle = &lsa_acct_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_Close_r(lb, tctx, &r),
+ "lsa_Close failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to close lsa");
+ }
+
+ {
+ struct samr_DeleteUser r;
+
+ torture_comment(tctx, "Testing SAMR DeleteUser\n");
+
+ r.in.user_handle = user_handle;
+ r.out.user_handle = user_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, tctx, &r),
+ "DeleteUser failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "DeleteUser failed");
+ }
+
+ {
+ struct lsa_EnumAccounts r;
+ uint32_t resume_handle = 0;
+ struct lsa_SidArray lsa_sid_array;
+ int i;
+ bool found_sid = false;
+
+ torture_comment(tctx, "Testing LSA EnumAccounts\n");
+
+ r.in.handle = lsa_handle;
+ r.in.num_entries = 0x1000;
+ r.in.resume_handle = &resume_handle;
+ r.out.sids = &lsa_sid_array;
+ r.out.resume_handle = &resume_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(lb, tctx, &r),
+ "lsa_EnumAccounts failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to enum accounts");
+
+ for (i=0; i < lsa_sid_array.num_sids; i++) {
+ if (dom_sid_equal(user_sid, lsa_sid_array.sids[i].sid)) {
+ found_sid = true;
+ }
+ }
+
+ torture_assert(tctx, found_sid,
+ "failed to list privileged account");
+ }
+
+ {
+ struct lsa_EnumAccountRights r;
+ struct lsa_RightSet user_rights;
+
+ torture_comment(tctx, "Testing LSA EnumAccountRights\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.out.rights = &user_rights;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r),
+ "lsa_EnumAccountRights failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to enum rights for account");
+
+ if (user_rights.count < 1) {
+ torture_result(tctx, TORTURE_FAIL, "failed to find newly added rights");
+ return false;
+ }
+ }
+
+ {
+ struct lsa_OpenAccount r;
+
+ torture_comment(tctx, "Testing LSA OpenAccount\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.acct_handle = &lsa_acct_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_OpenAccount_r(lb, tctx, &r),
+ "lsa_OpenAccount failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to open lsa account");
+ }
+
+ {
+ struct lsa_GetSystemAccessAccount r;
+ uint32_t access_mask;
+
+ torture_comment(tctx, "Testing LSA GetSystemAccessAccount\n");
+
+ r.in.handle = &lsa_acct_handle;
+ r.out.access_mask = &access_mask;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetSystemAccessAccount_r(lb, tctx, &r),
+ "lsa_GetSystemAccessAccount failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to get lsa system access account");
+ }
+
+ {
+ struct lsa_DeleteObject r;
+
+ torture_comment(tctx, "Testing LSA DeleteObject\n");
+
+ r.in.handle = &lsa_acct_handle;
+ r.out.handle = &lsa_acct_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_DeleteObject_r(lb, tctx, &r),
+ "lsa_DeleteObject failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to delete object");
+ }
+
+ {
+ struct lsa_EnumAccounts r;
+ uint32_t resume_handle = 0;
+ struct lsa_SidArray lsa_sid_array;
+ int i;
+ bool found_sid = false;
+
+ torture_comment(tctx, "Testing LSA EnumAccounts\n");
+
+ r.in.handle = lsa_handle;
+ r.in.num_entries = 0x1000;
+ r.in.resume_handle = &resume_handle;
+ r.out.sids = &lsa_sid_array;
+ r.out.resume_handle = &resume_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccounts_r(lb, tctx, &r),
+ "lsa_EnumAccounts failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "Failed to enum accounts");
+
+ for (i=0; i < lsa_sid_array.num_sids; i++) {
+ if (dom_sid_equal(user_sid, lsa_sid_array.sids[i].sid)) {
+ found_sid = true;
+ }
+ }
+
+ torture_assert(tctx, !found_sid,
+ "should not have listed privileged account");
+ }
+
+ {
+ struct lsa_EnumAccountRights r;
+ struct lsa_RightSet user_rights;
+
+ torture_comment(tctx, "Testing LSA EnumAccountRights\n");
+
+ r.in.handle = lsa_handle;
+ r.in.sid = user_sid;
+ r.out.rights = &user_rights;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_EnumAccountRights_r(lb, tctx, &r),
+ "lsa_EnumAccountRights failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_OBJECT_NAME_NOT_FOUND,
+ "Failed to enum rights for account");
+ }
+
+ 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,
+ const struct dom_sid *domain_sid,
+ uint32_t base_acct_flags,
+ const char *base_acct_name, enum torture_samr_choice which_ops,
+ struct cli_credentials *machine_credentials)
+{
+ char *password = NULL;
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ bool ret = true;
+ int i;
+ uint32_t rid;
+ const uint32_t password_fields[] = {
+ SAMR_FIELD_NT_PASSWORD_PRESENT,
+ SAMR_FIELD_LM_PASSWORD_PRESENT,
+ SAMR_FIELD_NT_PASSWORD_PRESENT | SAMR_FIELD_LM_PASSWORD_PRESENT,
+ 0
+ };
+
+ status = test_LookupName(b, 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(b, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo(b, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo2(b, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_SetUserInfo(b, tctx, user_handle, base_acct_flags,
+ base_acct_name)) {
+ ret = false;
+ }
+
+ if (!test_GetUserPwInfo(b, tctx, user_handle)) {
+ ret = false;
+ }
+
+ if (!test_TestPrivateFunctionsUser(b, 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);
+
+ torture_comment(tctx, "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;
+ }
+
+ /* Try a compleatly random password */
+ if (!test_ChangePasswordRandomBytes(p, tctx, base_acct_name, user_handle, &password)) {
+ 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_SetUserPass_31(p, tctx, user_handle, false, &password)) {
+ ret = false;
+ }
+
+ for (i = 0; password_fields[i]; i++) {
+ if (!test_SetUserPass_32(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_ChangePassword(p, tctx, base_acct_name, domain_handle, &password)) {
+ 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;
+ }
+
+ if (!test_SetUserPass_18(p, tctx, user_handle, &password)) {
+ ret = false;
+ }
+
+ if (!test_ChangePasswordUser3(p, tctx, base_acct_name, 0, &password, NULL, 0, false)) {
+ ret = false;
+ }
+
+ for (i = 0; password_fields[i]; i++) {
+
+ if (password_fields[i] == SAMR_FIELD_LM_PASSWORD_PRESENT) {
+ /* we need to skip as that would break
+ * the ChangePasswordUser3 verify */
+ continue;
+ }
+
+ if (!test_SetUserPass_21(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;
+ }
+ }
+
+ q.in.user_handle = user_handle;
+ q.in.level = 5;
+ q.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q),
+ "QueryUserInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(q.out.result));
+ ret = false;
+ } else {
+ uint32_t expected_flags = (base_acct_flags | ACB_PWNOTREQ | ACB_DISABLED);
+ if ((info->info5.acct_flags) != expected_flags) {
+ /* FIXME: GD */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n",
+ info->info5.acct_flags,
+ expected_flags);
+ ret = false;
+ }
+ }
+ if (info->info5.rid != rid) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5 failed, it returned %u when we expected rid of %u\n",
+ info->info5.rid, rid);
+
+ }
+ }
+
+ break;
+
+ case TORTURE_SAMR_PASSWORDS_PWDLASTSET:
+
+ /* test last password change timestamp behaviour */
+ torture_assert(tctx, test_SetPassword_pwdlastset(p, tctx, base_acct_flags,
+ base_acct_name,
+ user_handle, &password,
+ machine_credentials),
+ "pwdLastSet test failed\n");
+ break;
+
+ case TORTURE_SAMR_PASSWORDS_BADPWDCOUNT:
+
+ /* test bad pwd count change behaviour */
+ torture_assert(tctx, test_Password_badpwdcount_wrap(p, tctx, base_acct_flags,
+ base_acct_name,
+ domain_handle,
+ user_handle, &password,
+ machine_credentials),
+ "badPwdCount test failed\n");
+ break;
+
+ case TORTURE_SAMR_PASSWORDS_LOCKOUT:
+
+ torture_assert(tctx, test_Password_lockout_wrap(p, tctx, base_acct_flags,
+ base_acct_name,
+ domain_handle,
+ user_handle, &password,
+ machine_credentials),
+ "Lockout test failed");
+ break;
+
+
+ case TORTURE_SAMR_USER_PRIVILEGES: {
+
+ struct dcerpc_pipe *lp;
+ struct policy_handle *lsa_handle;
+ struct dcerpc_binding_handle *lb;
+
+ status = torture_rpc_connection(tctx, &lp, &ndr_table_lsarpc);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to open LSA pipe");
+ lb = lp->binding_handle;
+
+ if (!test_lsa_OpenPolicy2(lb, tctx, &lsa_handle)) {
+ ret = false;
+ }
+
+ if (!test_DeleteUser_with_privs(p, lp, tctx,
+ domain_handle, lsa_handle, user_handle,
+ domain_sid, rid,
+ machine_credentials)) {
+ ret = false;
+ }
+
+ if (!test_lsa_Close(lb, tctx, lsa_handle)) {
+ ret = false;
+ }
+
+ if (!ret) {
+ torture_result(tctx, TORTURE_FAIL, "privileged user delete test failed\n");
+ }
+
+ break;
+ }
+ case TORTURE_SAMR_OTHER:
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ case TORTURE_SAMR_MANY_GROUPS:
+ case TORTURE_SAMR_MANY_ALIASES:
+ /* We just need the account to exist */
+ break;
+ }
+ return ret;
+}
+
+static bool test_alias_ops(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *alias_handle,
+ const struct dom_sid *domain_sid)
+{
+ bool ret = true;
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_QuerySecurity(b, tctx, alias_handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_QueryAliasInfo(b, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_SetAliasInfo(b, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_AddMemberToAlias(b, tctx, alias_handle, domain_sid)) {
+ ret = false;
+ }
+
+ if (torture_setting_bool(tctx, "samba3", false) ||
+ torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "skipping MultipleMembers Alias tests against Samba\n");
+ return ret;
+ }
+
+ if (!test_AddMultipleMembersToAlias(b, tctx, alias_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_DeleteUser(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *user_handle)
+{
+ struct samr_DeleteUser d;
+ torture_comment(tctx, "Testing DeleteUser\n");
+
+ d.in.user_handle = user_handle;
+ d.out.user_handle = user_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, tctx, &d),
+ "DeleteUser failed");
+ torture_assert_ntstatus_ok(tctx, d.out.result, "DeleteUser");
+
+ return true;
+}
+
+bool test_DeleteUser_byname(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle, const char *name)
+{
+ NTSTATUS status;
+ struct samr_DeleteUser d;
+ struct policy_handle user_handle;
+ uint32_t rid;
+
+ status = test_LookupName(b, tctx, handle, name, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = test_OpenUser_byname(b, tctx, handle, name, &user_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, tctx, &d),
+ "DeleteUser failed");
+ if (!NT_STATUS_IS_OK(d.out.result)) {
+ status = d.out.result;
+ goto failed;
+ }
+
+ return true;
+
+failed:
+ torture_result(tctx, TORTURE_FAIL, "DeleteUser_byname(%s) failed - %s\n", name, nt_errstr(status));
+ return false;
+}
+
+
+static bool test_DeleteGroup_byname(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ 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(b, tctx, 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;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenGroup_r(b, tctx, &r),
+ "OpenGroup failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ status = r.out.result;
+ goto failed;
+ }
+
+ d.in.group_handle = &group_handle;
+ d.out.group_handle = &group_handle;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomainGroup_r(b, tctx, &d),
+ "DeleteDomainGroup failed");
+ if (!NT_STATUS_IS_OK(d.out.result)) {
+ status = d.out.result;
+ goto failed;
+ }
+
+ return true;
+
+failed:
+ torture_result(tctx, TORTURE_FAIL, "DeleteGroup_byname(%s) failed - %s\n", name, nt_errstr(status));
+ return false;
+}
+
+
+static bool test_DeleteAlias_byname(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ 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;
+
+ torture_comment(tctx, "Testing DeleteAlias_byname\n");
+
+ status = test_LookupName(b, tctx, 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;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenAlias_r(b, tctx, &r),
+ "OpenAlias failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ status = r.out.result;
+ goto failed;
+ }
+
+ d.in.alias_handle = &alias_handle;
+ d.out.alias_handle = &alias_handle;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomAlias_r(b, tctx, &d),
+ "DeleteDomAlias failed");
+ if (!NT_STATUS_IS_OK(d.out.result)) {
+ status = d.out.result;
+ goto failed;
+ }
+
+ return true;
+
+failed:
+ torture_result(tctx, TORTURE_FAIL, "DeleteAlias_byname(%s) failed - %s\n", name, nt_errstr(status));
+ return false;
+}
+
+static bool test_DeleteAlias(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *alias_handle)
+{
+ struct samr_DeleteDomAlias d;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing DeleteAlias\n");
+
+ d.in.alias_handle = alias_handle;
+ d.out.alias_handle = alias_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomAlias_r(b, tctx, &d),
+ "DeleteDomAlias failed");
+ if (!NT_STATUS_IS_OK(d.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "DeleteAlias failed - %s\n", nt_errstr(d.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_CreateAlias(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ const char *alias_name,
+ struct policy_handle *alias_handle,
+ const struct dom_sid *domain_sid,
+ bool test_alias)
+{
+ struct samr_CreateDomAlias r;
+ struct lsa_String name;
+ uint32_t rid;
+ bool ret = true;
+
+ init_lsa_String(&name, alias_name);
+ 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;
+
+ torture_comment(tctx, "Testing CreateAlias (%s)\n", r.in.alias_name->string);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomAlias_r(b, tctx, &r),
+ "CreateDomAlias failed");
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.alias_name->string);
+ return true;
+ } else {
+ torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.alias_name->string,
+ nt_errstr(r.out.result));
+ return false;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ALIAS_EXISTS)) {
+ if (!test_DeleteAlias_byname(b, tctx, domain_handle, r.in.alias_name->string)) {
+ return false;
+ }
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomAlias_r(b, tctx, &r),
+ "CreateDomAlias failed");
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "CreateAlias failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+
+ if (!test_alias) {
+ return ret;
+ }
+
+ if (!test_alias_ops(b, tctx, alias_handle, domain_sid)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_ChangePassword(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ const char *acct_name,
+ struct policy_handle *domain_handle, char **password)
+{
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!*password) {
+ return false;
+ }
+
+ if (!test_ChangePasswordUser(b, tctx, acct_name, domain_handle, password)) {
+ ret = false;
+ }
+
+ if (!test_ChangePasswordUser2(p, tctx, acct_name, password, 0, true)) {
+ ret = false;
+ }
+
+ if (!test_OemChangePasswordUser2(p, tctx, acct_name, domain_handle, password)) {
+ ret = false;
+ }
+
+ /* test what happens when setting the old password again */
+ if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, *password, 0, true)) {
+ ret = false;
+ }
+
+ {
+ char simple_pass[9];
+ char *v = generate_random_str(tctx, 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, tctx, acct_name, 0, password, simple_pass, 0, true)) {
+ ret = false;
+ }
+ }
+
+ /* set samr_SetDomainInfo level 1 with min_length 5 */
+ {
+ struct samr_QueryDomainInfo r;
+ union samr_DomainInfo *info = NULL;
+ struct samr_SetDomainInfo s;
+ uint16_t len_old, len;
+ uint32_t pwd_prop_old;
+ int64_t min_pwd_age_old;
+
+ len = 5;
+
+ r.in.domain_handle = domain_handle;
+ r.in.level = 1;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing samr_QueryDomainInfo level 1\n");
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r),
+ "QueryDomainInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ return false;
+ }
+
+ s.in.domain_handle = domain_handle;
+ s.in.level = 1;
+ s.in.info = 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;
+
+ torture_comment(tctx, "Testing samr_SetDomainInfo level 1\n");
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s),
+ "SetDomainInfo failed");
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ return false;
+ }
+
+ torture_comment(tctx, "calling test_ChangePasswordUser3 with too short password\n");
+
+ if (!test_ChangePasswordUser3(p, tctx, 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;
+
+ torture_comment(tctx, "Testing samr_SetDomainInfo level 1\n");
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s),
+ "SetDomainInfo failed");
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ return false;
+ }
+
+ }
+
+ {
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+ struct samr_LookupNames n;
+ struct policy_handle user_handle;
+ struct samr_Ids rids, types;
+
+ n.in.domain_handle = domain_handle;
+ n.in.num_names = 1;
+ n.in.names = talloc_array(tctx, struct lsa_String, 1);
+ n.in.names[0].string = acct_name;
+ n.out.rids = &rids;
+ n.out.types = &types;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupNames_r(b, tctx, &n),
+ "LookupNames failed");
+ if (!NT_STATUS_IS_OK(n.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "LookupNames failed - %s\n", nt_errstr(n.out.result));
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r),
+ "OpenUser failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "OpenUser(%u) failed - %s\n", n.out.rids->ids[0], nt_errstr(r.out.result));
+ return false;
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 5;
+ q.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q),
+ "QueryUserInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo failed - %s\n", nt_errstr(q.out.result));
+ return false;
+ }
+
+ torture_comment(tctx, "calling test_ChangePasswordUser3 with too early password change\n");
+
+ if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, NULL,
+ 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, tctx, acct_name, 0, password, NULL, 0, true)) {
+ ret = false;
+ }
+
+ if (!test_ChangePasswordUser3(p, tctx, acct_name, 0, password, NULL, 0, true)) {
+ ret = false;
+ }
+
+ if (!test_ChangePasswordUser4(p, tctx, acct_name, 0, password, NULL)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ const char *user_name,
+ struct policy_handle *user_handle_out,
+ struct dom_sid *domain_sid,
+ enum torture_samr_choice which_ops,
+ struct cli_credentials *machine_credentials,
+ bool test_user)
+{
+
+ TALLOC_CTX *user_ctx;
+
+ struct samr_CreateUser r;
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+ 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 dcerpc_binding_handle *b = p->binding_handle;
+
+ struct policy_handle user_handle;
+ user_ctx = talloc_named(tctx, 0, "test_CreateUser2 per-user context");
+ init_lsa_String(&name, user_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;
+
+ torture_comment(tctx, "Testing CreateUser(%s)\n", r.in.account_name->string);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser_r(b, user_ctx, &r),
+ "CreateUser failed");
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.account_name->string);
+ return true;
+ } else {
+ torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.account_name->string,
+ nt_errstr(r.out.result));
+ return false;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) {
+ if (!test_DeleteUser_byname(b, tctx, domain_handle, r.in.account_name->string)) {
+ talloc_free(user_ctx);
+ return false;
+ }
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser_r(b, user_ctx, &r),
+ "CreateUser failed");
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ talloc_free(user_ctx);
+ torture_result(tctx, TORTURE_FAIL, "CreateUser failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+
+ if (!test_user) {
+ if (user_handle_out) {
+ *user_handle_out = user_handle;
+ }
+ return ret;
+ }
+
+ {
+ q.in.user_handle = &user_handle;
+ q.in.level = 16;
+ q.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, user_ctx, &q),
+ "QueryUserInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(q.out.result));
+ ret = false;
+ } else {
+ if ((info->info16.acct_flags & acct_flags) != acct_flags) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 16 failed, it returned 0x%08x when we expected flags of 0x%08x\n",
+ info->info16.acct_flags,
+ acct_flags);
+ ret = false;
+ }
+ }
+
+ if (!test_user_ops(p, tctx, &user_handle, domain_handle,
+ domain_sid, acct_flags, name.string, which_ops,
+ machine_credentials)) {
+ ret = false;
+ }
+
+ if (user_handle_out) {
+ *user_handle_out = user_handle;
+ } else {
+ torture_comment(tctx, "Testing DeleteUser (createuser test)\n");
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, user_ctx, &d),
+ "DeleteUser failed");
+ if (!NT_STATUS_IS_OK(d.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "DeleteUser failed - %s\n", nt_errstr(d.out.result));
+ 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,
+ struct cli_credentials *machine_credentials)
+{
+ struct samr_CreateUser2 r;
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+ struct samr_DeleteUser d;
+ struct policy_handle user_handle;
+ uint32_t rid;
+ struct lsa_String name;
+ bool ret = true;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_ACCESS_DENIED },
+ { 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;
+
+ torture_comment(tctx, "Testing CreateUser2(%s, 0x%x)\n", r.in.account_name->string, acct_flags);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser2_r(b, user_ctx, &r),
+ "CreateUser2 failed");
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED) || NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.account_name->string);
+ continue;
+ } else {
+ torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.account_name->string,
+ nt_errstr(r.out.result));
+ ret = false;
+ continue;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) {
+ if (!test_DeleteUser_byname(b, tctx, domain_handle, r.in.account_name->string)) {
+ talloc_free(user_ctx);
+ ret = false;
+ continue;
+ }
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateUser2_r(b, user_ctx, &r),
+ "CreateUser2 failed");
+
+ }
+ if (!NT_STATUS_EQUAL(r.out.result, account_types[i].nt_status)) {
+ torture_result(tctx, TORTURE_FAIL, "CreateUser2 failed gave incorrect error return - %s (should be %s)\n",
+ nt_errstr(r.out.result), nt_errstr(account_types[i].nt_status));
+ ret = false;
+ }
+
+ if (NT_STATUS_IS_OK(r.out.result)) {
+ q.in.user_handle = &user_handle;
+ q.in.level = 5;
+ q.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, user_ctx, &q),
+ "QueryUserInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(q.out.result));
+ ret = false;
+ } else {
+ uint32_t expected_flags = (acct_flags | ACB_PWNOTREQ | ACB_DISABLED);
+ if (acct_flags == ACB_NORMAL) {
+ expected_flags |= ACB_PW_EXPIRED;
+ }
+ if ((info->info5.acct_flags) != expected_flags) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n",
+ info->info5.acct_flags,
+ expected_flags);
+ ret = false;
+ }
+ switch (acct_flags) {
+ case ACB_SVRTRUST:
+ if (info->info5.primary_gid != DOMAIN_RID_DCS) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5: DC should have had Primary Group %d, got %d\n",
+ DOMAIN_RID_DCS, info->info5.primary_gid);
+ ret = false;
+ }
+ break;
+ case ACB_WSTRUST:
+ if (info->info5.primary_gid != DOMAIN_RID_DOMAIN_MEMBERS) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5: Domain Member should have had Primary Group %d, got %d\n",
+ DOMAIN_RID_DOMAIN_MEMBERS, info->info5.primary_gid);
+ ret = false;
+ }
+ break;
+ case ACB_NORMAL:
+ if (info->info5.primary_gid != DOMAIN_RID_USERS) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 5: Users should have had Primary Group %d, got %d\n",
+ DOMAIN_RID_USERS, info->info5.primary_gid);
+ ret = false;
+ }
+ break;
+ }
+ }
+
+ if (!test_user_ops(p, tctx, &user_handle, domain_handle,
+ domain_sid, acct_flags, name.string, which_ops,
+ machine_credentials)) {
+ ret = false;
+ }
+
+ if (!ndr_policy_handle_empty(&user_handle)) {
+ torture_comment(tctx, "Testing DeleteUser (createuser2 test)\n");
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteUser_r(b, user_ctx, &d),
+ "DeleteUser failed");
+ if (!NT_STATUS_IS_OK(d.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "DeleteUser failed - %s\n", nt_errstr(d.out.result));
+ ret = false;
+ }
+ }
+ }
+ talloc_free(user_ctx);
+ }
+
+ return ret;
+}
+
+static bool test_QueryAliasInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryAliasInfo r;
+ union samr_AliasInfo *info;
+ uint16_t levels[] = {1, 2, 3};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "Testing QueryAliasInfo level %u\n", levels[i]);
+
+ r.in.alias_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryAliasInfo_r(b, tctx, &r),
+ "QueryAliasInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryAliasInfo level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryGroupInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryGroupInfo r;
+ union samr_GroupInfo *info;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ int i;
+ bool ret = true;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "Testing QueryGroupInfo level %u\n", levels[i]);
+
+ r.in.group_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupInfo_r(b, tctx, &r),
+ "QueryGroupInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryGroupInfo level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryGroupMember(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryGroupMember r;
+ struct samr_RidAttrArray *rids = NULL;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing QueryGroupMember\n");
+
+ r.in.group_handle = handle;
+ r.out.rids = &rids;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupMember_r(b, tctx, &r),
+ "QueryGroupMember failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryGroupMember failed - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_SetGroupInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryGroupInfo r;
+ union samr_GroupInfo *info;
+ 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++) {
+ torture_comment(tctx, "Testing QueryGroupInfo level %u\n", levels[i]);
+
+ r.in.group_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupInfo_r(b, tctx, &r),
+ "QueryGroupInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryGroupInfo level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ torture_comment(tctx, "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");
+ }
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetGroupInfo_r(b, tctx, &s),
+ "SetGroupInfo failed");
+ if (set_ok[i]) {
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetGroupInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(s.out.result));
+ ret = false;
+ continue;
+ }
+ } else {
+ if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetGroupInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n",
+ r.in.level, nt_errstr(s.out.result));
+ ret = false;
+ continue;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryUserInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryUserInfo r;
+ union samr_UserInfo *info;
+ 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++) {
+ torture_comment(tctx, "Testing QueryUserInfo level %u\n", levels[i]);
+
+ r.in.user_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
+ "QueryUserInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryUserInfo2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryUserInfo2 r;
+ union samr_UserInfo *info;
+ 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++) {
+ torture_comment(tctx, "Testing QueryUserInfo2 level %u\n", levels[i]);
+
+ r.in.user_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo2_r(b, tctx, &r),
+ "QueryUserInfo2 failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo2 level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_OpenUser(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t rid)
+{
+ struct samr_OpenUser r;
+ struct policy_handle user_handle;
+ bool ret = true;
+
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r),
+ "OpenUser failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "OpenUser(%u) failed - %s\n", rid, nt_errstr(r.out.result));
+ return false;
+ }
+
+ if (!test_QuerySecurity(b, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo(b, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryUserInfo2(b, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_GetUserPwInfo(b, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_GetGroupsForUser(b, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(b, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_OpenGroup(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t rid)
+{
+ struct samr_OpenGroup r;
+ struct policy_handle group_handle;
+ bool ret = true;
+
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenGroup_r(b, tctx, &r),
+ "OpenGroup failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "OpenGroup(%u) failed - %s\n", rid, nt_errstr(r.out.result));
+ return false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_QuerySecurity(b, tctx, &group_handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_QueryGroupInfo(b, tctx, &group_handle)) {
+ ret = false;
+ }
+
+ if (!test_QueryGroupMember(b, tctx, &group_handle)) {
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(b, tctx, &group_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_OpenAlias(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t rid)
+{
+ struct samr_OpenAlias r;
+ struct policy_handle alias_handle;
+ bool ret = true;
+
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenAlias_r(b, tctx, &r),
+ "OpenAlias failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "OpenAlias(%u) failed - %s\n", rid, nt_errstr(r.out.result));
+ return false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_QuerySecurity(b, tctx, &alias_handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_QueryAliasInfo(b, tctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_GetMembersInAlias(b, tctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!test_samr_handle_Close(b, tctx, &alias_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool check_mask(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle, uint32_t rid,
+ uint32_t acct_flag_mask)
+{
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+ struct policy_handle user_handle;
+ bool ret = true;
+
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r),
+ "OpenUser failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "OpenUser(%u) failed - %s\n", rid, nt_errstr(r.out.result));
+ return false;
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 16;
+ q.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q),
+ "QueryUserInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo level 16 failed - %s\n",
+ nt_errstr(q.out.result));
+ ret = false;
+ } else {
+ if ((acct_flag_mask & info->info16.acct_flags) == 0) {
+ torture_result(tctx, TORTURE_FAIL, "Server failed to filter for 0x%x, allowed 0x%x (%d) on EnumDomainUsers\n",
+ acct_flag_mask, info->info16.acct_flags, rid);
+ ret = false;
+ }
+ }
+
+ if (!test_samr_handle_Close(b, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumDomainUsers_all(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ 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 ;
+ struct lsa_Strings names;
+ struct samr_Ids rids, types;
+ struct samr_SamArray *sam = NULL;
+ uint32_t num_entries = 0;
+
+ uint32_t masks[] = {ACB_NORMAL, ACB_DOMTRUST, ACB_WSTRUST,
+ ACB_DISABLED, ACB_NORMAL | ACB_DISABLED,
+ ACB_SVRTRUST | ACB_DOMTRUST | ACB_WSTRUST,
+ ACB_PWNOEXP, 0};
+
+ torture_comment(tctx, "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;
+ r.out.num_entries = &num_entries;
+ r.out.sam = &sam;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainUsers_r(b, tctx, &r),
+ "EnumDomainUsers failed");
+ if (!NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "EnumDomainUsers failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+
+ torture_assert(tctx, sam, "EnumDomainUsers failed: r.out.sam unexpectedly NULL");
+
+ if (sam->count == 0) {
+ continue;
+ }
+
+ for (i=0;i<sam->count;i++) {
+ if (mask) {
+ if (!check_mask(b, tctx, handle, sam->entries[i].idx, mask)) {
+ ret = false;
+ }
+ } else if (!test_OpenUser(b, tctx, handle, sam->entries[i].idx)) {
+ ret = false;
+ }
+ }
+ }
+
+ torture_comment(tctx, "Testing LookupNames\n");
+ n.in.domain_handle = handle;
+ n.in.num_names = sam->count;
+ n.in.names = talloc_array(tctx, struct lsa_String, sam->count);
+ n.out.rids = &rids;
+ n.out.types = &types;
+ for (i=0;i<sam->count;i++) {
+ n.in.names[i].string = sam->entries[i].name.string;
+ }
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupNames_r(b, tctx, &n),
+ "LookupNames failed");
+ if (!NT_STATUS_IS_OK(n.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "LookupNames failed - %s\n", nt_errstr(n.out.result));
+ ret = false;
+ }
+
+
+ torture_comment(tctx, "Testing LookupRids\n");
+ lr.in.domain_handle = handle;
+ lr.in.num_rids = sam->count;
+ lr.in.rids = talloc_array(tctx, uint32_t, sam->count);
+ lr.out.names = &names;
+ lr.out.types = &types;
+ for (i=0;i<sam->count;i++) {
+ lr.in.rids[i] = sam->entries[i].idx;
+ }
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupRids_r(b, tctx, &lr),
+ "LookupRids failed");
+ torture_assert_ntstatus_ok(tctx, lr.out.result, "LookupRids");
+
+ return ret;
+}
+
+/*
+ try blasting the server with a bunch of sync requests
+*/
+static bool test_EnumDomainUsers_async(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_EnumDomainUsers r;
+ uint32_t resume_handle=0;
+ int i;
+#define ASYNC_COUNT 100
+ struct tevent_req *req[ASYNC_COUNT];
+
+ if (!torture_setting_bool(tctx, "dangerous", false)) {
+ torture_skip(tctx, "samr async test disabled - enable dangerous tests to use\n");
+ }
+
+ torture_comment(tctx, "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_r_send(tctx, tctx->ev, p->binding_handle, &r);
+ }
+
+ for (i=0;i<ASYNC_COUNT;i++) {
+ tevent_req_poll(req[i], tctx->ev);
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainUsers_r_recv(req[i], tctx),
+ talloc_asprintf(tctx, "EnumDomainUsers[%d] failed - %s\n",
+ i, nt_errstr(r.out.result)));
+ }
+
+ torture_comment(tctx, "%d async requests OK\n", i);
+
+ return true;
+}
+
+static bool test_EnumDomainGroups_all(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_EnumDomainGroups r;
+ uint32_t resume_handle=0;
+ struct samr_SamArray *sam = NULL;
+ uint32_t num_entries = 0;
+ int i;
+ bool ret = true;
+ bool universal_group_found = false;
+
+ torture_comment(tctx, "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;
+ r.out.num_entries = &num_entries;
+ r.out.sam = &sam;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &r),
+ "EnumDomainGroups failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "EnumDomainGroups failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+
+ if (!sam) {
+ return false;
+ }
+
+ for (i=0;i<sam->count;i++) {
+ if (!test_OpenGroup(b, tctx, handle, sam->entries[i].idx)) {
+ ret = false;
+ }
+ if ((ret == true) && (strcasecmp(sam->entries[i].name.string,
+ "Enterprise Admins") == 0)) {
+ universal_group_found = true;
+ }
+ }
+
+ /* when we are running this on s4 we should get back at least the
+ * "Enterprise Admins" universal group. If we don't get a group entry
+ * at all we probably are performing the test on the builtin domain.
+ * So ignore this case. */
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ if ((sam->count > 0) && (!universal_group_found)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_EnumDomainAliases_all(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_EnumDomainAliases r;
+ uint32_t resume_handle=0;
+ struct samr_SamArray *sam = NULL;
+ uint32_t num_entries = 0;
+ int i;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing EnumDomainAliases\n");
+
+ r.in.domain_handle = handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = (uint32_t)-1;
+ r.out.sam = &sam;
+ r.out.num_entries = &num_entries;
+ r.out.resume_handle = &resume_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainAliases_r(b, tctx, &r),
+ "EnumDomainAliases failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "EnumDomainAliases failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ }
+
+ if (!sam) {
+ return false;
+ }
+
+ for (i=0;i<sam->count;i++) {
+ if (!test_OpenAlias(b, tctx, handle, sam->entries[i].idx)) {
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetDisplayEnumerationIndex(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_GetDisplayEnumerationIndex r;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ uint16_t ok_lvl[] = {1, 1, 1, 0, 0};
+ struct lsa_String name;
+ uint32_t idx = 0;
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "Testing GetDisplayEnumerationIndex level %u\n", levels[i]);
+
+ init_lsa_String(&name, TEST_ACCOUNT_NAME);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.name = &name;
+ r.out.idx = &idx;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDisplayEnumerationIndex_r(b, tctx, &r),
+ "GetDisplayEnumerationIndex failed");
+
+ if (ok_lvl[i] &&
+ !NT_STATUS_IS_OK(r.out.result) &&
+ !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "GetDisplayEnumerationIndex level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ init_lsa_String(&name, "zzzzzzzz");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDisplayEnumerationIndex_r(b, tctx, &r),
+ "GetDisplayEnumerationIndex failed");
+
+ if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "GetDisplayEnumerationIndex level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetDisplayEnumerationIndex2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_GetDisplayEnumerationIndex2 r;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ uint16_t ok_lvl[] = {1, 1, 1, 0, 0};
+ struct lsa_String name;
+ uint32_t idx = 0;
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "Testing GetDisplayEnumerationIndex2 level %u\n", levels[i]);
+
+ init_lsa_String(&name, TEST_ACCOUNT_NAME);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.in.name = &name;
+ r.out.idx = &idx;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDisplayEnumerationIndex2_r(b, tctx, &r),
+ "GetDisplayEnumerationIndex2 failed");
+ if (ok_lvl[i] &&
+ !NT_STATUS_IS_OK(r.out.result) &&
+ !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "GetDisplayEnumerationIndex2 level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ init_lsa_String(&name, "zzzzzzzz");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDisplayEnumerationIndex2_r(b, tctx, &r),
+ "GetDisplayEnumerationIndex2 failed");
+ if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "GetDisplayEnumerationIndex2 level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ 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)) { \
+ torture_result(tctx, TORTURE_FAIL, "%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) { \
+ torture_result(tctx, TORTURE_FAIL, "%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_binding_handle *b,
+ struct torture_context *tctx,
+ struct samr_QueryDisplayInfo *querydisplayinfo,
+ bool *seen_testuser)
+{
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+ struct policy_handle user_handle;
+ int i, ret = true;
+ 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:
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r),
+ "OpenUser failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "OpenUser(%u) failed - %s\n", r.in.rid, nt_errstr(r.out.result));
+ return false;
+ }
+ }
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 21;
+ q.out.info = &info;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &q),
+ "QueryUserInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryUserInfo(%u) failed - %s\n", r.in.rid, nt_errstr(r.out.result));
+ return false;
+ }
+
+ switch (querydisplayinfo->in.level) {
+ case 1:
+ if (seen_testuser && strcmp(info->info21.account_name.string, TEST_ACCOUNT_NAME) == 0) {
+ *seen_testuser = true;
+ }
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].full_name,
+ info->info21.full_name, info->info21.account_name);
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].account_name,
+ info->info21.account_name, info->info21.account_name);
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].description,
+ info->info21.description, info->info21.account_name);
+ INT_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].rid,
+ info->info21.rid, info->info21.account_name);
+ INT_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].acct_flags,
+ info->info21.acct_flags, info->info21.account_name);
+
+ break;
+ case 2:
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].account_name,
+ info->info21.account_name, info->info21.account_name);
+ STRING_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].description,
+ info->info21.description, info->info21.account_name);
+ INT_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].rid,
+ info->info21.rid, info->info21.account_name);
+ INT_EQUAL_QUERY((querydisplayinfo->out.info->info2.entries[i].acct_flags & ~ACB_NORMAL),
+ info->info21.acct_flags, info->info21.account_name);
+
+ if (!(querydisplayinfo->out.info->info2.entries[i].acct_flags & ACB_NORMAL)) {
+ torture_result(tctx, TORTURE_FAIL, "Missing ACB_NORMAL in querydisplayinfo->out.info.info2.entries[i].acct_flags on %s\n",
+ info->info21.account_name.string);
+ }
+
+ if (!(info->info21.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST))) {
+ torture_result(tctx, TORTURE_FAIL, "Found non-trust account %s in trust account listing: 0x%x 0x%x\n",
+ info->info21.account_name.string,
+ querydisplayinfo->out.info->info2.entries[i].acct_flags,
+ info->info21.acct_flags);
+ return false;
+ }
+
+ break;
+ }
+
+ if (!test_samr_handle_Close(b, tctx, &user_handle)) {
+ return false;
+ }
+ }
+ return ret;
+}
+
+static bool test_QueryDisplayInfo(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryDisplayInfo r;
+ struct samr_QueryDomainInfo dom_info;
+ union samr_DomainInfo *info = NULL;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ int i;
+ bool seen_testuser = false;
+ uint32_t total_size;
+ uint32_t returned_size;
+ union samr_DispInfo disp_info;
+
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "Testing QueryDisplayInfo level %u\n", levels[i]);
+
+ r.in.start_idx = 0;
+ r.out.result = STATUS_MORE_ENTRIES;
+ while (NT_STATUS_EQUAL(r.out.result, 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;
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &disp_info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r),
+ "QueryDisplayInfo failed");
+ if (!NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) && !NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+ switch (r.in.level) {
+ case 1:
+ if (!test_each_DisplayInfo_user(b, tctx, &r, &seen_testuser)) {
+ ret = false;
+ }
+ r.in.start_idx += r.out.info->info1.count;
+ break;
+ case 2:
+ if (!test_each_DisplayInfo_user(b, tctx, &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;
+ dom_info.out.info = &info;
+
+ /* Check number of users returned is correct */
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &dom_info),
+ "QueryDomainInfo failed");
+ if (!NT_STATUS_IS_OK(dom_info.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(dom_info.out.result));
+ ret = false;
+ break;
+ }
+ switch (r.in.level) {
+ case 1:
+ case 4:
+ if (info->general.num_users < r.in.start_idx) {
+ /* On AD deployments this numbers don't match
+ * since QueryDisplayInfo returns universal and
+ * global groups, QueryDomainInfo only global
+ * ones. */
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo indicates that QueryDisplayInfo returned more users (%d/%d) than the domain %s is said to contain!\n",
+ r.in.start_idx, info->general.num_groups,
+ info->general.domain_name.string);
+ ret = false;
+ }
+ }
+ if (!seen_testuser) {
+ struct policy_handle user_handle;
+ if (NT_STATUS_IS_OK(test_OpenUser_byname(b, tctx, handle, TEST_ACCOUNT_NAME, &user_handle))) {
+ torture_result(tctx, TORTURE_FAIL, "Didn't find test user " TEST_ACCOUNT_NAME " in enumeration of %s\n",
+ info->general.domain_name.string);
+ ret = false;
+ test_samr_handle_Close(b, tctx, &user_handle);
+ }
+ }
+ break;
+ case 3:
+ case 5:
+ if (info->general.num_groups != r.in.start_idx) {
+ /* On AD deployments this numbers don't match
+ * since QueryDisplayInfo returns universal and
+ * global groups, QueryDomainInfo only global
+ * ones. */
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo indicates that QueryDisplayInfo didn't return all (%d/%d) the groups in %s\n",
+ r.in.start_idx, info->general.num_groups,
+ info->general.domain_name.string);
+ ret = false;
+ }
+ }
+
+ break;
+ }
+
+ }
+
+ return ret;
+}
+
+static bool test_QueryDisplayInfo2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryDisplayInfo2 r;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ int i;
+ uint32_t total_size;
+ uint32_t returned_size;
+ union samr_DispInfo info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "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;
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo2_r(b, tctx, &r),
+ "QueryDisplayInfo2 failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo2 level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_QueryDisplayInfo3(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryDisplayInfo3 r;
+ bool ret = true;
+ uint16_t levels[] = {1, 2, 3, 4, 5};
+ int i;
+ uint32_t total_size;
+ uint32_t returned_size;
+ union samr_DispInfo info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "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;
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo3_r(b, tctx, &r),
+ "QueryDisplayInfo3 failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo3 level %u failed - %s\n",
+ levels[i], nt_errstr(r.out.result));
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_QueryDisplayInfo_continue(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryDisplayInfo r;
+ bool ret = true;
+ uint32_t total_size;
+ uint32_t returned_size;
+ union samr_DispInfo info;
+
+ torture_comment(tctx, "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;
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &info;
+
+ do {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r),
+ "QueryDisplayInfo failed");
+ if (NT_STATUS_IS_OK(r.out.result) && *r.out.returned_size != 0) {
+ if (r.out.info->info1.entries[0].idx != r.in.start_idx + 1) {
+ torture_result(tctx, TORTURE_FAIL, "expected idx %d but got %d\n",
+ r.in.start_idx + 1,
+ r.out.info->info1.entries[0].idx);
+ break;
+ }
+ }
+ if (!NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(r.out.result));
+ ret = false;
+ break;
+ }
+ r.in.start_idx++;
+ } while ((NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) ||
+ NT_STATUS_IS_OK(r.out.result)) &&
+ *r.out.returned_size != 0);
+
+ return ret;
+}
+
+static bool test_QueryDomainInfo(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryDomainInfo r;
+ union samr_DomainInfo *info = NULL;
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *domain_comment = talloc_asprintf(tctx,
+ "Tortured by Samba4 RPC-SAMR: %s",
+ timestring(tctx, time(NULL)));
+
+ s.in.domain_handle = handle;
+ s.in.level = 4;
+ s.in.info = talloc(tctx, union samr_DomainInfo);
+
+ s.in.info->oem.oem_information.string = domain_comment;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s),
+ "SetDomainInfo failed");
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetDomainInfo level %u (set comment) failed - %s\n",
+ s.in.level, nt_errstr(s.out.result));
+ return false;
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ torture_comment(tctx, "Testing QueryDomainInfo level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r),
+ "QueryDomainInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(r.out.result));
+ ret = false;
+ continue;
+ }
+
+ switch (levels[i]) {
+ case 2:
+ if (strcmp(info->general.oem_information.string, domain_comment) != 0) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n",
+ levels[i], info->general.oem_information.string, domain_comment);
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret = false;
+ }
+ }
+ if (!info->general.primary.string) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned no PDC name\n",
+ levels[i]);
+ ret = false;
+ } else if (info->general.role == SAMR_ROLE_DOMAIN_PDC) {
+ if (dcerpc_server_name(p) && strcasecmp_m(dcerpc_server_name(p), info->general.primary.string) != 0) {
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different PDC name (%s) compared to server name (%s), despite claiming to be the PDC\n",
+ levels[i], info->general.primary.string, dcerpc_server_name(p));
+ }
+ }
+ }
+ break;
+ case 4:
+ if (strcmp(info->oem.oem_information.string, domain_comment) != 0) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n",
+ levels[i], info->oem.oem_information.string, domain_comment);
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret = false;
+ }
+ }
+ break;
+ case 6:
+ if (!info->info6.primary.string) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned no PDC name\n",
+ levels[i]);
+ ret = false;
+ }
+ break;
+ case 11:
+ if (strcmp(info->general2.general.oem_information.string, domain_comment) != 0) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u returned different comment (%s, expected %s)\n",
+ levels[i], info->general2.general.oem_information.string, domain_comment);
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret = false;
+ }
+ }
+ break;
+ }
+
+ torture_comment(tctx, "Testing SetDomainInfo level %u\n", levels[i]);
+
+ s.in.domain_handle = handle;
+ s.in.level = levels[i];
+ s.in.info = info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetDomainInfo_r(b, tctx, &s),
+ "SetDomainInfo failed");
+ if (set_ok[i]) {
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(s.out.result));
+ ret = false;
+ continue;
+ }
+ } else {
+ if (!NT_STATUS_EQUAL(NT_STATUS_INVALID_INFO_CLASS, s.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "SetDomainInfo level %u gave %s - should have been NT_STATUS_INVALID_INFO_CLASS\n",
+ r.in.level, nt_errstr(s.out.result));
+ ret = false;
+ continue;
+ }
+ }
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r),
+ "QueryDomainInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo level %u failed - %s\n",
+ r.in.level, nt_errstr(r.out.result));
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+
+static bool test_QueryDomainInfo2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct samr_QueryDomainInfo2 r;
+ union samr_DomainInfo *info = NULL;
+ 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++) {
+ torture_comment(tctx, "Testing QueryDomainInfo2 level %u\n", levels[i]);
+
+ r.in.domain_handle = handle;
+ r.in.level = levels[i];
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo2_r(b, tctx, &r),
+ "QueryDomainInfo2 failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDomainInfo2 level %u failed - %s\n",
+ r.in.level, nt_errstr(r.out.result));
+ ret = false;
+ continue;
+ }
+ }
+
+ return ret;
+}
+
+/* Test whether querydispinfo level 5 and enumdomgroups return the same
+ set of group names. */
+static bool test_GroupList(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct dom_sid *domain_sid,
+ struct policy_handle *handle)
+{
+ struct samr_EnumDomainGroups q1;
+ struct samr_QueryDisplayInfo q2;
+ NTSTATUS status;
+ uint32_t resume_handle=0;
+ struct samr_SamArray *sam = NULL;
+ uint32_t num_entries = 0;
+ int i;
+ bool ret = true;
+ uint32_t total_size;
+ uint32_t returned_size;
+ union samr_DispInfo info;
+
+ size_t num_names = 0;
+ const char **names = NULL;
+
+ bool builtin_domain = dom_sid_compare(domain_sid,
+ &global_sid_Builtin) == 0;
+
+ torture_comment(tctx, "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;
+ q1.out.num_entries = &num_entries;
+ q1.out.sam = &sam;
+
+ status = STATUS_MORE_ENTRIES;
+ while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &q1),
+ "EnumDomainGroups failed");
+ status = q1.out.result;
+
+ 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(tctx,
+ sam->entries[i].name.string,
+ &names, &num_names);
+ }
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumDomainGroups");
+
+ torture_assert(tctx, sam, "EnumDomainGroups failed to return sam");
+
+ if (builtin_domain) {
+ torture_assert(tctx, num_names == 0,
+ "EnumDomainGroups shouldn't return any group in the builtin domain!");
+ }
+
+ 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;
+ q2.out.total_size = &total_size;
+ q2.out.returned_size = &returned_size;
+ q2.out.info = &info;
+
+ status = STATUS_MORE_ENTRIES;
+ while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &q2),
+ "QueryDisplayInfo failed");
+ status = q2.out.result;
+ 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) && (!builtin_domain)) {
+ torture_result(tctx, TORTURE_FAIL, "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)) {
+ torture_result(tctx, TORTURE_FAIL, "QueryDisplayInfo level 5 failed - %s\n",
+ nt_errstr(status));
+ ret = false;
+ }
+
+ if (builtin_domain) {
+ torture_assert(tctx, q2.in.start_idx != 0,
+ "QueryDisplayInfo should return all domain groups also on the builtin domain handle!");
+ }
+
+ for (i=0; i<num_names; i++) {
+ if (names[i] != NULL) {
+ torture_result(tctx, TORTURE_FAIL, "EnumDomainGroups gave name [%s] that QueryDisplayInfo did not\n",
+ names[i]);
+ ret = false;
+ }
+ }
+
+ return ret;
+}
+
+static bool test_DeleteDomainGroup(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *group_handle)
+{
+ struct samr_DeleteDomainGroup d;
+
+ torture_comment(tctx, "Testing DeleteDomainGroup\n");
+
+ d.in.group_handle = group_handle;
+ d.out.group_handle = group_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteDomainGroup_r(b, tctx, &d),
+ "DeleteDomainGroup failed");
+ torture_assert_ntstatus_ok(tctx, d.out.result, "DeleteDomainGroup");
+
+ return true;
+}
+
+static bool test_TestPrivateFunctionsDomain(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle)
+{
+ struct samr_TestPrivateFunctionsDomain r;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing TestPrivateFunctionsDomain\n");
+
+ r.in.domain_handle = domain_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_TestPrivateFunctionsDomain_r(b, tctx, &r),
+ "TestPrivateFunctionsDomain failed");
+ torture_assert_ntstatus_equal(tctx, r.out.result, NT_STATUS_NOT_IMPLEMENTED, "TestPrivateFunctionsDomain");
+
+ return ret;
+}
+
+static bool test_RidToSid(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct dom_sid *domain_sid,
+ struct policy_handle *domain_handle)
+{
+ struct samr_RidToSid r;
+ bool ret = true;
+ struct dom_sid *calc_sid, *out_sid;
+ int rids[] = { 0, 42, 512, 10200 };
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(rids);i++) {
+ torture_comment(tctx, "Testing RidToSid\n");
+
+ calc_sid = dom_sid_dup(tctx, domain_sid);
+ r.in.domain_handle = domain_handle;
+ r.in.rid = rids[i];
+ r.out.sid = &out_sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_RidToSid_r(b, tctx, &r),
+ "RidToSid failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "RidToSid for %d failed - %s\n", rids[i], nt_errstr(r.out.result));
+ ret = false;
+ } else {
+ calc_sid = dom_sid_add_rid(calc_sid, calc_sid, rids[i]);
+
+ if (!dom_sid_equal(calc_sid, out_sid)) {
+ torture_result(tctx, TORTURE_FAIL, "RidToSid for %d failed - got %s, expected %s\n", rids[i],
+ dom_sid_string(tctx, out_sid),
+ dom_sid_string(tctx, calc_sid));
+ ret = false;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool test_GetBootKeyInformation(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle)
+{
+ struct samr_GetBootKeyInformation r;
+ bool ret = true;
+ uint32_t unknown = 0;
+ NTSTATUS status;
+
+ torture_comment(tctx, "Testing GetBootKeyInformation\n");
+
+ r.in.domain_handle = domain_handle;
+ r.out.unknown = &unknown;
+
+ status = dcerpc_samr_GetBootKeyInformation_r(b, tctx, &r);
+ if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(r.out.result)) {
+ status = r.out.result;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ /* w2k3 seems to fail this sometimes and pass it sometimes */
+ torture_comment(tctx, "GetBootKeyInformation (ignored) - %s\n", nt_errstr(status));
+ }
+
+ return ret;
+}
+
+static bool test_AddGroupMember(struct dcerpc_binding_handle *b,
+ 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_RidAttrArray *rids = NULL;
+ struct samr_SetMemberAttributesOfGroup s;
+ uint32_t rid;
+ bool found_member = false;
+ int i;
+
+ status = test_LookupName(b, tctx, domain_handle, TEST_ACCOUNT_NAME, &rid);
+ torture_assert_ntstatus_ok(tctx, status, "test_AddGroupMember looking up name " TEST_ACCOUNT_NAME);
+
+ r.in.group_handle = group_handle;
+ r.in.rid = rid;
+ r.in.flags = 0; /* ??? */
+
+ torture_comment(tctx, "Testing AddGroupMember, QueryGroupMember and DeleteGroupMember\n");
+
+ d.in.group_handle = group_handle;
+ d.in.rid = rid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteGroupMember_r(b, tctx, &d),
+ "DeleteGroupMember failed");
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_MEMBER_NOT_IN_GROUP, d.out.result, "DeleteGroupMember");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddGroupMember_r(b, tctx, &r),
+ "AddGroupMember failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "AddGroupMember");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddGroupMember_r(b, tctx, &r),
+ "AddGroupMember failed");
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_MEMBER_IN_GROUP, r.out.result, "AddGroupMember");
+
+ if (torture_setting_bool(tctx, "samba4", false) ||
+ torture_setting_bool(tctx, "samba3", false)) {
+ torture_comment(tctx, "skipping SetMemberAttributesOfGroup test against Samba\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();
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetMemberAttributesOfGroup_r(b, tctx, &s),
+ "SetMemberAttributesOfGroup failed");
+ torture_assert_ntstatus_ok(tctx, s.out.result, "SetMemberAttributesOfGroup");
+ }
+
+ q.in.group_handle = group_handle;
+ q.out.rids = &rids;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupMember_r(b, tctx, &q),
+ "QueryGroupMember failed");
+ torture_assert_ntstatus_ok(tctx, q.out.result, "QueryGroupMember");
+ torture_assert(tctx, rids, "QueryGroupMember did not fill in rids structure");
+
+ for (i=0; i < rids->count; i++) {
+ if (rids->rids[i] == rid) {
+ found_member = true;
+ }
+ }
+
+ torture_assert(tctx, found_member, "QueryGroupMember did not list newly added member");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_DeleteGroupMember_r(b, tctx, &d),
+ "DeleteGroupMember failed");
+ torture_assert_ntstatus_ok(tctx, d.out.result, "DeleteGroupMember");
+
+ rids = NULL;
+ found_member = false;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupMember_r(b, tctx, &q),
+ "QueryGroupMember failed");
+ torture_assert_ntstatus_ok(tctx, q.out.result, "QueryGroupMember");
+ torture_assert(tctx, rids, "QueryGroupMember did not fill in rids structure");
+
+ for (i=0; i < rids->count; i++) {
+ if (rids->rids[i] == rid) {
+ found_member = true;
+ }
+ }
+
+ torture_assert(tctx, !found_member, "QueryGroupMember does still list removed member");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_AddGroupMember_r(b, tctx, &r),
+ "AddGroupMember failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "AddGroupMember");
+
+ return true;
+}
+
+
+static bool test_CreateDomainGroup(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ const char *group_name,
+ struct policy_handle *group_handle,
+ struct dom_sid *domain_sid,
+ bool test_group)
+{
+ struct samr_CreateDomainGroup r;
+ uint32_t rid;
+ struct lsa_String name;
+ bool ret = true;
+
+ init_lsa_String(&name, group_name);
+
+ 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;
+
+ torture_comment(tctx, "Testing CreateDomainGroup(%s)\n", r.in.name->string);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomainGroup_r(b, tctx, &r),
+ "CreateDomainGroup failed");
+
+ if (dom_sid_equal(domain_sid, dom_sid_parse_talloc(tctx, SID_BUILTIN))) {
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx, "Server correctly refused create of '%s'\n", r.in.name->string);
+ return true;
+ } else {
+ torture_result(tctx, TORTURE_FAIL, "Server should have refused create of '%s', got %s instead\n", r.in.name->string,
+ nt_errstr(r.out.result));
+ return false;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_GROUP_EXISTS)) {
+ if (!test_DeleteGroup_byname(b, tctx, domain_handle, r.in.name->string)) {
+ torture_result(tctx, TORTURE_FAIL, "CreateDomainGroup failed: Could not delete domain group %s - %s\n", r.in.name->string,
+ nt_errstr(r.out.result));
+ return false;
+ }
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomainGroup_r(b, tctx, &r),
+ "CreateDomainGroup failed");
+ }
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) {
+ if (!test_DeleteUser_byname(b, tctx, domain_handle, r.in.name->string)) {
+
+ torture_result(tctx, TORTURE_FAIL, "CreateDomainGroup failed: Could not delete user %s - %s\n", r.in.name->string,
+ nt_errstr(r.out.result));
+ return false;
+ }
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_CreateDomainGroup_r(b, tctx, &r),
+ "CreateDomainGroup failed");
+ }
+ torture_assert_ntstatus_ok(tctx, r.out.result, "CreateDomainGroup");
+
+ if (!test_group) {
+ return ret;
+ }
+
+ if (!test_AddGroupMember(b, tctx, domain_handle, group_handle)) {
+ torture_result(tctx, TORTURE_FAIL, "CreateDomainGroup failed - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (!test_SetGroupInfo(b, tctx, 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_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle)
+{
+ struct samr_RemoveMemberFromForeignDomain r;
+
+ r.in.domain_handle = domain_handle;
+ r.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32-12-34-56-78");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_RemoveMemberFromForeignDomain_r(b, tctx, &r),
+ "RemoveMemberFromForeignDomain failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "RemoveMemberFromForeignDomain");
+
+ return true;
+}
+
+static bool test_EnumDomainUsers(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ uint32_t *total_num_entries_p)
+{
+ NTSTATUS status;
+ struct samr_EnumDomainUsers r;
+ uint32_t resume_handle = 0;
+ uint32_t num_entries = 0;
+ uint32_t total_num_entries = 0;
+ struct samr_SamArray *sam;
+
+ r.in.domain_handle = domain_handle;
+ r.in.acct_flags = 0;
+ r.in.max_size = (uint32_t)-1;
+ r.in.resume_handle = &resume_handle;
+
+ r.out.sam = &sam;
+ r.out.num_entries = &num_entries;
+ r.out.resume_handle = &resume_handle;
+
+ torture_comment(tctx, "Testing EnumDomainUsers\n");
+
+ do {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainUsers_r(b, tctx, &r),
+ "EnumDomainUsers failed");
+ if (NT_STATUS_IS_ERR(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to enumerate users");
+ }
+ status = r.out.result;
+
+ total_num_entries += num_entries;
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+ if (total_num_entries_p) {
+ *total_num_entries_p = total_num_entries;
+ }
+
+ return true;
+}
+
+static bool test_EnumDomainGroups(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ uint32_t *total_num_entries_p)
+{
+ NTSTATUS status;
+ struct samr_EnumDomainGroups r;
+ uint32_t resume_handle = 0;
+ uint32_t num_entries = 0;
+ uint32_t total_num_entries = 0;
+ struct samr_SamArray *sam;
+
+ r.in.domain_handle = domain_handle;
+ r.in.max_size = (uint32_t)-1;
+ r.in.resume_handle = &resume_handle;
+
+ r.out.sam = &sam;
+ r.out.num_entries = &num_entries;
+ r.out.resume_handle = &resume_handle;
+
+ torture_comment(tctx, "Testing EnumDomainGroups\n");
+
+ do {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &r),
+ "EnumDomainGroups failed");
+ if (NT_STATUS_IS_ERR(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to enumerate groups");
+ }
+ status = r.out.result;
+
+ total_num_entries += num_entries;
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+ if (total_num_entries_p) {
+ *total_num_entries_p = total_num_entries;
+ }
+
+ return true;
+}
+
+static bool test_EnumDomainAliases(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ uint32_t *total_num_entries_p)
+{
+ NTSTATUS status;
+ struct samr_EnumDomainAliases r;
+ uint32_t resume_handle = 0;
+ uint32_t num_entries = 0;
+ uint32_t total_num_entries = 0;
+ struct samr_SamArray *sam;
+
+ r.in.domain_handle = domain_handle;
+ r.in.max_size = (uint32_t)-1;
+ r.in.resume_handle = &resume_handle;
+
+ r.out.sam = &sam;
+ r.out.num_entries = &num_entries;
+ r.out.resume_handle = &resume_handle;
+
+ torture_comment(tctx, "Testing EnumDomainAliases\n");
+
+ do {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainAliases_r(b, tctx, &r),
+ "EnumDomainAliases failed");
+ if (NT_STATUS_IS_ERR(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to enumerate aliases");
+ }
+ status = r.out.result;
+
+ total_num_entries += num_entries;
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+ if (total_num_entries_p) {
+ *total_num_entries_p = total_num_entries;
+ }
+
+ return true;
+}
+
+static bool test_QueryDisplayInfo_level(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint16_t level,
+ uint32_t *total_num_entries_p)
+{
+ NTSTATUS status;
+ struct samr_QueryDisplayInfo r;
+ uint32_t total_num_entries = 0;
+
+ r.in.domain_handle = handle;
+ r.in.level = level;
+ r.in.start_idx = 0;
+ r.in.max_entries = (uint32_t)-1;
+ r.in.buf_size = (uint32_t)-1;
+
+ torture_comment(tctx, "Testing QueryDisplayInfo\n");
+
+ do {
+ uint32_t total_size;
+ uint32_t returned_size;
+ union samr_DispInfo info;
+
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r),
+ "failed to query displayinfo");
+ if (NT_STATUS_IS_ERR(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query displayinfo");
+ }
+ status = r.out.result;
+
+ if (*r.out.returned_size == 0) {
+ break;
+ }
+
+ switch (r.in.level) {
+ case 1:
+ total_num_entries += info.info1.count;
+ r.in.start_idx += info.info1.entries[info.info1.count - 1].idx + 1;
+ break;
+ case 2:
+ total_num_entries += info.info2.count;
+ r.in.start_idx += info.info2.entries[info.info2.count - 1].idx + 1;
+ break;
+ case 3:
+ total_num_entries += info.info3.count;
+ r.in.start_idx += info.info3.entries[info.info3.count - 1].idx + 1;
+ break;
+ case 4:
+ total_num_entries += info.info4.count;
+ r.in.start_idx += info.info4.entries[info.info4.count - 1].idx + 1;
+ break;
+ case 5:
+ total_num_entries += info.info5.count;
+ r.in.start_idx += info.info5.entries[info.info5.count - 1].idx + 1;
+ break;
+ default:
+ return false;
+ }
+
+ } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES));
+
+ if (total_num_entries_p) {
+ *total_num_entries_p = total_num_entries;
+ }
+
+ return true;
+}
+
+static bool test_ManyObjects(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct policy_handle *domain_handle,
+ struct dom_sid *domain_sid,
+ struct torture_samr_context *ctx)
+{
+ uint32_t num_total = ctx->num_objects_large_dc;
+ uint32_t num_enum = 0;
+ uint32_t num_disp = 0;
+ uint32_t num_created = 0;
+ uint32_t num_anounced = 0;
+ uint32_t i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ struct policy_handle *handles = talloc_zero_array(tctx, struct policy_handle, num_total);
+
+ /* query */
+
+ {
+ struct samr_QueryDomainInfo2 r;
+ union samr_DomainInfo *info;
+ r.in.domain_handle = domain_handle;
+ r.in.level = 2;
+ r.out.info = &info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo2_r(b, tctx, &r),
+ "QueryDomainInfo2 failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query domain info");
+
+ switch (ctx->choice) {
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ num_anounced = info->general.num_users;
+ break;
+ case TORTURE_SAMR_MANY_GROUPS:
+ num_anounced = info->general.num_groups;
+ break;
+ case TORTURE_SAMR_MANY_ALIASES:
+ num_anounced = info->general.num_aliases;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ /* create */
+
+ for (i=0; i < num_total; i++) {
+
+ const char *name = NULL;
+
+ switch (ctx->choice) {
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ name = talloc_asprintf(tctx, "%s%04d", TEST_ACCOUNT_NAME, i);
+ torture_assert(tctx,
+ test_CreateUser(p, tctx, domain_handle, name, &handles[i], domain_sid, 0, NULL, false),
+ "failed to create user");
+ break;
+ case TORTURE_SAMR_MANY_GROUPS:
+ name = talloc_asprintf(tctx, "%s%04d", TEST_GROUPNAME, i);
+ torture_assert(tctx,
+ test_CreateDomainGroup(b, tctx, domain_handle, name, &handles[i], domain_sid, false),
+ "failed to create group");
+ break;
+ case TORTURE_SAMR_MANY_ALIASES:
+ name = talloc_asprintf(tctx, "%s%04d", TEST_ALIASNAME, i);
+ torture_assert(tctx,
+ test_CreateAlias(b, tctx, domain_handle, name, &handles[i], domain_sid, false),
+ "failed to create alias");
+ break;
+ default:
+ return false;
+ }
+ if (!ndr_policy_handle_empty(&handles[i])) {
+ num_created++;
+ }
+ }
+
+ /* enum */
+
+ switch (ctx->choice) {
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ torture_assert(tctx,
+ test_EnumDomainUsers(b, tctx, domain_handle, &num_enum),
+ "failed to enum users");
+ break;
+ case TORTURE_SAMR_MANY_GROUPS:
+ torture_assert(tctx,
+ test_EnumDomainGroups(b, tctx, domain_handle, &num_enum),
+ "failed to enum groups");
+ break;
+ case TORTURE_SAMR_MANY_ALIASES:
+ torture_assert(tctx,
+ test_EnumDomainAliases(b, tctx, domain_handle, &num_enum),
+ "failed to enum aliases");
+ break;
+ default:
+ return false;
+ }
+
+ /* dispinfo */
+
+ switch (ctx->choice) {
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ torture_assert(tctx,
+ test_QueryDisplayInfo_level(b, tctx, domain_handle, 1, &num_disp),
+ "failed to query display info");
+ break;
+ case TORTURE_SAMR_MANY_GROUPS:
+ torture_assert(tctx,
+ test_QueryDisplayInfo_level(b, tctx, domain_handle, 3, &num_disp),
+ "failed to query display info");
+ break;
+ case TORTURE_SAMR_MANY_ALIASES:
+ /* no aliases in dispinfo */
+ break;
+ default:
+ return false;
+ }
+
+ /* close or delete */
+
+ for (i=0; i < num_total; i++) {
+
+ if (ndr_policy_handle_empty(&handles[i])) {
+ continue;
+ }
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_assert(tctx,
+ test_samr_handle_Close(b, tctx, &handles[i]),
+ "failed to close handle");
+ } else {
+ switch (ctx->choice) {
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ torture_assert(tctx,
+ test_DeleteUser(b, tctx, &handles[i]),
+ "failed to delete user");
+ break;
+ case TORTURE_SAMR_MANY_GROUPS:
+ torture_assert(tctx,
+ test_DeleteDomainGroup(b, tctx, &handles[i]),
+ "failed to delete group");
+ break;
+ case TORTURE_SAMR_MANY_ALIASES:
+ torture_assert(tctx,
+ test_DeleteAlias(b, tctx, &handles[i]),
+ "failed to delete alias");
+ break;
+ default:
+ return false;
+ }
+ }
+ }
+
+ talloc_free(handles);
+
+ if (ctx->choice == TORTURE_SAMR_MANY_ACCOUNTS && num_enum != num_anounced + num_created) {
+ torture_comment(tctx,
+ "unexpected number of results (%u) returned in enum call, expected %u\n",
+ num_enum, num_anounced + num_created);
+
+ torture_comment(tctx,
+ "unexpected number of results (%u) returned in dispinfo, call, expected %u\n",
+ num_disp, num_anounced + num_created);
+ }
+
+ return true;
+}
+
+static bool test_Connect(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle);
+
+static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct torture_samr_context *ctx, struct dom_sid *sid)
+{
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(alias_handle);
+ ZERO_STRUCT(user_handle);
+ ZERO_STRUCT(group_handle);
+ ZERO_STRUCT(domain_handle);
+
+ torture_comment(tctx, "Testing OpenDomain of %s\n", dom_sid_string(tctx, sid));
+
+ r.in.connect_handle = &ctx->handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.sid = sid;
+ r.out.domain_handle = &domain_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &r),
+ "OpenDomain failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "OpenDomain failed");
+
+ /* run the domain tests with the main handle closed - this tests
+ the servers reference counting */
+ torture_assert(tctx, test_samr_handle_Close(b, tctx, &ctx->handle), "Failed to close SAMR handle");
+
+ switch (ctx->choice) {
+ case TORTURE_SAMR_PASSWORDS:
+ case TORTURE_SAMR_USER_PRIVILEGES:
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret &= test_CreateUser2(p, tctx, &domain_handle, sid, ctx->choice, NULL);
+ }
+ ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, NULL, true);
+ if (!ret) {
+ torture_result(tctx, TORTURE_FAIL, "Testing PASSWORDS or PRIVILEGES on domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+ break;
+ case TORTURE_SAMR_USER_ATTRIBUTES:
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret &= test_CreateUser2(p, tctx, &domain_handle, sid, ctx->choice, NULL);
+ }
+ ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, NULL, true);
+ /* This test needs 'complex' users to validate */
+ ret &= test_QueryDisplayInfo(b, tctx, &domain_handle);
+ if (!ret) {
+ torture_result(tctx, TORTURE_FAIL, "Testing ATTRIBUTES on domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+ break;
+ case TORTURE_SAMR_PASSWORDS_PWDLASTSET:
+ case TORTURE_SAMR_PASSWORDS_BADPWDCOUNT:
+ case TORTURE_SAMR_PASSWORDS_LOCKOUT:
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret &= test_CreateUser2(p, tctx, &domain_handle, sid, ctx->choice, ctx->machine_credentials);
+ }
+ ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, ctx->machine_credentials, true);
+ if (!ret) {
+ torture_result(tctx, TORTURE_FAIL, "Testing PASSWORDS PWDLASTSET or BADPWDCOUNT on domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+ break;
+ case TORTURE_SAMR_MANY_ACCOUNTS:
+ case TORTURE_SAMR_MANY_GROUPS:
+ case TORTURE_SAMR_MANY_ALIASES:
+ ret &= test_ManyObjects(p, tctx, &domain_handle, sid, ctx);
+ if (!ret) {
+ torture_result(tctx, TORTURE_FAIL, "Testing MANY-{ACCOUNTS,GROUPS,ALIASES} on domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+ break;
+ case TORTURE_SAMR_OTHER:
+ ret &= test_CreateUser(p, tctx, &domain_handle, TEST_ACCOUNT_NAME, &user_handle, sid, ctx->choice, NULL, true);
+ if (!ret) {
+ torture_result(tctx, TORTURE_FAIL, "Failed to CreateUser in SAMR-OTHER on domain %s!\n", dom_sid_string(tctx, sid));
+ }
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ ret &= test_QuerySecurity(b, tctx, &domain_handle);
+ }
+ ret &= test_RemoveMemberFromForeignDomain(b, tctx, &domain_handle);
+ ret &= test_CreateAlias(b, tctx, &domain_handle, TEST_ALIASNAME, &alias_handle, sid, true);
+ ret &= test_CreateDomainGroup(b, tctx, &domain_handle, TEST_GROUPNAME, &group_handle, sid, true);
+ ret &= test_GetAliasMembership(b, tctx, &domain_handle);
+ ret &= test_QueryDomainInfo(p, tctx, &domain_handle);
+ ret &= test_QueryDomainInfo2(b, tctx, &domain_handle);
+ ret &= test_EnumDomainUsers_all(b, tctx, &domain_handle);
+ ret &= test_EnumDomainUsers_async(p, tctx, &domain_handle);
+ ret &= test_EnumDomainGroups_all(b, tctx, &domain_handle);
+ ret &= test_EnumDomainAliases_all(b, tctx, &domain_handle);
+ ret &= test_QueryDisplayInfo2(b, tctx, &domain_handle);
+ ret &= test_QueryDisplayInfo3(b, tctx, &domain_handle);
+ ret &= test_QueryDisplayInfo_continue(b, tctx, &domain_handle);
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "skipping GetDisplayEnumerationIndex test against Samba4\n");
+ } else {
+ ret &= test_GetDisplayEnumerationIndex(b, tctx, &domain_handle);
+ ret &= test_GetDisplayEnumerationIndex2(b, tctx, &domain_handle);
+ }
+ ret &= test_GroupList(b, tctx, sid, &domain_handle);
+ ret &= test_TestPrivateFunctionsDomain(b, tctx, &domain_handle);
+ ret &= test_RidToSid(b, tctx, sid, &domain_handle);
+ ret &= test_GetBootKeyInformation(b, tctx, &domain_handle);
+ if (!ret) {
+ torture_comment(tctx, "Testing SAMR-OTHER on domain %s failed!\n", dom_sid_string(tctx, sid));
+ }
+ break;
+ }
+
+ if (!ndr_policy_handle_empty(&user_handle) &&
+ !test_DeleteUser(b, tctx, &user_handle)) {
+ ret = false;
+ }
+
+ if (!ndr_policy_handle_empty(&alias_handle) &&
+ !test_DeleteAlias(b, tctx, &alias_handle)) {
+ ret = false;
+ }
+
+ if (!ndr_policy_handle_empty(&group_handle) &&
+ !test_DeleteDomainGroup(b, tctx, &group_handle)) {
+ ret = false;
+ }
+
+ torture_assert(tctx, test_samr_handle_Close(b, tctx, &domain_handle), "Failed to close SAMR domain handle");
+
+ torture_assert(tctx, test_Connect(b, tctx, &ctx->handle), "Faile to re-connect SAMR handle");
+ /* reconnect the main handle */
+
+ if (!ret) {
+ torture_result(tctx, TORTURE_FAIL, "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 torture_samr_context *ctx, const char *domain)
+{
+ struct samr_LookupDomain r;
+ struct dom_sid2 *sid = NULL;
+ struct lsa_String n1;
+ struct lsa_String n2;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing LookupDomain(%s)\n", domain);
+
+ /* check for correct error codes */
+ r.in.connect_handle = &ctx->handle;
+ r.in.domain_name = &n2;
+ r.out.sid = &sid;
+ n2.string = NULL;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r),
+ "LookupDomain failed");
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_INVALID_PARAMETER, r.out.result, "LookupDomain expected NT_STATUS_INVALID_PARAMETER");
+
+ init_lsa_String(&n2, "xxNODOMAINxx");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r),
+ "LookupDomain failed");
+ torture_assert_ntstatus_equal(tctx, NT_STATUS_NO_SUCH_DOMAIN, r.out.result, "LookupDomain expected NT_STATUS_NO_SUCH_DOMAIN");
+
+ r.in.connect_handle = &ctx->handle;
+
+ init_lsa_String(&n1, domain);
+ r.in.domain_name = &n1;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r),
+ "LookupDomain failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LookupDomain");
+
+ if (!test_GetDomPwInfo(p, tctx, &n1)) {
+ ret = false;
+ }
+
+ if (!test_OpenDomain(p, tctx, ctx, *r.out.sid)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+
+static bool test_EnumDomains(struct dcerpc_pipe *p, struct torture_context *tctx,
+ struct torture_samr_context *ctx)
+{
+ struct samr_EnumDomains r;
+ uint32_t resume_handle = 0;
+ uint32_t num_entries = 0;
+ struct samr_SamArray *sam = NULL;
+ int i;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.connect_handle = &ctx->handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.buf_size = (uint32_t)-1;
+ r.out.resume_handle = &resume_handle;
+ r.out.num_entries = &num_entries;
+ r.out.sam = &sam;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &r),
+ "EnumDomains failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "EnumDomains failed");
+
+ if (!*r.out.sam) {
+ return false;
+ }
+
+ for (i=0;i<sam->count;i++) {
+ if (!test_LookupDomain(p, tctx, ctx,
+ sam->entries[i].name.string)) {
+ ret = false;
+ }
+ }
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &r),
+ "EnumDomains failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "EnumDomains failed");
+
+ return ret;
+}
+
+
+static bool test_Connect(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ 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;
+ uint32_t level_out = 0;
+ bool ret = true, got_handle = false;
+
+ torture_comment(tctx, "Testing samr_Connect\n");
+
+ r.in.system_name = NULL;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = &h;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect_r(b, tctx, &r),
+ "Connect failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "Connect failed - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ } else {
+ got_handle = true;
+ *handle = h;
+ }
+
+ torture_comment(tctx, "Testing samr_Connect2\n");
+
+ r2.in.system_name = NULL;
+ r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r2.out.connect_handle = &h;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect2_r(b, tctx, &r2),
+ "Connect2 failed");
+ if (!NT_STATUS_IS_OK(r2.out.result)) {
+ torture_comment(tctx, "Connect2 failed - %s\n", nt_errstr(r2.out.result));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(b, tctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect3_r(b, tctx, &r3),
+ "Connect3 failed");
+ if (!NT_STATUS_IS_OK(r3.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "Connect3 failed - %s\n", nt_errstr(r3.out.result));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(b, tctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ torture_comment(tctx, "Testing samr_Connect4\n");
+
+ r4.in.system_name = "";
+ r4.in.client_version = 0;
+ r4.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r4.out.connect_handle = &h;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect4_r(b, tctx, &r4),
+ "Connect4 failed");
+ if (!NT_STATUS_IS_OK(r4.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "Connect4 failed - %s\n", nt_errstr(r4.out.result));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(b, tctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ torture_comment(tctx, "Testing samr_Connect5\n");
+
+ info.info1.client_version = 0;
+ info.info1.supported_features = 0;
+
+ r5.in.system_name = "";
+ r5.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r5.in.level_in = 1;
+ r5.out.level_out = &level_out;
+ r5.in.info_in = &info;
+ r5.out.info_out = &info;
+ r5.out.connect_handle = &h;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect5_r(b, tctx, &r5),
+ "Connect5 failed");
+ if (!NT_STATUS_IS_OK(r5.out.result)) {
+ torture_result(tctx, TORTURE_FAIL, "Connect5 failed - %s\n", nt_errstr(r5.out.result));
+ ret = false;
+ } else {
+ if (got_handle) {
+ test_samr_handle_Close(b, tctx, handle);
+ }
+ got_handle = true;
+ *handle = h;
+ }
+
+ return ret;
+}
+
+
+static bool test_samr_ValidatePassword(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct samr_ValidatePassword r;
+ union samr_ValidatePasswordReq req;
+ union samr_ValidatePasswordRep *repp = NULL;
+ NTSTATUS status;
+ const char *passwords[] = { "penguin", "p@ssw0rd", "p@ssw0rd123$", NULL };
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing samr_ValidatePassword\n");
+
+ if (p->conn->transport.transport != NCACN_IP_TCP) {
+ torture_comment(tctx, "samr_ValidatePassword only should succeed over NCACN_IP_TCP!\n");
+ }
+
+ ZERO_STRUCT(r);
+ r.in.level = NetValidatePasswordReset;
+ r.in.req = &req;
+ r.out.rep = &repp;
+
+ ZERO_STRUCT(req);
+ req.req3.account.string = "non-existent-account-aklsdji";
+
+ for (i=0; passwords[i]; i++) {
+ req.req3.password.string = passwords[i];
+
+ status = dcerpc_samr_ValidatePassword_r(b, tctx, &r);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ torture_skip(tctx, "ValidatePassword not supported by server\n");
+ }
+ torture_assert_ntstatus_ok(tctx, status,
+ "samr_ValidatePassword failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_ValidatePassword failed");
+ torture_comment(tctx, "Server %s password '%s' with code %i\n",
+ repp->ctr3.status==SAMR_VALIDATION_STATUS_SUCCESS?"allowed":"refused",
+ req.req3.password.string, repp->ctr3.status);
+ }
+
+ return true;
+}
+
+bool torture_rpc_samr(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx;
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx = talloc_zero(torture, struct torture_samr_context);
+
+ ctx->choice = TORTURE_SAMR_OTHER;
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ if (!torture_setting_bool(torture, "samba3", false)) {
+ ret &= test_QuerySecurity(b, torture, &ctx->handle);
+ }
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_SetDsrmPassword(b, torture, &ctx->handle);
+
+ ret &= test_Shutdown(b, torture, &ctx->handle);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+
+bool torture_rpc_samr_users(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx;
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx = talloc_zero(torture, struct torture_samr_context);
+
+ ctx->choice = TORTURE_SAMR_USER_ATTRIBUTES;
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ if (!torture_setting_bool(torture, "samba3", false)) {
+ ret &= test_QuerySecurity(b, torture, &ctx->handle);
+ }
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_SetDsrmPassword(b, torture, &ctx->handle);
+
+ ret &= test_Shutdown(b, torture, &ctx->handle);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+
+bool torture_rpc_samr_passwords(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx;
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx = talloc_zero(torture, struct torture_samr_context);
+
+ ctx->choice = TORTURE_SAMR_PASSWORDS;
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+static bool torture_rpc_samr_pwdlastset(struct torture_context *torture,
+ struct dcerpc_pipe *p2,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx;
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx = talloc_zero(torture, struct torture_samr_context);
+
+ ctx->choice = TORTURE_SAMR_PASSWORDS_PWDLASTSET;
+ ctx->machine_credentials = machine_credentials;
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_samr_passwords_pwdlastset(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.pwdlastset");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "samr",
+ &ndr_table_samr,
+ TEST_ACCOUNT_NAME_PWD);
+
+ torture_rpc_tcase_add_test_creds(tcase, "pwdLastSet",
+ torture_rpc_samr_pwdlastset);
+
+ return suite;
+}
+
+static bool torture_rpc_samr_users_privileges_delete_user(struct torture_context *torture,
+ struct dcerpc_pipe *p2,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx;
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx = talloc_zero(torture, struct torture_samr_context);
+
+ ctx->choice = TORTURE_SAMR_USER_PRIVILEGES;
+ ctx->machine_credentials = machine_credentials;
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_samr_user_privileges(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.users.privileges");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "samr",
+ &ndr_table_samr,
+ TEST_ACCOUNT_NAME_PWD);
+
+ torture_rpc_tcase_add_test_creds(tcase, "delete_privileged_user",
+ torture_rpc_samr_users_privileges_delete_user);
+
+ return suite;
+}
+
+static bool torture_rpc_samr_many_accounts(struct torture_context *torture,
+ struct dcerpc_pipe *p2,
+ void *data)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx =
+ talloc_get_type_abort(data, struct torture_samr_context);
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx->choice = TORTURE_SAMR_MANY_ACCOUNTS;
+ ctx->num_objects_large_dc = torture_setting_int(torture, "large_dc",
+ ctx->num_objects_large_dc);
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+static bool torture_rpc_samr_many_groups(struct torture_context *torture,
+ struct dcerpc_pipe *p2,
+ void *data)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx =
+ talloc_get_type_abort(data, struct torture_samr_context);
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx->choice = TORTURE_SAMR_MANY_GROUPS;
+ ctx->num_objects_large_dc = torture_setting_int(torture, "large_dc",
+ ctx->num_objects_large_dc);
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+static bool torture_rpc_samr_many_aliases(struct torture_context *torture,
+ struct dcerpc_pipe *p2,
+ void *data)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx =
+ talloc_get_type_abort(data, struct torture_samr_context);
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx->choice = TORTURE_SAMR_MANY_ALIASES;
+ ctx->num_objects_large_dc = torture_setting_int(torture, "large_dc",
+ ctx->num_objects_large_dc);
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_samr_large_dc(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.large-dc");
+ struct torture_rpc_tcase *tcase;
+ struct torture_samr_context *ctx;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "samr", &ndr_table_samr);
+
+ ctx = talloc_zero(suite, struct torture_samr_context);
+ ctx->num_objects_large_dc = 150;
+
+ torture_rpc_tcase_add_test_ex(tcase, "many_aliases",
+ torture_rpc_samr_many_aliases, ctx);
+ torture_rpc_tcase_add_test_ex(tcase, "many_groups",
+ torture_rpc_samr_many_groups, ctx);
+ torture_rpc_tcase_add_test_ex(tcase, "many_accounts",
+ torture_rpc_samr_many_accounts, ctx);
+
+ return suite;
+}
+
+static bool torture_rpc_samr_badpwdcount(struct torture_context *torture,
+ struct dcerpc_pipe *p2,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx;
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx = talloc_zero(torture, struct torture_samr_context);
+
+ ctx->choice = TORTURE_SAMR_PASSWORDS_BADPWDCOUNT;
+ ctx->machine_credentials = machine_credentials;
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_samr_passwords_badpwdcount(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.badpwdcount");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "samr",
+ &ndr_table_samr,
+ TEST_ACCOUNT_NAME_PWD);
+
+ torture_rpc_tcase_add_test_creds(tcase, "badPwdCount",
+ torture_rpc_samr_badpwdcount);
+
+ return suite;
+}
+
+static bool torture_rpc_samr_lockout(struct torture_context *torture,
+ struct dcerpc_pipe *p2,
+ struct cli_credentials *machine_credentials)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ bool ret = true;
+ struct torture_samr_context *ctx;
+ struct dcerpc_binding_handle *b;
+
+ status = torture_rpc_connection(torture, &p, &ndr_table_samr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ b = p->binding_handle;
+
+ ctx = talloc_zero(torture, struct torture_samr_context);
+
+ ctx->choice = TORTURE_SAMR_PASSWORDS_LOCKOUT;
+ ctx->machine_credentials = machine_credentials;
+
+ ret &= test_Connect(b, torture, &ctx->handle);
+
+ ret &= test_EnumDomains(p, torture, ctx);
+
+ ret &= test_samr_handle_Close(b, torture, &ctx->handle);
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_samr_passwords_lockout(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.lockout");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_machine_bdc_rpc_iface_tcase(suite, "samr",
+ &ndr_table_samr,
+ TEST_ACCOUNT_NAME_PWD);
+
+ torture_rpc_tcase_add_test_creds(tcase, "lockout",
+ torture_rpc_samr_lockout);
+
+ return suite;
+}
+
+struct torture_suite *torture_rpc_samr_passwords_validate(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.passwords.validate");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "samr",
+ &ndr_table_samr);
+ torture_rpc_tcase_add_test(tcase, "validate",
+ test_samr_ValidatePassword);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/samr_accessmask.c b/source4/torture/rpc/samr_accessmask.c
new file mode 100644
index 0000000..0d4c69b
--- /dev/null
+++ b/source4/torture/rpc/samr_accessmask.c
@@ -0,0 +1,1197 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for accessmasks on the SAMR pipe
+
+ Copyright (C) Ronnie Sahlberg 2007
+ Copyright (C) Guenther Deschner 2009
+
+ 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_samr_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+
+
+/* test user created to test the ACLs associated to SAMR objects */
+#define TEST_USER_NAME "samr_testuser"
+#define TEST_MACHINENAME "samrtestmach"
+
+static NTSTATUS torture_samr_Close(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *h)
+{
+ NTSTATUS status;
+ struct samr_Close cl;
+
+ cl.in.handle = h;
+ cl.out.handle = h;
+ status = dcerpc_samr_Close_r(b, tctx, &cl);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return cl.out.result;
+}
+
+static NTSTATUS torture_samr_Connect5(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t mask, struct policy_handle *h)
+{
+ NTSTATUS status;
+ struct samr_Connect5 r5;
+ union samr_ConnectInfo info;
+ uint32_t level_out = 0;
+
+ info.info1.client_version = 0;
+ info.info1.supported_features = 0;
+ r5.in.system_name = "";
+ r5.in.level_in = 1;
+ r5.in.info_in = &info;
+ r5.out.info_out = &info;
+ r5.out.level_out = &level_out;
+ r5.out.connect_handle = h;
+ r5.in.access_mask = mask;
+
+ status = dcerpc_samr_Connect5_r(b, tctx, &r5);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r5.out.result;
+}
+
+/* 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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, b, 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, b, &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;
+ struct samr_SamArray *sam = NULL;
+ uint32_t num_entries = 0;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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, b, 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;
+ ed.out.num_entries = &num_entries;
+ ed.out.sam = &sam;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &ed),
+ "EnumDomains failed");
+ if (!NT_STATUS_IS_OK(ed.out.result)) {
+ printf("EnumDomains failed - %s\n", nt_errstr(ed.out.result));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, b, &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;
+ ed.out.num_entries = &num_entries;
+ ed.out.sam = &sam;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &ed),
+ "EnumDomains failed");
+ if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, ed.out.result)) {
+ printf("EnumDomains failed - %s\n", nt_errstr(ed.out.result));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, b, &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_binding_handle *b,
+ 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, *sdbuf = NULL;
+ bool ret = true;
+ int sd_size;
+ struct dcerpc_pipe *test_p;
+ struct dcerpc_binding_handle *test_b;
+ 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, b, 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;
+ qs.out.sdbuf = &sdbuf;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &qs),
+ "QuerySecurity failed");
+ if (!NT_STATUS_IS_OK(qs.out.result)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(qs.out.result));
+ ret = false;
+ }
+
+ /* how big is the security descriptor? */
+ sd_size = sdbuf->sd_size;
+
+
+ /* add an ACE to the security descriptor to deny the user the
+ * 'connect to server' right
+ */
+ sd = 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;
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_SetSecurity_r(b, tctx, &ss),
+ "SetSecurity failed");
+ if (!NT_STATUS_IS_OK(ss.out.result)) {
+ printf("SetSecurity failed - %s\n", nt_errstr(ss.out.result));
+ ret = false;
+ }
+
+
+ /* Try to connect as the test user */
+ status = dcerpc_pipe_connect(tctx,
+ &test_p, binding, &ndr_table_samr,
+ test_credentials, tctx->ev, tctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_pipe_connect failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ test_b = test_p->binding_handle;
+
+ /* connect to SAMR as the user */
+ status = torture_samr_Connect5(tctx, test_b, 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);
+
+
+ /* read the sequrity descriptor back. it should not have changed
+ * eventhough samr_SetSecurity returned SUCCESS
+ */
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QuerySecurity_r(b, tctx, &qs),
+ "QuerySecurity failed");
+ if (!NT_STATUS_IS_OK(qs.out.result)) {
+ printf("QuerySecurity failed - %s\n", nt_errstr(qs.out.result));
+ ret = false;
+ }
+ if (sd_size != sdbuf->sd_size) {
+ printf("security descriptor changed\n");
+ ret = false;
+ }
+
+
+ status = torture_samr_Close(tctx, b, &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_binding_handle *b,
+ 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;
+ struct dcerpc_binding_handle *test_b;
+ 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, tctx->ev, tctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("dcerpc_pipe_connect failed: %s\n", nt_errstr(status));
+ return false;
+ }
+ test_b = test_p->binding_handle;
+
+ /* connect to SAMR as the user */
+ status = torture_samr_Connect5(tctx, test_b, 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 dom_sid2 *sid = NULL;
+ struct policy_handle ch;
+ struct lsa_String dn;
+ int i;
+ uint32_t mask;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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, b, 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;
+ ld.out.sid = &sid;
+ dn.string = lpcfg_workgroup(tctx->lp_ctx);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &ld),
+ "LookupDomain failed");
+ if (!NT_STATUS_IS_OK(ld.out.result)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(ld.out.result));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, b, &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;
+ ld.out.sid = &sid;
+ dn.string = lpcfg_workgroup(tctx->lp_ctx);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &ld),
+ "LookupDomain failed");
+ if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, ld.out.result)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(ld.out.result));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, b, &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 dom_sid2 *sid = NULL;
+ struct samr_OpenDomain od;
+ struct policy_handle ch;
+ struct policy_handle dh;
+ struct lsa_String dn;
+ int i;
+ uint32_t mask;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+
+ /* first we must grab the sid of the domain */
+ status = torture_samr_Connect5(tctx, b, 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;
+ ld.out.sid = &sid;
+ dn.string = lpcfg_workgroup(tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &ld),
+ "LookupDomain failed");
+ if (!NT_STATUS_IS_OK(ld.out.result)) {
+ printf("LookupDomain failed - %s\n", nt_errstr(ld.out.result));
+ 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, b, 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 = sid;
+ od.out.domain_handle = &dh;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &od),
+ "OpenDomain failed");
+ if (!NT_STATUS_IS_OK(od.out.result)) {
+ printf("OpenDomain failed - %s\n", nt_errstr(od.out.result));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, b, &dh);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Close failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ status = torture_samr_Close(tctx, b, &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, b, &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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "Skipping test against Samba 3");
+ }
+
+ /* create a test user */
+ testuser = torture_create_testuser(tctx, TEST_USER_NAME, lpcfg_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, lpcfg_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 if ACLs can be changed for the policy handle
+ * returned by Connect5
+ */
+ if (!test_samr_connect_user_acl(tctx, b, 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, b, 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);
+
+ /* test which bits in the accessmask to Connect5 will allow
+ * us to call OpenDomain() */
+ torture_rpc_tcase_add_test(tcase, "OpenDomain",
+ test_samr_accessmask_OpenDomain);
+
+ /* test which bits in the accessmask to Connect5 will allow
+ * us to call LookupDomain() */
+ torture_rpc_tcase_add_test(tcase, "LookupDomain",
+ test_samr_accessmask_LookupDomain);
+
+ /* test which bits in the accessmask to Connect5 will allow
+ * us to call EnumDomains() */
+ torture_rpc_tcase_add_test(tcase, "EnumDomains",
+ test_samr_accessmask_EnumDomains);
+
+ /* test which bits in the accessmask to Connect5
+ will allow us to connect to the server */
+ torture_rpc_tcase_add_test(tcase, "Connect5",
+ test_samr_accessmask_Connect5);
+
+ return suite;
+}
+
+static bool test_LookupRids(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *domain_handle,
+ uint32_t rid)
+{
+ struct samr_LookupRids r;
+ struct lsa_Strings names;
+ struct samr_Ids types;
+
+ torture_comment(tctx, "Testing LookupRids %d\n", rid);
+
+ r.in.domain_handle = domain_handle;
+ r.in.num_rids = 1;
+ r.in.rids = &rid;
+ r.out.names = &names;
+ r.out.types = &types;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupRids_r(b, tctx, &r),
+ "failed to call samr_LookupRids");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to call samr_LookupRids");
+
+ return true;
+}
+
+
+static bool test_user(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *domain_handle,
+ uint32_t access_mask,
+ struct samr_DispEntryGeneral *u)
+{
+ struct policy_handle user_handle;
+
+ torture_comment(tctx, "Testing user %s (%d)\n", u->account_name.string, u->rid);
+
+ torture_assert(tctx, test_LookupRids(tctx, b, domain_handle, u->rid),
+ "failed to call lookuprids");
+
+ {
+ struct samr_OpenUser r;
+
+ r.in.domain_handle = domain_handle;
+ r.in.access_mask = access_mask;
+ r.in.rid = u->rid;
+ r.out.user_handle = &user_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenUser_r(b, tctx, &r),
+ "failed to open user");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to open user");
+ }
+ {
+ struct samr_QueryUserInfo r;
+ union samr_UserInfo *info;
+ uint32_t levels[] = { 16, 21 };
+ int i;
+
+ r.in.user_handle = &user_handle;
+ r.out.info = &info;
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+
+ r.in.level = levels[i];
+
+ torture_comment(tctx, "Testing QueryUserInfo rid: %d level: %d\n",
+ u->rid, r.in.level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryUserInfo_r(b, tctx, &r),
+ talloc_asprintf(tctx, "failed to query user info level %d", r.in.level));
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "failed to query user info level %d", r.in.level));
+ }
+ }
+ {
+ struct samr_GetGroupsForUser r;
+ struct samr_RidWithAttributeArray *rids;
+
+ r.in.user_handle = &user_handle;
+ r.out.rids = &rids;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetGroupsForUser_r(b, tctx, &r),
+ "failed to query groups for user");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query groups for user");
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_samr_Close(tctx, b, &user_handle),
+ "failed to close user handle");
+
+ return true;
+}
+
+static bool test_samr_group(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *domain_handle,
+ uint32_t access_mask,
+ struct samr_SamEntry *g)
+{
+ struct policy_handle group_handle;
+
+ torture_comment(tctx, "Testing group %s (%d)\n", g->name.string, g->idx);
+
+ torture_assert(tctx, test_LookupRids(tctx, b, domain_handle, g->idx),
+ "failed to call lookuprids");
+ {
+ struct samr_OpenGroup r;
+
+ r.in.domain_handle = domain_handle;
+ r.in.access_mask = access_mask;
+ r.in.rid = g->idx;
+ r.out.group_handle = &group_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenGroup_r(b, tctx, &r),
+ "failed to open group");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to open group");
+ }
+ {
+ struct samr_QueryGroupMember r;
+ struct samr_RidAttrArray *rids;
+
+ r.in.group_handle = &group_handle;
+ r.out.rids = &rids;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryGroupMember_r(b, tctx, &r),
+ "failed to query group member");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to query group member");
+
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_samr_Close(tctx, b, &group_handle),
+ "failed to close group handle");
+
+ return true;
+}
+
+static bool test_samr_alias(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *domain_handle,
+ struct samr_SamEntry *a)
+{
+ torture_comment(tctx, "Testing alias %s (%d)\n", a->name.string, a->idx);
+
+ torture_assert(tctx, test_LookupRids(tctx, b, domain_handle, a->idx),
+ "failed to call lookuprids");
+
+ {
+ struct samr_GetAliasMembership r;
+ struct lsa_SidArray sids;
+ struct samr_Ids rids;
+
+ ZERO_STRUCT(sids);
+
+ r.in.domain_handle = domain_handle;
+ r.in.sids = &sids;
+ r.out.rids = &rids;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetAliasMembership_r(b, tctx, &r),
+ "failed to get alias membership");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to get alias membership");
+ }
+
+
+ return true;
+}
+
+static bool test_samr_domain(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t access_mask,
+ const char *domain_name,
+ struct policy_handle *connect_handle,
+ struct policy_handle *domain_handle_p)
+{
+ struct policy_handle domain_handle;
+ struct dom_sid *domain_sid;
+
+ if (!domain_name) {
+ struct samr_EnumDomains r;
+ uint32_t resume_handle;
+ struct samr_SamArray *sam;
+ uint32_t num_entries;
+ int i;
+
+ r.in.connect_handle = connect_handle;
+ r.in.buf_size = 0xffff;
+ r.in.resume_handle = &resume_handle;
+ r.out.sam = &sam;
+ r.out.num_entries = &num_entries;
+ r.out.resume_handle = &resume_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomains_r(b, tctx, &r),
+ "failed to enum domains");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to enum domains");
+
+ torture_assert_int_equal(tctx, num_entries, 2,
+ "unexpected number of domains");
+
+ torture_assert(tctx, sam,
+ "no domain pointer returned");
+
+ for (i=0; i < sam->count; i++) {
+ if (!strequal(sam->entries[i].name.string, "builtin")) {
+ domain_name = sam->entries[i].name.string;
+ break;
+ }
+ }
+
+ torture_assert(tctx, domain_name,
+ "no domain found other than builtin found");
+ }
+
+ {
+ struct samr_LookupDomain r;
+ struct dom_sid2 *sid;
+ struct lsa_String name;
+
+ name.string = talloc_strdup(tctx, domain_name);
+
+ r.in.connect_handle = connect_handle;
+ r.in.domain_name = &name;
+ r.out.sid = &sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_LookupDomain_r(b, tctx, &r),
+ "failed to lookup domain");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to lookup domain");
+
+ domain_sid = dom_sid_dup(tctx, sid);
+ }
+
+ {
+ struct samr_OpenDomain r;
+
+ r.in.connect_handle = connect_handle;
+ r.in.access_mask = access_mask;
+ r.in.sid = domain_sid;
+ r.out.domain_handle = &domain_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &r),
+ "failed to open domain");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to open domain");
+
+ }
+
+ {
+ struct samr_QueryDomainInfo r;
+ union samr_DomainInfo *info;
+ uint32_t levels[] = { 1, 2, 8, 12 };
+ int i;
+
+ r.in.domain_handle = &domain_handle;
+ r.out.info = &info;
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+
+ r.in.level = levels[i];
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDomainInfo_r(b, tctx, &r),
+ talloc_asprintf(tctx, "failed to query domain info level %d", r.in.level));
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "failed to query domain info level %d", r.in.level));
+ }
+
+ }
+
+ *domain_handle_p = domain_handle;
+
+ return true;
+}
+
+static void get_query_dispinfo_params(int loop_count,
+ uint32_t *max_entries,
+ uint32_t *buf_size)
+{
+ switch(loop_count) {
+ case 0:
+ *max_entries = 512;
+ *buf_size = 16383;
+ break;
+ case 1:
+ *max_entries = 1024;
+ *buf_size = 32766;
+ break;
+ case 2:
+ *max_entries = 2048;
+ *buf_size = 65532;
+ break;
+ case 3:
+ *max_entries = 4096;
+ *buf_size = 131064;
+ break;
+ default: /* loop_count >= 4 */
+ *max_entries = 4096;
+ *buf_size = 131071;
+ break;
+ }
+}
+
+
+static bool test_samr_users(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t access_mask,
+ struct policy_handle *domain_handle)
+{
+ {
+ struct samr_QueryDisplayInfo r;
+ uint32_t total_size;
+ uint32_t returned_size;
+ union samr_DispInfo info;
+ int loop_count = 0;
+
+ r.in.domain_handle = domain_handle;
+ r.in.level = 1;
+ r.in.start_idx = 0;
+
+ r.out.total_size = &total_size;
+ r.out.returned_size = &returned_size;
+ r.out.info = &info;
+
+ do {
+ int i;
+
+ r.in.max_entries = 0xffff;
+ r.in.buf_size = 0xffff;
+
+ get_query_dispinfo_params(loop_count,
+ &r.in.max_entries,
+ &r.in.buf_size);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_QueryDisplayInfo_r(b, tctx, &r),
+ "QueryDisplayInfo failed");
+ if (NT_STATUS_IS_ERR(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to call QueryDisplayInfo");
+ }
+
+ for (i=0; i < info.info1.count; i++) {
+ torture_assert(tctx,
+ test_user(tctx, b, domain_handle, access_mask, &info.info1.entries[i]),
+ "failed to test user");
+ }
+ loop_count++;
+ r.in.start_idx += info.info1.count;
+
+ } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+static bool test_samr_groups(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t access_mask,
+ struct policy_handle *domain_handle)
+{
+ {
+ struct samr_EnumDomainGroups r;
+ uint32_t resume_handle = 0;
+ struct samr_SamArray *sam;
+ uint32_t num_entries;
+
+ r.in.domain_handle = domain_handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = 0xFFFF;
+
+ r.out.sam = &sam;
+ r.out.num_entries = &num_entries;
+ r.out.resume_handle = &resume_handle;
+
+ do {
+ int i;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainGroups_r(b, tctx, &r),
+ "EnumDomainGroups failed");
+ if (NT_STATUS_IS_ERR(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to call EnumDomainGroups");
+ }
+
+ for (i=0; i < num_entries; i++) {
+ torture_assert(tctx,
+ test_samr_group(tctx, b, domain_handle, access_mask, &sam->entries[i]),
+ "failed to test group");
+ }
+
+ } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+static bool test_samr_aliases(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t access_mask,
+ struct policy_handle *domain_handle)
+{
+ {
+ struct samr_EnumDomainAliases r;
+ uint32_t resume_handle = 0;
+ struct samr_SamArray *sam;
+ uint32_t num_entries;
+
+ r.in.domain_handle = domain_handle;
+ r.in.resume_handle = &resume_handle;
+ r.in.max_size = 0xFFFF;
+
+ r.out.sam = &sam;
+ r.out.num_entries = &num_entries;
+ r.out.resume_handle = &resume_handle;
+
+ do {
+ int i;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_EnumDomainAliases_r(b, tctx, &r),
+ "EnumDomainAliases failed");
+ if (NT_STATUS_IS_ERR(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "failed to call EnumDomainAliases");
+ }
+
+ for (i=0; i < num_entries; i++) {
+ torture_assert(tctx,
+ test_samr_alias(tctx, b, domain_handle, &sam->entries[i]),
+ "failed to test alias");
+ }
+
+ } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES));
+ }
+
+ return true;
+}
+
+static bool torture_rpc_samr_workstation_query(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct cli_credentials *machine_credentials)
+{
+ struct policy_handle connect_handle;
+ struct policy_handle domain_handle;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_samr_Connect5(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED,
+ &connect_handle),
+ "failed to connect to samr server");
+
+ torture_assert(tctx,
+ test_samr_domain(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED,
+ lpcfg_workgroup(tctx->lp_ctx),
+ &connect_handle, &domain_handle),
+ "failed to test domain");
+
+ torture_assert(tctx,
+ test_samr_users(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED,
+ &domain_handle),
+ "failed to test users");
+
+ torture_assert(tctx,
+ test_samr_groups(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED,
+ &domain_handle),
+ "failed to test groups");
+
+ torture_assert(tctx,
+ test_samr_aliases(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED,
+ &domain_handle),
+ "failed to test aliases");
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_samr_Close(tctx, b, &domain_handle),
+ "failed to close domain handle");
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_samr_Close(tctx, b, &connect_handle),
+ "failed to close connect handle");
+
+ return true;
+}
+
+/* The purpose of this test is to verify that an account authenticated as a
+ * domain member workstation can query a DC for various remote read calls all
+ * opening objects while requesting SEC_FLAG_MAXIMUM_ALLOWED access rights on
+ * the object open calls. This is the behavior of winbind (and most of samba's
+ * client code) - gd */
+
+struct torture_suite *torture_rpc_samr_workstation_auth(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "samr.machine.auth");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "samr",
+ &ndr_table_samr,
+ TEST_MACHINENAME);
+
+ torture_rpc_tcase_add_test_creds(tcase, "workstation_query",
+ torture_rpc_samr_workstation_query);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/samr_handletype.c b/source4/torture/rpc/samr_handletype.c
new file mode 100644
index 0000000..ab5e7d0
--- /dev/null
+++ b/source4/torture/rpc/samr_handletype.c
@@ -0,0 +1,217 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ test suite for handle types on the SAMR pipe
+
+ Copyright (C) Samuel Cabrero <scabrero@samba.org> 2020
+
+ 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_samr_c.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+
+enum samr_handle {
+ SAMR_HANDLE_CONNECT,
+ SAMR_HANDLE_DOMAIN,
+ SAMR_HANDLE_USER,
+ SAMR_HANDLE_GROUP,
+ SAMR_HANDLE_ALIAS
+};
+
+static NTSTATUS torture_samr_Close(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *h)
+{
+ NTSTATUS status;
+ struct samr_Close cl;
+
+ cl.in.handle = h;
+ cl.out.handle = h;
+ status = dcerpc_samr_Close_r(b, tctx, &cl);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return cl.out.result;
+}
+
+static NTSTATUS torture_samr_Connect5(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t mask, struct policy_handle *h)
+{
+ NTSTATUS status;
+ struct samr_Connect5 r5;
+ union samr_ConnectInfo info;
+ uint32_t level_out = 0;
+
+ info.info1.client_version = 0;
+ info.info1.supported_features = 0;
+ r5.in.system_name = "";
+ r5.in.level_in = 1;
+ r5.in.info_in = &info;
+ r5.out.info_out = &info;
+ r5.out.level_out = &level_out;
+ r5.out.connect_handle = h;
+ r5.in.access_mask = mask;
+
+ status = dcerpc_samr_Connect5_r(b, tctx, &r5);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return r5.out.result;
+}
+
+static bool test_samr_handletype_OpenDomain(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct samr_LookupDomain ld;
+ struct dom_sid2 *sid = NULL;
+ struct samr_OpenDomain od;
+ struct samr_OpenUser ou;
+ struct samr_OpenGroup og;
+ struct policy_handle ch;
+ struct policy_handle bad;
+ struct policy_handle dh;
+ struct policy_handle oh;
+ struct lsa_String dn;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ /* first we must grab the sid of the domain */
+ status = torture_samr_Connect5(tctx, b, SEC_FLAG_MAXIMUM_ALLOWED, &ch);
+ torture_assert_ntstatus_ok(tctx, status, "Connect5 failed");
+
+ ld.in.connect_handle = &ch;
+ ld.in.domain_name = &dn;
+ ld.out.sid = &sid;
+ dn.string = lpcfg_workgroup(tctx->lp_ctx);
+ status = dcerpc_samr_LookupDomain_r(b, tctx, &ld);
+ torture_assert_ntstatus_ok(tctx, status, "LookupDomain failed");
+ torture_assert_ntstatus_ok(tctx, ld.out.result, "LookupDomain failed");
+
+ status = torture_samr_Connect5(tctx, b, 1, &ch);
+ torture_assert_ntstatus_ok(tctx, status, "Connect5 failed");
+
+ od.in.connect_handle = &bad;
+ od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ od.in.sid = sid;
+ od.out.domain_handle = &dh;
+
+ /* Open domain, wrong handle GUID */
+ bad = ch;
+ bad.uuid = GUID_random();
+
+ status = dcerpc_samr_OpenDomain_r(b, tctx, &od);
+ torture_assert_ntstatus_equal(tctx,
+ status,
+ NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "OpenDomain succeeded with random GUID");
+
+ /* Open domain, wrong handle type */
+ bad = ch;
+ bad.handle_type = SAMR_HANDLE_USER;
+
+ status = dcerpc_samr_OpenDomain_r(b, tctx, &od);
+ torture_assert_ntstatus_equal(tctx,
+ status,
+ NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "OpenDomain succeeded with wrong type");
+
+ /* Open domain */
+ bad = ch;
+
+ status = dcerpc_samr_OpenDomain_r(b, tctx, &od);
+ torture_assert_ntstatus_ok(tctx, status, "OpenDomain failed");
+ torture_assert_ntstatus_ok(tctx, od.out.result, "OpenDomain failed");
+
+ bad = dh;
+
+ /* Open user, wrong handle type */
+ ou.in.domain_handle = &bad;
+ ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ ou.in.rid = 501;
+ ou.out.user_handle = &oh;
+
+ bad.handle_type = SAMR_HANDLE_ALIAS;
+
+ status = dcerpc_samr_OpenUser_r(b, tctx, &ou);
+ torture_assert_ntstatus_equal(tctx,
+ status,
+ NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "OpenUser succeeded with wrong type");
+
+ /* Open user */
+ bad.handle_type = SAMR_HANDLE_DOMAIN;
+
+ status = dcerpc_samr_OpenUser_r(b, tctx, &ou);
+ torture_assert_ntstatus_ok(tctx, status, "OpenUser failed");
+ torture_assert_ntstatus_ok(tctx, ou.out.result, "OpenUser failed");
+
+ /* Close user */
+ status = torture_samr_Close(tctx, b, &oh);
+ torture_assert_ntstatus_ok(tctx, status, "Close failed");
+
+ bad = dh;
+
+ /* Open group, wrong type */
+ og.in.domain_handle = &bad;
+ og.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ og.in.rid = 513;
+ og.out.group_handle = &oh;
+
+ bad.handle_type = SAMR_HANDLE_GROUP;
+
+ status = dcerpc_samr_OpenGroup_r(b, tctx, &og);
+ torture_assert_ntstatus_equal(tctx,
+ status,
+ NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "OpenGroup succeeded with wrong type");
+
+ /* Open group */
+ bad.handle_type = SAMR_HANDLE_DOMAIN;
+
+ status = dcerpc_samr_OpenGroup_r(b, tctx, &og);
+ torture_assert_ntstatus_ok(tctx, status, "OpenGroup failed");
+ torture_assert_ntstatus_ok(tctx, ou.out.result, "OpenGroup failed");
+
+ /* Close group */
+ status = torture_samr_Close(tctx, b, &oh);
+ torture_assert_ntstatus_ok(tctx, status, "Close failed");
+
+ /* Close connect */
+ status = torture_samr_Close(tctx, b, &ch);
+ torture_assert_ntstatus_ok(tctx, status, "Close failed");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_samr_handletype(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = NULL;
+ struct torture_rpc_tcase *tcase = NULL;
+
+ suite = torture_suite_create(mem_ctx, "samr.handletype");
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "samr",
+ &ndr_table_samr);
+
+ torture_rpc_tcase_add_test(tcase, "OpenDomainHandleType",
+ test_samr_handletype_OpenDomain);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/samr_priv.c b/source4/torture/rpc/samr_priv.c
new file mode 100644
index 0000000..a5aa4b7
--- /dev/null
+++ b/source4/torture/rpc/samr_priv.c
@@ -0,0 +1,580 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * test suite for samr rpc operations
+ *
+ * Copyright (c) 2011 Andreas Schneider
+ *
+ * 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 "param/param.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/rpc/dcerpc_proto.h"
+#include "libcli/security/security.h"
+#include "torture/rpc/torture_rpc.h"
+
+#define TEST_ACCOUNT_NAME "guru"
+
+struct torture_user {
+ const char *username;
+ const char *password;
+ const char *domain;
+ uint32_t *builtin_memberships;
+ uint32_t num_builtin_memberships;
+ bool admin_rights;
+};
+
+struct torture_access_context {
+ struct dcerpc_pipe *pipe;
+ struct torture_user user;
+ struct test_join *join;
+};
+
+static void init_lsa_String(struct lsa_String *name, const char *s)
+{
+ name->string = s;
+}
+
+static bool test_samr_queryUserInfo(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *user_handle)
+{
+ struct samr_QueryUserInfo r;
+ union samr_UserInfo *info;
+ NTSTATUS status;
+
+ r.in.level = UserGeneralInformation;
+ r.in.user_handle = user_handle;
+ r.out.info = &info;
+
+ status = dcerpc_samr_QueryUserInfo_r(b,
+ tctx,
+ &r);
+ torture_assert_ntstatus_ok(tctx, status, "queryUserInfo failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "queryUserInfo failed");
+
+ return true;
+}
+
+static bool test_LookupName(struct dcerpc_binding_handle *b,
+ 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[1];
+ struct samr_Ids rids, types;
+
+ init_lsa_String(&sname[0], name);
+
+ n.in.domain_handle = domain_handle;
+ n.in.num_names = 1;
+ n.in.names = sname;
+ n.out.rids = &rids;
+ n.out.types = &types;
+
+ status = dcerpc_samr_LookupNames_r(b, tctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(n.out.result)) {
+ return false;
+ }
+
+ *rid = n.out.rids->ids[0];
+ return true;
+}
+
+static bool test_samr_CreateUser(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *domain_handle,
+ const char *name,
+ struct policy_handle *user_handle)
+{
+ struct lsa_String username;
+ struct samr_CreateUser r;
+ uint32_t rid = 0;
+ NTSTATUS status;
+
+ init_lsa_String(&username, name);
+
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.domain_handle = domain_handle;
+ r.in.account_name = &username;
+ r.out.user_handle = user_handle;
+ r.out.rid = &rid;
+
+ status = dcerpc_samr_CreateUser_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "CreateUser failed");
+
+ return NT_STATUS_IS_OK(r.out.result);
+}
+
+static bool test_samr_OpenUser(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *domain_handle,
+ const char *name,
+ struct policy_handle *user_handle,
+ bool expected)
+{
+ struct samr_OpenUser r;
+ uint32_t rid = 0;
+ NTSTATUS status;
+ bool ok;
+
+ ok = test_LookupName(b, tctx, domain_handle, name, &rid);
+ if (!ok && expected) {
+ torture_comment(tctx, " - lookup name for %s failed\n", name);
+ return true;
+ } else if (!ok) {
+ return false;
+ }
+
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.domain_handle = domain_handle;
+ r.in.rid = rid;
+ r.out.user_handle = user_handle;
+
+ status = dcerpc_samr_OpenUser_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "OpenUser failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "OpenUser failed");
+
+ return true;
+}
+
+static bool test_samr_openDomain(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *connect_handle,
+ const char *domain,
+ struct policy_handle *domain_handle)
+{
+ struct samr_LookupDomain r;
+ struct samr_OpenDomain r2;
+ struct lsa_String n;
+ struct dom_sid *sid;
+ NTSTATUS status;
+
+ r.in.connect_handle = connect_handle;
+ init_lsa_String(&n, domain);
+ r.in.domain_name = &n;
+ r.out.sid = &sid;
+
+ status = dcerpc_samr_LookupDomain_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "LookupDomain failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "LookupDomain failed");
+
+ r2.in.connect_handle = connect_handle;
+ r2.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r2.in.sid = sid;
+ r2.out.domain_handle = domain_handle;
+
+ status = dcerpc_samr_OpenDomain_r(b, tctx, &r2);
+ torture_assert_ntstatus_ok(tctx, status, "OpenDomain failed");
+ torture_assert_ntstatus_ok(tctx, r2.out.result, "OpenDomain failed");
+
+ return true;
+}
+
+static bool test_samr_Connect(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *connect_handle)
+{
+ struct samr_Connect r;
+ NTSTATUS status;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = connect_handle;
+
+ status = dcerpc_samr_Connect_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "SAMR connect failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "SAMR connect failed");
+
+ return true;
+}
+
+static bool test_samr_create_user(struct torture_context *tctx,
+ struct torture_access_context *t,
+ const char *name)
+{
+ struct dcerpc_binding_handle *b = t->pipe->binding_handle;
+ struct policy_handle connect_handle;
+ struct policy_handle domain_handle;
+ struct policy_handle user_handle;
+ bool ok = false;
+
+ torture_comment(tctx, "Connecting to SAMR\n");
+ ZERO_STRUCT(connect_handle);
+ ok = test_samr_Connect(tctx, b, &connect_handle);
+ torture_assert(tctx, ok, "Unable to connect to domain");
+
+ torture_comment(tctx, "Opening domain %s\n", t->user.domain);
+ ZERO_STRUCT(domain_handle);
+ ok = test_samr_openDomain(tctx,
+ b,
+ &connect_handle,
+ t->user.domain,
+ &domain_handle);
+ torture_assert(tctx, ok, "Unable to open to domain");
+
+ torture_comment(tctx, "Creating account %s\n", name);
+ ZERO_STRUCT(user_handle);
+ ok = test_samr_CreateUser(tctx,
+ b,
+ &domain_handle,
+ name,
+ &user_handle);
+
+ /* We don't check ok with torture macros here because the
+ * caller might be looking for failure */
+ test_samr_handle_Close(b, tctx, &domain_handle);
+ test_samr_handle_Close(b, tctx, &connect_handle);
+
+ return ok;
+}
+
+static bool test_samr_userinfo_getinfo(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ bool expected)
+{
+ const char *name;
+ struct dcerpc_pipe *p2 = NULL;
+ struct dcerpc_binding_handle *b;
+ struct policy_handle connect_handle;
+ struct policy_handle domain_handle;
+ struct policy_handle user_handle;
+ NTSTATUS status;
+ uint32_t i = 0;
+ bool ok;
+
+ status = torture_rpc_connection(tctx, &p2, &ndr_table_samr);
+ torture_assert_ntstatus_ok(tctx, status,
+ "Creating secondary connection failed");
+ b = p2->binding_handle;
+
+ torture_comment(tctx, " - 2nd connect\n");
+ /* connect */
+ ZERO_STRUCT(connect_handle);
+ ok = test_samr_Connect(tctx, b, &connect_handle);
+ torture_assert(tctx, ok, "Unable to connect to domain");
+
+ torture_comment(tctx, " - 2nd open domain\n");
+ /* open domain */
+ ZERO_STRUCT(domain_handle);
+ ok = test_samr_openDomain(tctx,
+ b,
+ &connect_handle,
+ torture_setting_string(tctx, "workgroup",
+ lpcfg_workgroup(tctx->lp_ctx)),
+ &domain_handle);
+ torture_assert(tctx, ok, "Unable to open to domain");
+
+ /* create user */
+ name = talloc_asprintf(tctx,
+ "%s%04d",
+ TEST_ACCOUNT_NAME,
+ i);
+
+ torture_comment(tctx, " - 2nd open user\n");
+ ZERO_STRUCT(user_handle);
+ ok = test_samr_OpenUser(tctx,
+ b,
+ &domain_handle,
+ name,
+ &user_handle,
+ expected);
+ torture_assert(tctx, ok, "Unable to open user");
+
+ if (!expected) {
+ torture_comment(tctx, " - 2nd query user\n");
+ ok = test_samr_queryUserInfo(tctx, b, &user_handle);
+ torture_assert(tctx, ok, "Unable to query user");
+
+ test_samr_handle_Close(b, tctx, &user_handle);
+ }
+
+ test_samr_handle_Close(b, tctx, &domain_handle);
+ test_samr_handle_Close(b, tctx, &connect_handle);
+
+ talloc_free(p2);
+
+ return true;
+}
+
+#define NUM_RUNS 20
+static bool torture_rpc_samr_caching(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct test_join *join;
+ const char *password = NULL;
+ const char *name;
+ NTSTATUS status;
+ uint32_t i = 0;
+ bool ok;
+
+ torture_comment(tctx, ">>> Testing User Info Caching\n");
+
+ /* create user */
+ name = talloc_asprintf(tctx,
+ "%s%04d",
+ TEST_ACCOUNT_NAME,
+ i);
+
+ torture_comment(tctx, "- Creating user %s\n", name);
+
+ join = torture_create_testuser(tctx,
+ name,
+ torture_setting_string(tctx, "workgroup",
+ lpcfg_workgroup(tctx->lp_ctx)),
+ ACB_NORMAL,
+ &password);
+ torture_assert(tctx, join, "failed to join domain");
+
+ torture_comment(tctx, "- Query user information\n");
+ for (i = 0; i < NUM_RUNS; i++) {
+ ok = test_samr_userinfo_getinfo(tctx, p, false);
+ torture_assert(tctx, ok, "test_samr_userinfo_getinfo failed");
+ }
+
+ torture_comment(tctx, "- Delete user\n");
+ status = torture_delete_testuser(tctx,
+ join,
+ name);
+ torture_assert_ntstatus_ok(tctx, status, "DeleteUser failed");
+
+ torture_comment(tctx, "- Try to query user information again (should fail)\n");
+ for (i = 0; i < NUM_RUNS; i++) {
+ ok = test_samr_userinfo_getinfo(tctx,
+ p,
+ true);
+ torture_assert(tctx, ok, "test_samr_userinfo_getinfo failed");
+ }
+
+ return true;
+}
+#undef NUM_RUNS
+
+static bool torture_rpc_samr_access_setup_membership(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ uint32_t num_members,
+ uint32_t *members,
+ struct dom_sid *user_sid)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle connect_handle, domain_handle;
+ int i;
+
+ torture_comment(tctx,
+ "Setting up BUILTIN membership for %s\n",
+ dom_sid_string(tctx, user_sid));
+
+ for (i=0; i < num_members; i++) {
+ torture_comment(tctx, "adding user to S-1-5-32-%d\n", members[i]);
+ }
+
+ /* connect */
+ {
+ struct samr_Connect2 r;
+ r.in.system_name = "";
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ ZERO_STRUCT(connect_handle);
+ r.out.connect_handle = &connect_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_Connect2_r(b, tctx, &r),
+ "samr_Connect2 failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_Connect2 failed");
+ }
+
+ /* open domain */
+ {
+ struct samr_OpenDomain r;
+ r.in.connect_handle = &connect_handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32");
+ ZERO_STRUCT(domain_handle);
+ r.out.domain_handle = &domain_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_OpenDomain_r(b, tctx, &r),
+ "samr_OpenDomain failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_OpenDomain failed");
+ }
+
+ for (i = 0; i < num_members; i++) {
+
+ struct policy_handle alias_handle;
+
+ /* open alias */
+ {
+ struct samr_OpenAlias r;
+ r.in.domain_handle = &domain_handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = members[i];
+ ZERO_STRUCT(alias_handle);
+ r.out.alias_handle = &alias_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_OpenAlias_r(b, tctx, &r),
+ "samr_OpenAlias failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_OpenAlias failed");
+ }
+
+ /* add alias member */
+ {
+ struct samr_AddAliasMember r;
+ ZERO_STRUCT(alias_handle);
+ r.in.alias_handle = &alias_handle;
+ r.in.sid = user_sid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_AddAliasMember_r(b, tctx, &r),
+ "samr_AddAliasMember failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_AddAliasMember failed");
+ }
+
+ test_samr_handle_Close(b, tctx, &alias_handle);
+ }
+
+ test_samr_handle_Close(b, tctx, &domain_handle);
+ test_samr_handle_Close(b, tctx, &connect_handle);
+
+ return true;
+}
+
+static bool torture_rpc_samr_access_setup(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct torture_access_context *t)
+{
+ const char *binding = torture_setting_string(tctx, "binding", NULL);
+ struct cli_credentials *test_credentials;
+ struct test_join *join;
+ struct dom_sid *test_sid;
+ struct dcerpc_pipe *samr_pipe;
+
+ t->user.domain = torture_setting_string(tctx, "workgroup",
+ lpcfg_workgroup(tctx->lp_ctx)),
+
+ join = torture_create_testuser(tctx,
+ t->user.username,
+ t->user.domain,
+ ACB_NORMAL,
+ &t->user.password);
+ torture_assert(tctx, join, "failed to join domain");
+ t->join = join;
+
+ test_credentials = cli_credentials_init(tctx);
+
+ cli_credentials_set_workstation(test_credentials,
+ "localhost",
+ CRED_SPECIFIED);
+ cli_credentials_set_domain(test_credentials,
+ torture_setting_string(tctx, "workgroup",
+ lpcfg_workgroup(tctx->lp_ctx)),
+ CRED_SPECIFIED);
+ cli_credentials_set_username(test_credentials,
+ t->user.username,
+ CRED_SPECIFIED);
+ cli_credentials_set_password(test_credentials,
+ t->user.password,
+ CRED_SPECIFIED);
+ test_sid = discard_const_p(struct dom_sid,
+ torture_join_user_sid(t->join));
+
+ if (t->user.num_builtin_memberships) {
+ torture_assert(tctx,
+ torture_rpc_samr_access_setup_membership(tctx,
+ p,
+ t->user.num_builtin_memberships,
+ t->user.builtin_memberships,
+ test_sid),
+ "failed to setup membership");
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect(tctx,
+ &samr_pipe,
+ binding,
+ &ndr_table_samr,
+ test_credentials,
+ tctx->ev,
+ tctx->lp_ctx),
+ "Error connecting to server");
+
+ t->pipe = samr_pipe;
+
+ return true;
+}
+
+static bool torture_rpc_samr_access(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_access_context *t;
+ const char *testuser;
+ bool ok;
+
+ torture_comment(tctx, "Testing non-privileged user access\n");
+
+ t = talloc_zero(tctx, struct torture_access_context);
+ torture_assert(tctx, t, "talloc failed");
+
+ t->user.username = talloc_asprintf(t, "%s%04d", TEST_ACCOUNT_NAME, 100);
+
+ torture_comment(tctx, "*** Setting up non-privleged user\n"
+ "***\n");
+
+ ok = torture_rpc_samr_access_setup(tctx, p, t);
+ torture_assert(tctx, ok, "torture_rpc_samr_access_setup failed");
+
+ testuser = talloc_asprintf(t, "%s%04d", TEST_ACCOUNT_NAME, 200);
+
+ torture_comment(tctx, "*** Try to create user (%s) as non-privileged "
+ "user - should fail\n"
+ "***\n", testuser);
+
+ ok = test_samr_create_user(tctx, t, testuser);
+
+ torture_assert(tctx, ok == false, "*** Creating user was successful but it should fail");
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_samr_priv(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite =
+ torture_suite_create(mem_ctx, "samr.priv");
+ struct torture_rpc_tcase *tcase;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite,
+ "samr",
+ &ndr_table_samr);
+
+ torture_rpc_tcase_add_test(tcase,
+ "caching",
+ torture_rpc_samr_caching);
+
+ torture_rpc_tcase_add_test(tcase,
+ "access",
+ torture_rpc_samr_access);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/samsync.c b/source4/torture/rpc/samsync.c
new file mode 100644
index 0000000..a8541d3
--- /dev/null
+++ b/source4/torture/rpc/samsync.c
@@ -0,0 +1,1798 @@
+/*
+ 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 "../lib/util/dlinklist.h"
+#include "../lib/crypto/crypto.h"
+#include "system/time.h"
+#include "torture/rpc/torture_rpc.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/auth/libcli_auth.h"
+#include "libcli/samsync/samsync.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"
+#include "lib/crypto/gnutls_helpers.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 torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct netlogon_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;
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ int rc;
+
+ ninfo.identity_info.domain_name.string = domain;
+ ninfo.identity_info.parameter_control = 0;
+ ninfo.identity_info.logon_id = 0;
+ ninfo.identity_info.account_name.string = account_name;
+ ninfo.identity_info.workstation.string = workstation;
+ generate_random_buffer(ninfo.challenge,
+ sizeof(ninfo.challenge));
+ if (nt_hash) {
+ ninfo.nt.length = 24;
+ ninfo.nt.data = talloc_array(mem_ctx, uint8_t, 24);
+ rc = SMBOWFencrypt(nt_hash->hash, ninfo.challenge,
+ ninfo.nt.data);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ } 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);
+ rc = SMBOWFencrypt(lm_hash->hash, ninfo.challenge,
+ ninfo.lm.data);
+ if (rc != 0) {
+ return gnutls_error_to_ntstatus(rc, NT_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER);
+ }
+ } else {
+ ninfo.lm.length = 0;
+ ninfo.lm.data = NULL;
+ }
+
+ logon.network = &ninfo;
+
+ 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 = NetlogonNetworkInformation;
+ r.in.logon = &logon;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+
+ ZERO_STRUCT(auth2);
+ netlogon_creds_client_authenticator(creds, &auth);
+
+ r.in.validation_level = 3;
+
+ status = dcerpc_netr_LogonSamLogon_r(b, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ if (info3) {
+ *info3 = validation.sam3;
+ }
+
+ return r.out.result;
+}
+
+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 netlogon_creds_CredentialState *creds;
+ struct netlogon_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_binding_handle *b;
+ struct dcerpc_pipe *p_netlogon_wksta;
+ struct dcerpc_pipe *p_samr;
+ struct dcerpc_binding_handle *b_samr;
+ struct dcerpc_pipe *p_lsa;
+ struct dcerpc_binding_handle *b_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(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct samsync_state *samsync_state,
+ const char *domain,
+ struct dom_sid **sid_p)
+{
+ struct lsa_String name;
+ struct samr_OpenDomain o;
+ struct samr_LookupDomain l;
+ struct dom_sid2 *sid = NULL;
+ 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;
+ l.out.sid = &sid;
+
+ nt_status = dcerpc_samr_LookupDomain_r(samsync_state->b_samr, mem_ctx, &l);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(nt_status));
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(l.out.result)) {
+ torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(l.out.result));
+ 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_p) {
+ *sid_p = *l.out.sid;
+ }
+
+ nt_status = dcerpc_samr_OpenDomain_r(samsync_state->b_samr, mem_ctx, &o);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(nt_status));
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(o.out.result)) {
+ torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(o.out.result));
+ return NULL;
+ }
+
+ return domain_handle;
+}
+
+static struct sec_desc_buf *samsync_query_samr_sec_desc(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct samsync_state *samsync_state,
+ struct policy_handle *handle)
+{
+ struct samr_QuerySecurity r;
+ struct sec_desc_buf *sdbuf = NULL;
+ NTSTATUS status;
+
+ r.in.handle = handle;
+ r.in.sec_info = 0x7;
+ r.out.sdbuf = &sdbuf;
+
+ status = dcerpc_samr_QuerySecurity_r(samsync_state->b_samr, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "SAMR QuerySecurity failed - %s\n", nt_errstr(status));
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "SAMR QuerySecurity failed - %s\n", nt_errstr(r.out.result));
+ return NULL;
+ }
+
+ return sdbuf;
+}
+
+static struct sec_desc_buf *samsync_query_lsa_sec_desc(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx,
+ struct samsync_state *samsync_state,
+ struct policy_handle *handle)
+{
+ struct lsa_QuerySecurity r;
+ struct sec_desc_buf *sdbuf = NULL;
+ NTSTATUS status;
+
+ r.in.handle = handle;
+ r.in.sec_info = 0x7;
+ r.out.sdbuf = &sdbuf;
+
+ status = dcerpc_lsa_QuerySecurity_r(samsync_state->b_lsa, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "LSA QuerySecurity failed - %s\n", nt_errstr(status));
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "LSA QuerySecurity failed - %s\n", nt_errstr(r.out.result));
+ return NULL;
+ }
+
+ return sdbuf;
+}
+
+#define TEST_UINT64_EQUAL(i1, i2) do {\
+ if (i1 != i2) {\
+ torture_comment(tctx, "%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) {\
+ torture_comment(tctx, "%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) {\
+ torture_comment(tctx, "%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) {\
+ torture_comment(tctx, "%s: string mismatch: " #s1 ":%s != " #s2 ": %s\n", \
+ __location__, s1.string, s2.string);\
+ ret = false;\
+ } \
+} while (0)
+
+#define TEST_BINARY_STRING_EQUAL(s1, s2) do {\
+ if (!((!s1.array || s1.array[0]=='\0') && (!s2.array || s2.array[0]=='\0')) \
+ && memcmp(s1.array, s2.array, s1.length * 2) != 0) {\
+ torture_comment(tctx, "%s: string mismatch: " #s1 ":%s != " #s2 ": %s\n", \
+ __location__, (const char *)s1.array, (const char *)s2.array);\
+ ret = false;\
+ } \
+} while (0)
+
+#define TEST_SID_EQUAL(s1, s2) do {\
+ if (!dom_sid_equal(s1, s2)) {\
+ torture_comment(tctx, "%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(tctx, mem_ctx, samsync_state, \
+ handle); \
+ if (!sdbuf || !sdbuf->sd) { \
+ torture_comment(tctx, "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)) {\
+ torture_comment(tctx, "Security Descriptor Mismatch for %s:\n", #sd1);\
+ NDR_PRINT_DEBUG(security_descriptor, sd1.sd);\
+ NDR_PRINT_DEBUG(security_descriptor, sdbuf->sd);\
+ ret = false;\
+ }\
+ }\
+} while (0)
+
+static bool samsync_handle_domain(struct torture_context *tctx, 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 */
+ union samr_DomainInfo *info[14];
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13};
+ 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) {
+ torture_comment(tctx, "BUILTIN domain has different name: %s\n", domain->domain_name.string);
+ }
+ break;
+ case SAM_DATABASE_PRIVS:
+ torture_comment(tctx, "DOMAIN entry on privs DB!\n");
+ return false;
+ break;
+ }
+
+ if (!samsync_state->domain_name[database_id]) {
+ samsync_state->domain_name[database_id] =
+ talloc_strdup(samsync_state, domain->domain_name.string);
+ } else {
+ if (strcasecmp_m(samsync_state->domain_name[database_id], domain->domain_name.string) != 0) {
+ torture_comment(tctx, "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] =
+ samsync_open_domain(tctx,
+ samsync_state,
+ samsync_state,
+ samsync_state->domain_name[database_id],
+ &dom_sid);
+ }
+ if (samsync_state->domain_handle[database_id]) {
+ samsync_state->sid[database_id] = dom_sid_dup(samsync_state, dom_sid);
+ }
+
+ torture_comment(tctx, "\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];
+ q[levels[i]].out.info = &info[levels[i]];
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_QueryDomainInfo_r(samsync_state->b_samr, mem_ctx, &q[levels[i]]),
+ talloc_asprintf(tctx, "QueryDomainInfo level %u failed", q[levels[i]].in.level));
+ torture_assert_ntstatus_ok(tctx, q[levels[i]].out.result,
+ talloc_asprintf(tctx, "QueryDomainInfo level %u failed", q[levels[i]].in.level));
+ }
+
+ TEST_STRING_EQUAL(info[5]->info5.domain_name, domain->domain_name);
+
+ TEST_STRING_EQUAL(info[2]->general.oem_information, domain->oem_information);
+ TEST_STRING_EQUAL(info[4]->oem.oem_information, domain->oem_information);
+ TEST_TIME_EQUAL(info[2]->general.force_logoff_time, domain->force_logoff_time);
+ TEST_TIME_EQUAL(info[3]->info3.force_logoff_time, domain->force_logoff_time);
+
+ TEST_TIME_EQUAL(info[1]->info1.min_password_length, domain->min_password_length);
+ TEST_TIME_EQUAL(info[1]->info1.password_history_length, domain->password_history_length);
+ TEST_TIME_EQUAL(info[1]->info1.max_password_age, domain->max_password_age);
+ TEST_TIME_EQUAL(info[1]->info1.min_password_age, domain->min_password_age);
+
+ TEST_UINT64_EQUAL(info[8]->info8.sequence_num,
+ domain->sequence_num);
+ TEST_TIME_EQUAL(info[8]->info8.domain_create_time,
+ domain->domain_create_time);
+ TEST_TIME_EQUAL(info[13]->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(struct torture_context *tctx,
+ 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;
+
+ switch (database_id) {
+ case SAM_DATABASE_DOMAIN:
+ case SAM_DATABASE_BUILTIN:
+ break;
+ case SAM_DATABASE_PRIVS:
+ torture_comment(tctx, "DOMAIN entry on privs DB!\n");
+ return false;
+ }
+
+ 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_strdup(samsync_state, policy->primary_domain_name.string);
+ } else {
+ if (strcasecmp_m(samsync_state->domain_name[SAM_DATABASE_DOMAIN], policy->primary_domain_name.string) != 0) {
+ torture_comment(tctx, "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)) {
+ torture_comment(tctx, "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;
+ }
+
+ torture_comment(tctx, "\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 = NULL;
+ 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;
+ const char *username = user->account_name.string;
+ NTSTATUS nt_status;
+ bool ret = true;
+ struct samr_OpenUser r;
+ struct samr_QueryUserInfo q;
+ union samr_UserInfo *info;
+ struct policy_handle user_handle;
+ struct samr_GetGroupsForUser getgr;
+ struct samr_RidWithAttributeArray *rids;
+
+ switch (database_id) {
+ case SAM_DATABASE_DOMAIN:
+ case SAM_DATABASE_BUILTIN:
+ break;
+ case SAM_DATABASE_PRIVS:
+ torture_comment(tctx, "DOMAIN entry on privs DB!\n");
+ return false;
+ }
+
+ domain = samsync_state->domain_name[database_id];
+
+ if (domain == NULL ||
+ samsync_state->domain_handle[database_id] == NULL) {
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_OpenUser_r(samsync_state->b_samr, mem_ctx, &r),
+ talloc_asprintf(tctx, "OpenUser(%u) failed", rid));
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "OpenUser(%u) failed", rid));
+
+ q.in.user_handle = &user_handle;
+ q.in.level = 21;
+ q.out.info = &info;
+
+ TEST_SEC_DESC_EQUAL(user->sdbuf, samr, &user_handle);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_QueryUserInfo_r(samsync_state->b_samr, mem_ctx, &q),
+ talloc_asprintf(tctx, "OpenUserInfo level %u failed", q.in.level));
+ torture_assert_ntstatus_ok(tctx, q.out.result,
+ talloc_asprintf(tctx, "OpenUserInfo level %u failed", q.in.level));
+
+ getgr.in.user_handle = &user_handle;
+ getgr.out.rids = &rids;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_GetGroupsForUser_r(samsync_state->b_samr, mem_ctx, &getgr),
+ "GetGroupsForUser failed");
+ torture_assert_ntstatus_ok(tctx, getgr.out.result,
+ "GetGroupsForUser failed");
+
+ if (!test_samr_handle_Close(samsync_state->b_samr, tctx, &user_handle)) {
+ torture_comment(tctx, "samr_handle_Close failed\n");
+ ret = false;
+ }
+ if (!ret) {
+ return false;
+ }
+
+ TEST_STRING_EQUAL(info->info21.account_name, user->account_name);
+ TEST_STRING_EQUAL(info->info21.full_name, user->full_name);
+ TEST_INT_EQUAL(info->info21.rid, user->rid);
+ TEST_INT_EQUAL(info->info21.primary_gid, user->primary_gid);
+ TEST_STRING_EQUAL(info->info21.home_directory, user->home_directory);
+ TEST_STRING_EQUAL(info->info21.home_drive, user->home_drive);
+ TEST_STRING_EQUAL(info->info21.logon_script, user->logon_script);
+ TEST_STRING_EQUAL(info->info21.description, user->description);
+ TEST_STRING_EQUAL(info->info21.workstations, user->workstations);
+
+ TEST_TIME_EQUAL(info->info21.last_logon, user->last_logon);
+ TEST_TIME_EQUAL(info->info21.last_logoff, user->last_logoff);
+
+
+ TEST_INT_EQUAL(info->info21.logon_hours.units_per_week,
+ user->logon_hours.units_per_week);
+ if (ret) {
+ if (memcmp(info->info21.logon_hours.bits, user->logon_hours.bits,
+ info->info21.logon_hours.units_per_week/8) != 0) {
+ torture_comment(tctx, "Logon hours mismatch\n");
+ ret = false;
+ }
+ }
+
+ TEST_INT_EQUAL(info->info21.bad_password_count,
+ user->bad_password_count);
+ TEST_INT_EQUAL(info->info21.logon_count,
+ user->logon_count);
+
+ TEST_TIME_EQUAL(info->info21.last_password_change,
+ user->last_password_change);
+ TEST_TIME_EQUAL(info->info21.acct_expiry,
+ user->acct_expiry);
+
+ TEST_INT_EQUAL((info->info21.acct_flags & ~ACB_PW_EXPIRED), user->acct_flags);
+ if (user->acct_flags & ACB_PWNOEXP) {
+ if (info->info21.acct_flags & ACB_PW_EXPIRED) {
+ torture_comment(tctx, "ACB flags mismatch: both expired and no expiry!\n");
+ ret = false;
+ }
+ if (info->info21.force_password_change != (NTTIME)0x7FFFFFFFFFFFFFFFULL) {
+ torture_comment(tctx, "ACB flags mismatch: no password expiry, but force password change 0x%016llx (%lld) != 0x%016llx (%lld)\n",
+ (unsigned long long)info->info21.force_password_change,
+ (unsigned long long)info->info21.force_password_change,
+ (unsigned long long)0x7FFFFFFFFFFFFFFFULL, (unsigned long long)0x7FFFFFFFFFFFFFFFULL
+ );
+ ret = false;
+ }
+ }
+
+ TEST_INT_EQUAL(info->info21.nt_password_set, user->nt_password_present);
+ TEST_INT_EQUAL(info->info21.lm_password_set, user->lm_password_present);
+ TEST_INT_EQUAL(info->info21.password_expired, user->password_expired);
+
+ TEST_STRING_EQUAL(info->info21.comment, user->comment);
+ TEST_BINARY_STRING_EQUAL(info->info21.parameters, user->parameters);
+
+ TEST_INT_EQUAL(info->info21.country_code, user->country_code);
+ TEST_INT_EQUAL(info->info21.code_page, user->code_page);
+
+ TEST_STRING_EQUAL(info->info21.profile_path, user->profile_path);
+
+ if (user->lm_password_present) {
+ lm_hash_p = &lm_hash;
+ }
+ if (user->nt_password_present) {
+ 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;
+ ndr_err = ndr_pull_struct_blob(&data, mem_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) {
+ lm_hash_p = &lm_hash;
+ }
+ if (keys.keys.keys2.ntpassword.length == 16) {
+ nt_hash_p = &nt_hash;
+ }
+ } else {
+ torture_comment(tctx, "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_upper(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_upper(mem_ctx, &lm_hash_blob)));
+ }
+
+ nt_status = test_SamLogon(tctx,
+ 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 (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.logon_time);
+ TEST_TIME_EQUAL(user->acct_expiry, info3->base.kickoff_time);
+ TEST_TIME_EQUAL(user->last_password_change, info3->base.last_password_change);
+ TEST_TIME_EQUAL(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.logoff_time == 0x7fffffffffffffffLL))) {
+ TEST_TIME_EQUAL(user->last_logoff, info3->base.logoff_time);
+ }
+
+ TEST_INT_EQUAL(rids->count, info3->base.groups.count);
+ if (rids->count == info3->base.groups.count) {
+ int i, j;
+ int count = rids->count;
+ bool *matched = talloc_zero_array(mem_ctx, bool, rids->count);
+
+ for (i = 0; i < count; i++) {
+ for (j = 0; j < count; j++) {
+ if ((rids->rids[i].rid ==
+ info3->base.groups.rids[j].rid)
+ && (rids->rids[i].attributes ==
+ info3->base.groups.rids[j].attributes)) {
+ matched[i] = true;
+ }
+ }
+ }
+
+ for (i = 0; i < rids->count; i++) {
+ if (matched[i] == false) {
+ ret = false;
+ torture_comment(tctx, "Could not find group RID %u found in getgroups in NETLOGON reply\n",
+ rids->rids[i].rid);
+ }
+ }
+ }
+ return ret;
+ } else {
+ torture_comment(tctx, "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(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_ALIAS *alias = delta->delta_union.alias;
+ bool ret = true;
+
+ struct samr_OpenAlias r;
+ struct samr_QueryAliasInfo q;
+ union samr_AliasInfo *info;
+ struct policy_handle alias_handle;
+
+ switch (database_id) {
+ case SAM_DATABASE_DOMAIN:
+ case SAM_DATABASE_BUILTIN:
+ break;
+ case SAM_DATABASE_PRIVS:
+ torture_comment(tctx, "DOMAIN entry on privs DB!\n");
+ return false;
+ }
+
+ if (samsync_state->domain_name[database_id] == NULL ||
+ samsync_state->domain_handle[database_id] == NULL) {
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_OpenAlias_r(samsync_state->b_samr, mem_ctx, &r),
+ talloc_asprintf(tctx, "OpenUser(%u) failed", rid));
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "OpenUser(%u) failed", rid));
+
+ q.in.alias_handle = &alias_handle;
+ q.in.level = 1;
+ q.out.info = &info;
+
+ TEST_SEC_DESC_EQUAL(alias->sdbuf, samr, &alias_handle);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_QueryAliasInfo_r(samsync_state->b_samr, mem_ctx, &q),
+ "QueryAliasInfo failed");
+ if (!test_samr_handle_Close(samsync_state->b_samr, tctx, &alias_handle)) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_comment(tctx, "QueryAliasInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(q.out.result));
+ return false;
+ }
+
+ TEST_STRING_EQUAL(info->all.name, alias->alias_name);
+ TEST_STRING_EQUAL(info->all.description, alias->description);
+ return ret;
+}
+
+static bool samsync_handle_group(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_GROUP *group = delta->delta_union.group;
+ bool ret = true;
+
+ struct samr_OpenGroup r;
+ struct samr_QueryGroupInfo q;
+ union samr_GroupInfo *info;
+ struct policy_handle group_handle;
+
+ switch (database_id) {
+ case SAM_DATABASE_DOMAIN:
+ case SAM_DATABASE_BUILTIN:
+ break;
+ case SAM_DATABASE_PRIVS:
+ torture_comment(tctx, "DOMAIN entry on privs DB!\n");
+ return false;
+ }
+
+ if (samsync_state->domain_name[database_id] == NULL ||
+ samsync_state->domain_handle[database_id] == NULL) {
+ torture_comment(tctx, "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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_OpenGroup_r(samsync_state->b_samr, mem_ctx, &r),
+ talloc_asprintf(tctx, "OpenUser(%u) failed", rid));
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "OpenUser(%u) failed", rid));
+
+ q.in.group_handle = &group_handle;
+ q.in.level = 1;
+ q.out.info = &info;
+
+ TEST_SEC_DESC_EQUAL(group->sdbuf, samr, &group_handle);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_QueryGroupInfo_r(samsync_state->b_samr, mem_ctx, &q),
+ "QueryGroupInfo failed");
+ if (!test_samr_handle_Close(samsync_state->b_samr, tctx, &group_handle)) {
+ return false;
+ }
+
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_comment(tctx, "QueryGroupInfo level %u failed - %s\n",
+ q.in.level, nt_errstr(q.out.result));
+ return false;
+ }
+
+ TEST_STRING_EQUAL(info->all.name, group->group_name);
+ TEST_INT_EQUAL(info->all.attributes, group->attributes);
+ TEST_STRING_EQUAL(info->all.description, group->description);
+ return ret;
+}
+
+static bool samsync_handle_secret(struct torture_context *tctx,
+ 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 *nsec = 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 nsec_mtime;
+ NTTIME old_mtime;
+ bool ret = true;
+ DATA_BLOB lsa_blob1, lsa_blob_out, session_key;
+ NTSTATUS status;
+
+ nsec->name = talloc_strdup(nsec, name);
+ nsec->secret = data_blob_talloc(nsec, secret->current_cipher.cipher_data, secret->current_cipher.maxlen);
+ nsec->mtime = secret->current_cipher_set_time;
+
+ DLIST_ADD(samsync_state->secrets, nsec);
+
+ old->name = talloc_strdup(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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_OpenSecret_r(samsync_state->b_lsa, mem_ctx, &o),
+ "OpenSecret failed");
+ torture_assert_ntstatus_ok(tctx, o.out.result,
+ "OpenSecret failed");
+
+/*
+ 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)) {
+ torture_comment(tctx, "dcerpc_fetch_session_key failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+
+ ZERO_STRUCT(nsec_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 = &nsec_mtime;
+ q.in.old_val = &bufp2;
+ q.in.old_mtime = &old_mtime;
+
+ bufp1.buf = NULL;
+ bufp2.buf = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_QuerySecret_r(samsync_state->b_lsa, mem_ctx, &q),
+ "QuerySecret failed");
+ if (NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, q.out.result)) {
+ /* some things are just off limits */
+ return true;
+ } else if (!NT_STATUS_IS_OK(q.out.result)) {
+ torture_comment(tctx, "QuerySecret failed - %s\n", nt_errstr(q.out.result));
+ 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)) {
+ torture_comment(tctx, "Failed to decrypt secrets OLD blob: %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if (!q.out.old_mtime) {
+ torture_comment(tctx, "OLD mtime not available on LSA for secret %s\n", old->name);
+ ret = false;
+ }
+ if (old->mtime != *q.out.old_mtime) {
+ torture_comment(tctx, "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) {
+ torture_comment(tctx, "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) {
+ torture_comment(tctx, "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)) {
+ torture_comment(tctx, "Failed to decrypt secrets OLD blob\n");
+ return false;
+ }
+
+ if (!q.out.new_mtime) {
+ torture_comment(tctx, "NEW mtime not available on LSA for secret %s\n", nsec->name);
+ ret = false;
+ }
+ if (nsec->mtime != *q.out.new_mtime) {
+ torture_comment(tctx, "NEW mtime on secret %s does not match between SAMSYNC (%s) and LSA (%s)\n",
+ nsec->name, nt_time_string(mem_ctx, nsec->mtime),
+ nt_time_string(mem_ctx, *q.out.new_mtime));
+ ret = false;
+ }
+
+ if (nsec->secret.length != lsa_blob_out.length) {
+ torture_comment(tctx, "Returned secret %s doesn't match: %d != %d\n",
+ nsec->name, (int)nsec->secret.length, (int)lsa_blob_out.length);
+ ret = false;
+ } else if (memcmp(lsa_blob_out.data,
+ nsec->secret.data, nsec->secret.length) != 0) {
+ torture_comment(tctx, "Returned secret %s doesn't match: \n",
+ nsec->name);
+ DEBUG(1, ("SamSync Secret:\n"));
+ dump_data(1, nsec->secret.data, nsec->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(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ 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 *ndom = 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];
+ union lsa_TrustedDomainInfo *_info = NULL;
+ int levels [] = {1, 3, 8};
+ int i;
+
+ ndom->name = talloc_strdup(ndom, trusted_domain->domain_name.string);
+ ndom->sid = dom_sid_dup(ndom, 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_OpenTrustedDomain_r(samsync_state->b_lsa, mem_ctx, &t),
+ "OpenTrustedDomain failed");
+ torture_assert_ntstatus_ok(tctx, t.out.result,
+ "OpenTrustedDomain failed");
+
+ for (i=0; i< ARRAY_SIZE(levels); i++) {
+ q.in.trustdom_handle = &trustdom_handle;
+ q.in.level = levels[i];
+ q.out.info = &_info;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_QueryTrustedDomainInfo_r(samsync_state->b_lsa, mem_ctx, &q),
+ "QueryTrustedDomainInfo failed");
+ if (!NT_STATUS_IS_OK(q.out.result)) {
+ if (q.in.level == 8 && NT_STATUS_EQUAL(q.out.result, NT_STATUS_INVALID_PARAMETER)) {
+ info[levels[i]] = NULL;
+ continue;
+ }
+ torture_comment(tctx, "QueryInfoTrustedDomain level %d failed - %s\n",
+ levels[i], nt_errstr(q.out.result));
+ return false;
+ }
+ info[levels[i]] = _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);
+*/
+ DLIST_ADD(samsync_state->trusted_domains, ndom);
+
+ return ret;
+}
+
+static bool samsync_handle_account(struct torture_context *tctx,
+ TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state,
+ int database_id, struct netr_DELTA_ENUM *delta)
+{
+ 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_PrivilegeSet *privs = NULL;
+ 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;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_OpenAccount_r(samsync_state->b_lsa, mem_ctx, &a),
+ "OpenAccount failed");
+ torture_assert_ntstatus_ok(tctx, a.out.result,
+ "OpenAccount failed");
+
+ 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;
+ e.out.privs = &privs;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_EnumPrivsAccount_r(samsync_state->b_lsa, mem_ctx, &e),
+ "EnumPrivsAccount failed");
+ torture_assert_ntstatus_ok(tctx, e.out.result,
+ "EnumPrivsAccount failed");
+
+ if ((account->privilege_entries && !privs)) {
+ torture_comment(tctx, "Account %s has privileges in SamSync, but not LSA\n",
+ dom_sid_string(mem_ctx, dom_sid));
+ return false;
+ }
+
+ if (!account->privilege_entries && privs && privs->count) {
+ torture_comment(tctx, "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, privs->count);
+
+ for (i=0;i< privs->count; i++) {
+
+ struct lsa_StringLarge *name = NULL;
+
+ r.in.handle = samsync_state->lsa_handle;
+ r.in.luid = &privs->set[i].luid;
+ r.out.name = &name;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_LookupPrivName_r(samsync_state->b_lsa, mem_ctx, &r),
+ "\nLookupPrivName failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "\nLookupPrivName failed");
+
+ if (!r.out.name) {
+ torture_comment(tctx, "\nLookupPrivName failed to return a name\n");
+ return false;
+ }
+ for (j=0;j<account->privilege_entries; j++) {
+ if (strcmp(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]) {
+ torture_comment(tctx, "Privilege %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)
+{
+ 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;
+ struct netr_Authenticator return_authenticator, credential;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+
+ const char *domain, *username;
+
+ ZERO_STRUCT(return_authenticator);
+
+ 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;
+ r.in.return_authenticator = &return_authenticator;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.delta_enum_array = &delta_enum_array;
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint32_t sync_context = 0;
+
+ r.in.database_id = database_ids[i];
+ r.in.sync_context = &sync_context;
+ r.out.sync_context = &sync_context;
+
+ torture_comment(tctx, "Testing DatabaseSync of id %d\n", r.in.database_id);
+
+ do {
+ loop_ctx = talloc_named(mem_ctx, 0, "DatabaseSync loop context");
+ netlogon_creds_client_authenticator(samsync_state->creds, &credential);
+
+ r.in.credential = &credential;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_DatabaseSync_r(samsync_state->b, loop_ctx, &r),
+ "DatabaseSync failed");
+ if (!NT_STATUS_IS_OK(r.out.result) &&
+ !NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)) {
+ torture_comment(tctx, "DatabaseSync - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ break;
+ }
+
+ if (!netlogon_creds_client_check(samsync_state->creds, &r.out.return_authenticator->cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ r.in.sync_context = r.out.sync_context;
+
+ for (d=0; d < delta_enum_array->num_deltas; d++) {
+ delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context");
+
+ if (!NT_STATUS_IS_OK(samsync_fix_delta(delta_ctx, samsync_state->creds,
+ r.in.database_id,
+ &delta_enum_array->delta_enum[d]))) {
+ torture_comment(tctx, "Failed to decrypt delta\n");
+ ret = false;
+ }
+
+ switch (delta_enum_array->delta_enum[d].delta_type) {
+ case NETR_DELTA_DOMAIN:
+ if (!samsync_handle_domain(tctx, delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ torture_comment(tctx, "Failed to handle DELTA_DOMAIN\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_GROUP:
+ if (!samsync_handle_group(tctx, delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ torture_comment(tctx, "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, &delta_enum_array->delta_enum[d])) {
+ torture_comment(tctx, "Failed to handle DELTA_USER\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_ALIAS:
+ if (!samsync_handle_alias(tctx, delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ torture_comment(tctx, "Failed to handle DELTA_ALIAS\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_POLICY:
+ if (!samsync_handle_policy(tctx, delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ torture_comment(tctx, "Failed to handle DELTA_POLICY\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_TRUSTED_DOMAIN:
+ if (!samsync_handle_trusted_domain(tctx, delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ torture_comment(tctx, "Failed to handle DELTA_TRUSTED_DOMAIN\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_ACCOUNT:
+ if (!samsync_handle_account(tctx, delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ torture_comment(tctx, "Failed to handle DELTA_ACCOUNT\n");
+ ret = false;
+ }
+ break;
+ case NETR_DELTA_SECRET:
+ if (!samsync_handle_secret(tctx, delta_ctx, samsync_state,
+ r.in.database_id, &delta_enum_array->delta_enum[d])) {
+ torture_comment(tctx, "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:
+ torture_comment(tctx, "Uxpected delta type %d\n", delta_enum_array->delta_enum[d].delta_type);
+ ret = false;
+ break;
+ }
+ talloc_free(delta_ctx);
+ }
+ talloc_free(loop_ctx);
+ } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES));
+
+ }
+
+ domain = samsync_state->domain_name[SAM_DATABASE_DOMAIN];
+ if (!domain) {
+ torture_comment(tctx, "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);
+
+ torture_comment(tctx, "Checking password for %s\\%s\n", t->name, username);
+ nt_status = test_SamLogon(tctx,
+ 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)) {
+ torture_comment(tctx, "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)) {
+ torture_comment(tctx, "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(tctx,
+ 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)) {
+ torture_comment(tctx, "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 torture_context *tctx,
+ struct samsync_state *samsync_state, TALLOC_CTX *mem_ctx)
+{
+ TALLOC_CTX *loop_ctx;
+ struct netr_DatabaseDeltas r;
+ struct netr_Authenticator credential;
+ struct netr_Authenticator return_authenticator;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ const uint32_t database_ids[] = {0, 1, 2};
+ int i;
+ bool ret = true;
+
+ ZERO_STRUCT(return_authenticator);
+
+ r.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(samsync_state->p));
+ r.in.computername = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.preferredmaximumlength = (uint32_t)-1;
+ r.in.return_authenticator = &return_authenticator;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.delta_enum_array = &delta_enum_array;
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint64_t seq_num = samsync_state->seq_num[i];
+
+ r.in.database_id = database_ids[i];
+ r.in.sequence_num = &seq_num;
+ r.out.sequence_num = &seq_num;
+
+ if (seq_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
+ */
+ seq_num -= 10;
+
+ torture_comment(tctx, "Testing DatabaseDeltas of id %d at %llu\n",
+ r.in.database_id, (long long)seq_num);
+
+ do {
+ loop_ctx = talloc_named(mem_ctx, 0, "test_DatabaseDeltas loop context");
+ netlogon_creds_client_authenticator(samsync_state->creds, &credential);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_DatabaseDeltas_r(samsync_state->b, loop_ctx, &r),
+ "DatabaseDeltas failed");
+ if (!NT_STATUS_IS_OK(r.out.result) &&
+ !NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES) &&
+ !NT_STATUS_EQUAL(r.out.result, NT_STATUS_SYNCHRONIZATION_REQUIRED)) {
+ torture_comment(tctx, "DatabaseDeltas - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (!netlogon_creds_client_check(samsync_state->creds, &return_authenticator.cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ seq_num++;
+ talloc_free(loop_ctx);
+ } while (NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES));
+ }
+
+ return ret;
+}
+
+
+/*
+ try a netlogon DatabaseSync2
+*/
+static bool test_DatabaseSync2(struct torture_context *tctx,
+ struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct netlogon_creds_CredentialState *creds)
+{
+ TALLOC_CTX *loop_ctx;
+ struct netr_DatabaseSync2 r;
+ const uint32_t database_ids[] = {0, 1, 2};
+ int i;
+ bool ret = true;
+ struct netr_Authenticator return_authenticator, credential;
+ struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(return_authenticator);
+
+ 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;
+ r.in.return_authenticator = &return_authenticator;
+ r.out.return_authenticator = &return_authenticator;
+ r.out.delta_enum_array = &delta_enum_array;
+
+ for (i=0;i<ARRAY_SIZE(database_ids);i++) {
+
+ uint32_t sync_context = 0;
+
+ r.in.database_id = database_ids[i];
+ r.in.sync_context = &sync_context;
+ r.out.sync_context = &sync_context;
+ r.in.restart_state = 0;
+
+ torture_comment(tctx, "Testing DatabaseSync2 of id %d\n", r.in.database_id);
+
+ do {
+ loop_ctx = talloc_named(mem_ctx, 0, "test_DatabaseSync2 loop context");
+ netlogon_creds_client_authenticator(creds, &credential);
+
+ r.in.credential = &credential;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_DatabaseSync2_r(b, loop_ctx, &r),
+ "DatabaseSync2 failed");
+ if (!NT_STATUS_IS_OK(r.out.result) &&
+ !NT_STATUS_EQUAL(r.out.result, STATUS_MORE_ENTRIES)) {
+ torture_comment(tctx, "DatabaseSync2 - %s\n", nt_errstr(r.out.result));
+ ret = false;
+ }
+
+ if (!netlogon_creds_client_check(creds, &r.out.return_authenticator->cred)) {
+ torture_comment(tctx, "Credential chaining failed\n");
+ }
+
+ talloc_free(loop_ctx);
+ } while (NT_STATUS_EQUAL(r.out.result, 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,
+ lpcfg_workgroup(torture->lp_ctx), ACB_SVRTRUST,
+ &machine_password);
+ if (!join_ctx) {
+ talloc_free(mem_ctx);
+ torture_comment(torture, "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, lpcfg_workgroup(torture->lp_ctx), ACB_WSTRUST, &wksta_machine_password);
+ if (!join_ctx2) {
+ talloc_free(mem_ctx);
+ torture_comment(torture, "Failed to join as member\n");
+ return false;
+ }
+
+ user_ctx = torture_create_testuser(torture, TEST_USER_NAME,
+ lpcfg_workgroup(torture->lp_ctx),
+ ACB_NORMAL, NULL);
+ if (!user_ctx) {
+ talloc_free(mem_ctx);
+ torture_comment(torture, "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->b_samr = samsync_state->p_samr->binding_handle;
+ 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;
+
+ torture_assert_ntstatus_ok_goto(torture,
+ dcerpc_samr_Connect_r(samsync_state->b_samr, mem_ctx, &c),
+ ret, failed,
+ "samr_Connect failed");
+ torture_assert_ntstatus_ok_goto(torture, c.out.result,
+ ret, failed,
+ "samr_Connect failed");
+
+ domain_policy = samsync_open_domain(torture, mem_ctx, samsync_state, lpcfg_workgroup(torture->lp_ctx), NULL);
+ if (!domain_policy) {
+ torture_comment(torture, "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)));
+ torture_assert_ntstatus_ok_goto(torture,
+ dcerpc_samr_SetDomainInfo_r(samsync_state->b_samr, mem_ctx, &s),
+ ret, failed,
+ "SetDomainInfo failed");
+
+ if (!test_samr_handle_Close(samsync_state->b_samr, torture, domain_policy)) {
+ ret = false;
+ goto failed;
+ }
+
+ torture_assert_ntstatus_ok_goto(torture, s.out.result,
+ ret, failed,
+ talloc_asprintf(torture, "SetDomainInfo level %u failed", s.in.level));
+
+ status = torture_rpc_connection(torture,
+ &samsync_state->p_lsa,
+ &ndr_table_lsarpc);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+ samsync_state->b_lsa = samsync_state->p_lsa->binding_handle;
+
+ 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;
+
+ torture_assert_ntstatus_ok_goto(torture,
+ dcerpc_lsa_OpenPolicy2_r(samsync_state->b_lsa, mem_ctx, &r),
+ ret, failed,
+ "OpenPolicy2 failed");
+ torture_assert_ntstatus_ok_goto(torture, r.out.result,
+ ret, failed,
+ "OpenPolicy2 failed");
+
+ status = torture_rpc_binding(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ status = dcerpc_binding_set_flags(b,
+ DCERPC_SCHANNEL | DCERPC_SIGN,
+ DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(torture, status, "set flags");
+
+ credentials = cli_credentials_init(mem_ctx);
+
+ cli_credentials_set_workstation(credentials, TEST_MACHINE_NAME, CRED_SPECIFIED);
+ cli_credentials_set_domain(credentials, lpcfg_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)) {
+ torture_comment(torture, "Failed to connect to server as a BDC: %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+ samsync_state->b = samsync_state->p->binding_handle;
+
+ samsync_state->creds = cli_credentials_get_netlogon_creds(credentials);
+ if (samsync_state->creds == NULL) {
+ ret = false;
+ }
+
+
+
+ status = torture_rpc_binding(torture, &b_netlogon_wksta);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ goto failed;
+ }
+
+ status = dcerpc_binding_set_flags(b_netlogon_wksta,
+ DCERPC_SCHANNEL | DCERPC_SIGN,
+ DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(torture, status, "set flags");
+
+ 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, lpcfg_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)) {
+ torture_comment(torture, "Failed to connect to server as a Workstation: %s\n", nt_errstr(status));
+ ret = false;
+ goto failed;
+ }
+
+ samsync_state->creds_netlogon_wksta = cli_credentials_get_netlogon_creds(credentials_wksta);
+ if (samsync_state->creds_netlogon_wksta == NULL) {
+ torture_comment(torture, "Failed to obtail schanel creds!\n");
+ ret = false;
+ }
+
+ if (!test_DatabaseSync(torture, samsync_state, mem_ctx)) {
+ torture_comment(torture, "DatabaseSync failed\n");
+ ret = false;
+ }
+
+ if (!test_DatabaseDeltas(torture, samsync_state, mem_ctx)) {
+ torture_comment(torture, "DatabaseDeltas failed\n");
+ ret = false;
+ }
+
+ if (!test_DatabaseSync2(torture, samsync_state->p, mem_ctx, samsync_state->creds)) {
+ torture_comment(torture, "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 0000000..261c3b9
--- /dev/null
+++ b/source4/torture/rpc/scanner.c
@@ -0,0 +1,187 @@
+/*
+ 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 "librpc/gen_ndr/ndr_mgmt_c.h"
+#include "librpc/ndr/ndr_table.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.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;
+ unsigned int i;
+ DATA_BLOB stub_in, stub_out;
+ struct ndr_interface_table _tbl;
+ const struct ndr_interface_table *tbl;
+
+ /* FIXME: This should be fixed when torture_rpc_connection
+ * takes a ndr_syntax_id */
+ tbl = ndr_table_by_syntax(id);
+ if (tbl == NULL) {
+ _tbl = *iface;
+ _tbl.name = "__unknown__";
+ _tbl.syntax_id = *id;
+ _tbl.num_calls = UINT32_MAX;
+ tbl = &_tbl;
+ }
+
+ status = torture_rpc_connection(tctx, &p, tbl);
+ 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_talloc(mem_ctx, NULL, 1000);
+ memset(stub_in.data, 0xFF, stub_in.length);
+
+ for (i=0;i<200;i++) {
+ bool ok;
+ uint32_t out_flags = 0;
+
+ status = dcerpc_binding_handle_raw_call(p->binding_handle,
+ NULL, i,
+ 0, /* in_flags */
+ stub_in.data,
+ stub_in.length,
+ mem_ctx,
+ &stub_out.data,
+ &stub_out.length,
+ &out_flags);
+ ok = dcerpc_binding_handle_is_connected(p->binding_handle);
+ if (!ok) {
+ printf("\tpipe disconnected at %u\n", i);
+ goto done;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ break;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ printf("\taccess denied at %u\n", i);
+ goto done;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROTOCOL_ERROR)) {
+ printf("\tprotocol error at %u\n", i);
+ }
+ }
+
+ printf("\t%d calls available\n", i);
+ if (tbl->num_calls == UINT32_MAX) {
+ printf("\tinterface not known in local IDL\n");
+ } else if (tbl->num_calls != i) {
+ printf("\tWARNING: local IDL defines %u calls\n",
+ (unsigned int)tbl->num_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;
+ enum dcerpc_transport_t transport;
+
+ status = torture_rpc_binding(torture, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ transport = dcerpc_binding_get_transport(b);
+
+ 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 (transport == NCACN_IP_TCP) {
+ status = dcerpc_epm_map_binding(torture, b, l->table,
+ torture->ev,
+ 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 {
+ status = dcerpc_binding_set_string_option(b, "endpoint",
+ l->table->name);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(loop_ctx);
+ ret = false;
+ continue;
+ }
+ status = dcerpc_binding_set_abstract_syntax(b,
+ &l->table->syntax_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(loop_ctx);
+ ret = false;
+ continue;
+ }
+ }
+
+ lpcfg_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->binding_handle, 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 0000000..ed94c91
--- /dev/null
+++ b/source4/torture/rpc/schannel.c
@@ -0,0 +1,1338 @@
+/*
+ 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 "auth/credentials/credentials_krb5.h"
+#include "torture/rpc/torture_rpc.h"
+#include "lib/cmdline/cmdline.h"
+#include "../libcli/auth/schannel.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 "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 netlogon_creds_CredentialState *creds)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogonEx r;
+ struct netr_NetworkInfo ninfo;
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative = 1;
+ uint32_t _flags = 0;
+ DATA_BLOB names_blob, chal, lm_resp, nt_resp;
+ int i;
+ int flags = CLI_CRED_NTLM_AUTH;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ struct netr_UserSessionKey key;
+ struct netr_LMSessionKey LMSessKey;
+ uint32_t validation_levels[] = { 2, 3 };
+ struct netr_SamBaseInfo *base = NULL;
+ const char *crypto_alg = "";
+ bool can_do_validation_6 = true;
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+
+ if (lpcfg_client_lanman_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lpcfg_client_ntlmv2_auth(tctx->lp_ctx)) {
+ flags |= CLI_CRED_NTLMv2_AUTH;
+ }
+
+ cli_credentials_get_ntlm_username_domain(samba_cmdline_get_creds(),
+ 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, cli_credentials_get_workstation(credentials),
+ cli_credentials_get_domain(credentials));
+
+ status = cli_credentials_get_ntlm_response(
+ samba_cmdline_get_creds(),
+ tctx,
+ &flags,
+ chal,
+ NULL, /* server_timestamp */
+ 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 = 0;
+ ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials);
+
+ logon.network = &ninfo;
+
+ 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 = NetlogonNetworkInformation;
+ r.in.logon= &logon;
+ r.in.flags = &_flags;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+ r.out.flags = &_flags;
+
+ /*
+ - retrieve level6
+ - save usrsession and lmsession key
+ - retrieve level 2
+ - calculate, compare
+ - retrieve level 3
+ - calculate, compare
+ */
+
+ if (creds) {
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ crypto_alg = "AES";
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ crypto_alg = "ARCFOUR";
+ }
+ }
+
+ dcerpc_binding_handle_auth_info(b, NULL, &auth_level);
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ r.in.validation_level = 6;
+
+ torture_comment(tctx,
+ "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n",
+ ninfo.identity_info.account_name.string, crypto_alg,
+ r.in.validation_level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r),
+ "LogonSamLogonEx failed");
+ } else {
+ torture_comment(tctx,
+ "Skip auth_level[%u] Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n",
+ auth_level, ninfo.identity_info.account_name.string, crypto_alg,
+ r.in.validation_level);
+ r.out.result = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) {
+ can_do_validation_6 = false;
+ } else {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LogonSamLogonEx failed");
+
+ key = r.out.validation->sam6->base.key;
+ LMSessKey = r.out.validation->sam6->base.LMSessKey;
+
+ DEBUG(1,("unencrypted session keys from validation_level 6:\n"));
+ dump_data(1, r.out.validation->sam6->base.key.key, 16);
+ dump_data(1, r.out.validation->sam6->base.LMSessKey.key, 8);
+ }
+
+ for (i=0; i < ARRAY_SIZE(validation_levels); i++) {
+
+ r.in.validation_level = validation_levels[i];
+
+ torture_comment(tctx,
+ "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n",
+ ninfo.identity_info.account_name.string, crypto_alg,
+ r.in.validation_level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r),
+ "LogonSamLogonEx failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LogonSamLogonEx failed");
+
+ if (creds == NULL) {
+ /* when this test is called without creds no point in
+ * testing the session keys */
+ continue;
+ }
+
+ switch (validation_levels[i]) {
+ case 2:
+ base = &r.out.validation->sam2->base;
+ break;
+ case 3:
+ base = &r.out.validation->sam3->base;
+ break;
+ default:
+ break;
+ }
+
+ DEBUG(1,("encrypted keys validation_level %d:\n",
+ validation_levels[i]));
+ dump_data(1, base->key.key, 16);
+ dump_data(1, base->LMSessKey.key, 8);
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_decrypt(creds, base->key.key, 16);
+ netlogon_creds_aes_decrypt(creds, base->LMSessKey.key, 8);
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ netlogon_creds_arcfour_crypt(creds, base->key.key, 16);
+ netlogon_creds_arcfour_crypt(creds, base->LMSessKey.key, 8);
+ }
+
+ DEBUG(1,("decryped keys validation_level %d\n",
+ validation_levels[i]));
+
+ dump_data(1, base->key.key, 16);
+ dump_data(1, base->LMSessKey.key, 8);
+
+ if (!can_do_validation_6) {
+ /* we cant compare against unencrypted keys */
+ continue;
+ }
+
+ torture_assert_mem_equal(tctx,
+ base->key.key,
+ key.key,
+ 16,
+ "unexpected user session key\n");
+ torture_assert_mem_equal(tctx,
+ base->LMSessKey.key,
+ LMSessKey.key,
+ 8,
+ "unexpected LM session key\n");
+ }
+
+ return true;
+}
+
+static bool test_netlogon_ex_bug14932(struct dcerpc_pipe *p,
+ struct torture_context *tctx,
+ struct cli_credentials *credentials,
+ struct netlogon_creds_CredentialState *creds)
+{
+ NTSTATUS status;
+ struct netr_LogonSamLogonEx r;
+ struct netr_NetworkInfo ninfo;
+ union netr_LogonLevel logon;
+ union netr_Validation validation;
+ uint8_t authoritative = 1;
+ uint32_t _flags = 0;
+ static const char *netapp_magic =
+ "\x01\x01\x00\x00\x00\x00\x00\x00"
+ "\x3f\x3f\x3f\x3f\x3f\x3f\x3f\x3f"
+ "\xb8\x82\x3a\xf1\xb3\xdd\x08\x15"
+ "\x00\x00\x00\x00\x11\xa2\x08\x81"
+ "\x50\x38\x22\x78\x2b\x94\x47\xfe"
+ "\x54\x94\x7b\xff\x17\x27\x5a\xb4"
+ "\xf4\x18\xba\xdc\x2c\x38\xfd\x5b"
+ "\xfb\x0e\xc1\x85\x1e\xcc\x92\xbb"
+ "\x9b\xb1\xc4\xd5\x53\x14\xff\x8c"
+ "\x76\x49\xf5\x45\x90\x19\xa2";
+ NTTIME timestamp = BVAL(netapp_magic, 8);
+ DATA_BLOB names_blob = data_blob_string_const(netapp_magic + 28);
+ DATA_BLOB chal, lm_resp, nt_resp;
+ int i;
+ int flags = CLI_CRED_NTLM_AUTH;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct netr_UserSessionKey key;
+ struct netr_LMSessionKey LMSessKey;
+ uint32_t validation_levels[] = { 2, 3 };
+ struct netr_SamBaseInfo *base = NULL;
+ const char *crypto_alg = "";
+ bool can_do_validation_6 = true;
+ enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE;
+
+ flags |= CLI_CRED_NTLMv2_AUTH;
+
+ cli_credentials_get_ntlm_username_domain(samba_cmdline_get_creds(),
+ 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));
+
+ status = cli_credentials_get_ntlm_response(
+ samba_cmdline_get_creds(),
+ tctx,
+ &flags,
+ chal,
+ &timestamp,
+ 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 = 0;
+ ninfo.identity_info.workstation.string = cli_credentials_get_workstation(credentials);
+
+ logon.network = &ninfo;
+
+ 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 = NetlogonNetworkInformation;
+ r.in.logon= &logon;
+ r.in.flags = &_flags;
+ r.out.validation = &validation;
+ r.out.authoritative = &authoritative;
+ r.out.flags = &_flags;
+
+ /*
+ - retrieve level6
+ - save usrsession and lmsession key
+ - retrieve level 2
+ - calculate, compare
+ - retrieve level 3
+ - calculate, compare
+ */
+
+ if (creds != NULL) {
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ crypto_alg = "AES";
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ crypto_alg = "ARCFOUR";
+ }
+ }
+
+ dcerpc_binding_handle_auth_info(b, NULL, &auth_level);
+ if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
+ r.in.validation_level = 6;
+
+ torture_comment(tctx,
+ "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n",
+ ninfo.identity_info.account_name.string, crypto_alg,
+ r.in.validation_level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r),
+ "LogonSamLogonEx failed");
+ } else {
+ torture_comment(tctx,
+ "Skip auth_level[%u] Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n",
+ auth_level, ninfo.identity_info.account_name.string, crypto_alg,
+ r.in.validation_level);
+ r.out.result = NT_STATUS_INVALID_INFO_CLASS;
+ }
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_INVALID_INFO_CLASS)) {
+ can_do_validation_6 = false;
+ } else {
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LogonSamLogonEx failed");
+
+ key = r.out.validation->sam6->base.key;
+ LMSessKey = r.out.validation->sam6->base.LMSessKey;
+
+ DEBUG(1,("unencrypted session keys from validation_level 6:\n"));
+ dump_data(1, r.out.validation->sam6->base.key.key, 16);
+ dump_data(1, r.out.validation->sam6->base.LMSessKey.key, 8);
+ }
+
+ for (i=0; i < ARRAY_SIZE(validation_levels); i++) {
+
+ r.in.validation_level = validation_levels[i];
+
+ torture_comment(tctx,
+ "Testing LogonSamLogonEx with name %s using %s and validation_level: %d\n",
+ ninfo.identity_info.account_name.string, crypto_alg,
+ r.in.validation_level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_netr_LogonSamLogonEx_r(b, tctx, &r),
+ "LogonSamLogonEx failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "LogonSamLogonEx failed");
+
+ if (creds == NULL) {
+ /* when this test is called without creds no point in
+ * testing the session keys */
+ continue;
+ }
+
+ switch (validation_levels[i]) {
+ case 2:
+ base = &r.out.validation->sam2->base;
+ break;
+ case 3:
+ base = &r.out.validation->sam3->base;
+ break;
+ default:
+ break;
+ }
+
+ DEBUG(1,("encrypted keys validation_level %d:\n",
+ validation_levels[i]));
+ dump_data(1, base->key.key, 16);
+ dump_data(1, base->LMSessKey.key, 8);
+
+ if (creds->negotiate_flags & NETLOGON_NEG_SUPPORTS_AES) {
+ netlogon_creds_aes_decrypt(creds, base->key.key, 16);
+ netlogon_creds_aes_decrypt(creds, base->LMSessKey.key, 8);
+ } else if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+ netlogon_creds_arcfour_crypt(creds, base->key.key, 16);
+ netlogon_creds_arcfour_crypt(creds, base->LMSessKey.key, 8);
+ }
+
+ DEBUG(1,("decryped keys validation_level %d\n",
+ validation_levels[i]));
+
+ dump_data(1, base->key.key, 16);
+ dump_data(1, base->LMSessKey.key, 8);
+
+ if (!can_do_validation_6) {
+ /* we cant compare against unencrypted keys */
+ continue;
+ }
+
+ torture_assert_mem_equal(tctx,
+ base->key.key,
+ key.key,
+ 16,
+ "unexpected user session key\n");
+ torture_assert_mem_equal(tctx,
+ base->LMSessKey.key,
+ LMSessKey.key,
+ 8,
+ "unexpected LM session key\n");
+ }
+
+ return true;
+}
+
+/*
+ do some samr ops using the schannel connection
+ */
+static bool test_samr_ops(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b)
+{
+ struct samr_GetDomPwInfo r;
+ struct samr_PwInfo info;
+ struct samr_Connect connect_r;
+ struct samr_OpenDomain opendom;
+ int i;
+ struct lsa_String name;
+ struct policy_handle handle;
+ struct policy_handle domain_handle;
+
+ name.string = lpcfg_workgroup(tctx->lp_ctx);
+ r.in.domain_name = &name;
+ r.out.info = &info;
+
+ connect_r.in.system_name = 0;
+ connect_r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ connect_r.out.connect_handle = &handle;
+
+ torture_comment(tctx, "Testing Connect and OpenDomain on BUILTIN\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_Connect_r(b, tctx, &connect_r),
+ "Connect failed");
+ if (!NT_STATUS_IS_OK(connect_r.out.result)) {
+ if (NT_STATUS_EQUAL(connect_r.out.result, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx, "Connect failed (expected, schannel mapped to anonymous): %s\n",
+ nt_errstr(connect_r.out.result));
+ } else {
+ torture_comment(tctx, "Connect failed - %s\n", nt_errstr(connect_r.out.result));
+ 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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_OpenDomain_r(b, tctx, &opendom),
+ "OpenDomain failed");
+ if (!NT_STATUS_IS_OK(opendom.out.result)) {
+ torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(opendom.out.result));
+ return false;
+ }
+ }
+
+ torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string);
+
+ /* do several ops to test credential chaining */
+ for (i=0;i<5;i++) {
+ torture_assert_ntstatus_ok(tctx, dcerpc_samr_GetDomPwInfo_r(b, tctx, &r),
+ "GetDomPwInfo failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ if (!NT_STATUS_EQUAL(r.out.result, NT_STATUS_ACCESS_DENIED)) {
+ torture_comment(tctx, "GetDomPwInfo op %d failed - %s\n", i, nt_errstr(r.out.result));
+ 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;
+ bool ret = true;
+ struct lsa_String *account_name_p = NULL;
+ struct lsa_String *authority_name_p = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "\nTesting GetUserName\n");
+
+ r.in.system_name = "\\";
+ r.in.account_name = &account_name_p;
+ r.in.authority_name = &authority_name_p;
+ r.out.account_name = &account_name_p;
+
+ /* do several ops to test credential chaining and various operations */
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_GetUserName_r(b, tctx, &r),
+ "lsa_GetUserName failed");
+
+ authority_name_p = *r.out.authority_name;
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "GetUserName failed - %s\n", nt_errstr(r.out.result));
+ return false;
+ } else {
+ if (!r.out.account_name) {
+ return false;
+ }
+
+ if (strcmp(account_name_p->string, "ANONYMOUS LOGON") != 0) {
+ torture_comment(tctx, "GetUserName returned wrong user: %s, expected %s\n",
+ account_name_p->string, "ANONYMOUS LOGON");
+ /* FIXME: gd */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ return false;
+ }
+ }
+ if (!authority_name_p || !authority_name_p->string) {
+ return false;
+ }
+
+ if (strcmp(authority_name_p->string, "NT AUTHORITY") != 0) {
+ torture_comment(tctx, "GetUserName returned wrong user: %s, expected %s\n",
+ authority_name_p->string, "NT AUTHORITY");
+ /* FIXME: gd */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ 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 netlogon_creds_CredentialState *creds;
+ struct cli_credentials *credentials;
+ enum dcerpc_transport_t transport;
+
+ 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");
+
+ status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(tctx, status, "set 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 to samr with schannel");
+
+ torture_assert(tctx, test_samr_ops(tctx, p->binding_handle),
+ "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_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(tctx, status, "set flags");
+
+ status = dcerpc_secondary_auth_connection(p, b, &ndr_table_netlogon,
+ credentials, tctx->lp_ctx,
+ tctx, &p_netlogon);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+
+ creds = cli_credentials_get_netlogon_creds(credentials);
+ torture_assert(tctx, (creds != NULL), "schannel creds");
+
+ /* checks the capabilities */
+ torture_assert(tctx, test_netlogon_capabilities(p_netlogon, tctx, credentials, creds),
+ "Failed to process schannel secured capability ops (on fresh connection)");
+
+ /* 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");
+
+ /* regression test for https://bugzilla.samba.org/show_bug.cgi?id=14932 */
+ torture_assert(tctx, test_netlogon_ex_bug14932(p_netlogon, tctx, credentials, creds),
+ "Failed to process schannel secured NETLOGON EX for BUG 14932");
+
+ /* we *MUST* use ncacn_np for openpolicy etc. */
+ transport = dcerpc_binding_get_transport(b);
+ status = dcerpc_binding_set_transport(b, NCACN_NP);
+ torture_assert_ntstatus_ok(tctx, status, "set transport");
+
+ /* 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");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p_lsa, b, &ndr_table_lsarpc,
+ credentials, tctx->ev, tctx->lp_ctx),
+ "failed to connect lsarpc with schannel");
+
+ torture_assert(tctx, test_lsa_ops(tctx, p_lsa),
+ "Failed to process schannel secured LSA ops");
+
+ talloc_free(p_lsa);
+ p_lsa = NULL;
+
+ /* we *MUST* use ncacn_ip_tcp for lookupsids3/lookupnames4 */
+ status = dcerpc_binding_set_transport(b, NCACN_IP_TCP);
+ torture_assert_ntstatus_ok(tctx, status, "set transport");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_epm_map_binding(tctx, b, &ndr_table_lsarpc, tctx->ev, tctx->lp_ctx),
+ "failed to call epm map");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &p_lsa, b, &ndr_table_lsarpc,
+ credentials, tctx->ev, tctx->lp_ctx),
+ "failed to connect lsarpc with schannel");
+
+ torture_assert(tctx,
+ test_many_LookupSids(p_lsa, tctx, NULL, LSA_LOOKUP_NAMES_ALL),
+ "LsaLookupSids3 failed!\n");
+
+ status = dcerpc_binding_set_transport(b, transport);
+ torture_assert_ntstatus_ok(tctx, status, "set transport");
+
+
+ /* 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");
+
+ status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(tctx, status, "set 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->binding_handle),
+ "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_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(tctx, status, "set flags");
+
+ status = dcerpc_secondary_auth_connection(p_samr2, b, &ndr_table_netlogon,
+ credentials, tctx->lp_ctx,
+ tctx, &p_netlogon2);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+
+ /* checks the capabilities */
+ torture_assert(tctx, test_netlogon_capabilities(p_netlogon2, tctx, credentials, creds),
+ "Failed to process schannel secured capability ops (on fresh connection)");
+
+ /* 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 */
+ status = dcerpc_binding_set_flags(b, 0, DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(tctx, status, "set flags");
+
+ 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;
+}
+
+/*
+ * Purpose of this test is to demonstrate that a netlogon server carefully deals
+ * with anonymous attempts to set passwords, in particular when the server
+ * enforces the use of schannel. This test makes most sense to be run in an
+ * environment where the netlogon server enforces use of schannel.
+ */
+
+static bool test_schannel_anonymous_setPassword(struct torture_context *tctx,
+ uint32_t dcerpc_flags,
+ bool use2)
+{
+ NTSTATUS status, result;
+ const char *binding = torture_setting_string(tctx, "binding", NULL);
+ struct dcerpc_binding *b;
+ struct dcerpc_pipe *p = NULL;
+ struct cli_credentials *credentials;
+ bool ok = true;
+
+ credentials = cli_credentials_init(NULL);
+ torture_assert(tctx, credentials != NULL, "Bad credentials");
+ cli_credentials_set_anonymous(credentials);
+
+ status = dcerpc_parse_binding(tctx, binding, &b);
+ torture_assert_ntstatus_ok(tctx, status, "Bad binding string");
+
+ status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(tctx, status, "set flags");
+
+ status = dcerpc_pipe_connect_b(tctx,
+ &p,
+ b,
+ &ndr_table_netlogon,
+ credentials,
+ tctx->ev,
+ tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to connect without schannel");
+
+ if (use2) {
+ struct netr_ServerPasswordSet2 r = {};
+ struct netr_Authenticator credential = {};
+ struct netr_Authenticator return_authenticator = {};
+ struct netr_CryptPassword new_password = {};
+
+ 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 = 0;
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ status = dcerpc_netr_ServerPasswordSet2_r(p->binding_handle, tctx, &r);
+ result = r.out.result;
+ } else {
+ struct netr_ServerPasswordSet r = {};
+ struct netr_Authenticator credential = {};
+ struct netr_Authenticator return_authenticator = {};
+ struct samr_Password new_password = {};
+
+ 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 = 0;
+ r.in.computer_name = TEST_MACHINE_NAME;
+ r.in.credential = &credential;
+ r.in.new_password = &new_password;
+ r.out.return_authenticator = &return_authenticator;
+
+ status = dcerpc_netr_ServerPasswordSet_r(p->binding_handle, tctx, &r);
+ result = r.out.result;
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "ServerPasswordSet failed");
+
+ if (NT_STATUS_IS_OK(result)) {
+ torture_fail(tctx, "unexpectedly received NT_STATUS_OK");
+ }
+
+ return ok;
+}
+
+
+/*
+ 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 | DCERPC_SCHANNEL_AUTO},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_AUTO},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 },
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_AES},
+ { ACB_WSTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_AES },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_AUTO},
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_AUTO},
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_128 },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128 },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_AES },
+ { ACB_SVRTRUST, DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_AES }
+ };
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(tests);i++) {
+ torture_comment(torture, "Testing with acct_flags=0x%x dcerpc_flags=0x%x \n",
+ tests[i].acct_flags, tests[i].dcerpc_flags);
+
+ 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;
+}
+
+bool torture_rpc_schannel_anon_setpw(struct torture_context *torture)
+{
+ bool ret = true;
+ bool ok;
+ uint32_t dcerpc_flags = DCERPC_SCHANNEL | DCERPC_SIGN | DCERPC_SCHANNEL_AUTO;
+
+ ok = test_schannel_anonymous_setPassword(torture,
+ dcerpc_flags,
+ true);
+ if (!ok) {
+ torture_comment(torture,
+ "Failed with dcerpc_flags=0x%x\n",
+ dcerpc_flags);
+ ret = false;
+ }
+
+ ok = test_schannel_anonymous_setPassword(torture,
+ dcerpc_flags,
+ false);
+ if (!ok) {
+ torture_comment(torture,
+ "Failed with dcerpc_flags=0x%x\n",
+ 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_SCHANNEL_AUTO | 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 = cli_credentials_shallow_copy(torture, credentials1);
+ cli_credentials_set_netlogon_creds(credentials1, NULL);
+ cli_credentials_set_netlogon_creds(credentials2, NULL);
+
+ status = dcerpc_parse_binding(torture, binding, &b);
+ torture_assert_ntstatus_ok(torture, status, "Bad binding string");
+
+ status = dcerpc_binding_set_flags(b, dcerpc_flags, DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(torture, status, "set flags");
+
+ torture_comment(torture, "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");
+
+ cli_credentials_set_netlogon_creds(credentials1, NULL);
+ cli_credentials_set_netlogon_creds(credentials2, 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;
+};
+
+#if 0
+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++;
+ }
+}
+#endif
+
+static void torture_schannel_bench_recv(struct tevent_req *subreq);
+
+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 tevent_req *subreq;
+ struct cli_credentials *user_creds;
+
+ if (conn->total % 2) {
+ user_creds = s->user1_creds;
+ } else {
+ user_creds = s->user2_creds;
+ }
+
+ if (lpcfg_client_lanman_auth(s->tctx->lp_ctx)) {
+ flags |= CLI_CRED_LANMAN_AUTH;
+ }
+
+ if (lpcfg_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,
+ 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,
+ NULL, /* server_timestamp */
+ 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 = 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 = NetlogonNetworkInformation;
+ conn->r.in.logon = talloc(conn->tmp, union netr_LogonLevel);
+ conn->r.in.logon->network = &conn->ninfo;
+ conn->r.in.flags = talloc(conn->tmp, uint32_t);
+ conn->r.in.validation_level = 2;
+ conn->r.out.validation = talloc(conn->tmp, union netr_Validation);
+ conn->r.out.authoritative = talloc(conn->tmp, uint8_t);
+ conn->r.out.flags = conn->r.in.flags;
+
+ subreq = dcerpc_netr_LogonSamLogonEx_r_send(s, s->tctx->ev,
+ conn->pipe->binding_handle,
+ &conn->r);
+ torture_assert(s->tctx, subreq, "Failed to setup LogonSamLogonEx request");
+
+ tevent_req_set_callback(subreq, torture_schannel_bench_recv, conn);
+
+ return true;
+}
+
+static void torture_schannel_bench_recv(struct tevent_req *subreq)
+{
+ bool ret;
+ struct torture_schannel_bench_conn *conn =
+ (struct torture_schannel_bench_conn *)tevent_req_callback_data_void(subreq);
+ struct torture_schannel_bench *s = talloc_get_type(conn->s,
+ struct torture_schannel_bench);
+
+ s->error = dcerpc_netr_LogonSamLogonEx_r_recv(subreq, subreq);
+ TALLOC_FREE(subreq);
+ 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 = cli_credentials_shallow_copy(s,
+ samba_cmdline_get_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 = cli_credentials_shallow_copy(s,
+ samba_cmdline_get_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_USE_KERBEROS_DISABLED,
+ CRED_SPECIFIED);
+ cli_credentials_set_kerberos_state(s->wks_creds2,
+ CRED_USE_KERBEROS_DISABLED,
+ CRED_SPECIFIED);
+
+ for (i=0; i < s->nprocs; i++) {
+ struct cli_credentials *wks = s->wks_creds1;
+
+ if ((i % 2) && (torture_setting_bool(torture, "multijoin", false))) {
+ wks = s->wks_creds2;
+ }
+
+ s->conns[i].s = s;
+ s->conns[i].index = i;
+ s->conns[i].wks_creds = cli_credentials_shallow_copy(s->conns, wks);
+ cli_credentials_set_netlogon_creds(s->conns[i].wks_creds, NULL);
+ }
+
+ status = dcerpc_parse_binding(s, binding, &s->b);
+ torture_assert_ntstatus_ok(torture, status, "Bad binding string");
+
+ status = dcerpc_binding_set_flags(s->b, DCERPC_SCHANNEL | DCERPC_SIGN,
+ DCERPC_AUTH_OPTIONS);
+ torture_assert_ntstatus_ok(torture, status, "set flags");
+
+ 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 = tevent_loop_once(torture->ev);
+ torture_assert(torture, ev_ret == 0, "tevent_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_password(s->join_ctx1, 8, 255);
+ struct netlogon_creds_CredentialState *creds_state;
+ struct dcerpc_pipe *net_pipe;
+ struct netr_Authenticator credential, return_authenticator;
+ struct samr_Password new_password;
+
+ 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;
+ pwset.in.credential = &credential;
+ pwset.in.new_password = &new_password;
+ pwset.out.return_authenticator = &return_authenticator;
+
+ E_md4hash(password, new_password.hash);
+
+ creds_state = cli_credentials_get_netlogon_creds(
+ s->wks_creds1);
+ netlogon_creds_des_encrypt(creds_state, &new_password);
+ netlogon_creds_client_authenticator(creds_state, &credential);
+
+ torture_assert_ntstatus_ok(torture, dcerpc_netr_ServerPasswordSet_r(net_pipe->binding_handle, torture, &pwset),
+ "ServerPasswordSet failed");
+ torture_assert_ntstatus_ok(torture, pwset.out.result,
+ "ServerPasswordSet failed");
+
+ if (!netlogon_creds_client_check(creds_state,
+ &pwset.out.return_authenticator->cred)) {
+ torture_comment(torture, "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 */
+
+ cli_credentials_set_netlogon_creds(s->wks_creds1, 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 = tevent_loop_once(torture->ev);
+ torture_assert(torture, ev_ret == 0, "tevent_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 0000000..96f9965
--- /dev/null
+++ b/source4/torture/rpc/session_key.c
@@ -0,0 +1,250 @@
+/*
+ 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 "librpc/gen_ndr/ndr_lsa_c.h"
+
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/torture_rpc.h"
+#include "lib/cmdline/cmdline.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,
+ struct policy_handle *sec_handle)
+{
+ NTSTATUS status;
+ struct lsa_CreateSecret r;
+ struct lsa_SetSecret r3;
+ struct lsa_QuerySecret r4;
+ 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;
+ const char *secret1 = "abcdef12345699qwerty";
+ char *secret2;
+ char *secname;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ secname = talloc_asprintf(tctx, "torturesecret-%08x", (unsigned int)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;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_CreateSecret_r(b, tctx, &r),
+ "CreateSecret failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "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");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r3),
+ "SetSecret failed");
+ torture_assert_ntstatus_ok(tctx, r3.out.result, "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");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_SetSecret_r(b, tctx, &r3),
+ "SetSecret failed");
+ torture_assert_ntstatus_equal(tctx, r3.out.result, 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");
+ torture_assert_ntstatus_ok(tctx, dcerpc_lsa_QuerySecret_r(b, tctx, &r4),
+ "QuerySecret failed");
+ torture_assert_ntstatus_ok(tctx, r4.out.result, "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;
+
+ secret2 = sess_decrypt_string(tctx, &blob1, &session_key);
+
+ torture_assert_str_equal(tctx, secret1, secret2, "Returned secret invalid");
+
+ 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;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b;
+ struct policy_handle sec_handle = {0};
+ bool ok;
+
+ lpcfg_set_cmdline(torture->lp_ctx, "ntlmssp client:keyexchange", settings->keyexchange?"True":"False");
+ lpcfg_set_cmdline(torture->lp_ctx, "ntlmssp_client:ntlm2", settings->ntlm2?"True":"False");
+ lpcfg_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");
+
+ status = dcerpc_binding_set_flags(binding, settings->bindoptions, 0);
+ torture_assert_ntstatus_ok(torture, status, "dcerpc_binding_set_flags");
+
+ status = dcerpc_pipe_connect_b(torture, &p, binding,
+ &ndr_table_lsarpc,
+ samba_cmdline_get_creds(),
+ torture->ev,
+ torture->lp_ctx);
+
+ torture_assert_ntstatus_ok(torture, status, "connect");
+ b = p->binding_handle;
+
+ if (!test_lsa_OpenPolicy2(b, torture, &handle)) {
+ talloc_free(p);
+ return false;
+ }
+
+ torture_assert(torture, handle, "OpenPolicy2 failed. This test cannot run against this server");
+
+ ok = test_CreateSecret_basic(p, torture, handle, &sec_handle);
+
+ if (is_valid_policy_hnd(&sec_handle)) {
+ struct lsa_DeleteObject d;
+
+ d.in.handle = &sec_handle;
+ d.out.handle = &sec_handle;
+
+ status = dcerpc_lsa_DeleteObject_r(b, torture, &d);
+ if (!NT_STATUS_IS_OK(status) ||
+ !NT_STATUS_IS_OK(d.out.result)) {
+ torture_warning(torture,
+ "Failed to delete secrets object");
+ }
+ }
+
+ talloc_free(p);
+ return ok;
+}
+
+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, "lsa.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 0000000..dd0d458
--- /dev/null
+++ b/source4/torture/rpc/spoolss.c
@@ -0,0 +1,11704 @@
+/*
+ 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
+ Copyright (C) Guenther Deschner 2009-2011,2013
+
+ 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_misc.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+#include "lib/registry/registry.h"
+#include "libcli/libcli.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+#include "lib/cmdline/cmdline.h"
+#include "system/filesys.h"
+#include "torture/ndr/ndr.h"
+#include "torture/smb2/proto.h"
+
+#define TORTURE_WELLKNOWN_PRINTER "torture_wkn_printer"
+#define TORTURE_PRINTER "torture_printer"
+#define TORTURE_WELLKNOWN_PRINTER_EX "torture_wkn_printer_ex"
+#define TORTURE_PRINTER_EX "torture_printer_ex"
+#define TORTURE_DRIVER "torture_driver"
+#define TORTURE_DRIVER_ADD "torture_driver_add"
+#define TORTURE_DRIVER_EX "torture_driver_ex"
+#define TORTURE_DRIVER_ADOBE "torture_driver_adobe"
+#define TORTURE_DRIVER_EX_ADOBE "torture_driver_ex_adobe"
+#define TORTURE_DRIVER_ADOBE_CUPSADDSMB "torture_driver_adobe_cupsaddsmb"
+#define TORTURE_DRIVER_TIMESTAMPS "torture_driver_timestamps"
+#define TORTURE_DRIVER_DELETER "torture_driver_deleter"
+#define TORTURE_DRIVER_COPY_DIR "torture_driver_copy_from_directory"
+#define TORTURE_DRIVER_DELETERIN "torture_driver_deleterin"
+#define TORTURE_PRINTER_STATIC1 "print1"
+
+#define TOP_LEVEL_PRINT_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print"
+#define TOP_LEVEL_PRINT_PRINTERS_KEY TOP_LEVEL_PRINT_KEY "\\Printers"
+#define TOP_LEVEL_CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control\\Print"
+#define TOP_LEVEL_CONTROL_FORMS_KEY TOP_LEVEL_CONTROL_KEY "\\Forms"
+#define TOP_LEVEL_CONTROL_PRINTERS_KEY TOP_LEVEL_CONTROL_KEY "\\Printers"
+#define TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY TOP_LEVEL_CONTROL_KEY "\\Environments"
+
+struct test_spoolss_context {
+ struct dcerpc_pipe *spoolss_pipe;
+
+ /* server environment */
+ const char *environment;
+
+ /* 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[9];
+ union spoolss_DriverInfo *drivers[9];
+
+ /* 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];
+};
+
+struct torture_driver_context {
+ struct {
+ const char *driver_directory;
+ const char *environment;
+ } local;
+ struct {
+ const char *driver_directory;
+ const char *driver_upload_directory;
+ const char *environment;
+ } remote;
+ struct spoolss_AddDriverInfo8 info8;
+ bool ex;
+};
+
+struct torture_printer_context {
+ struct dcerpc_pipe *spoolss_pipe;
+ struct spoolss_SetPrinterInfo2 info2;
+ struct torture_driver_context driver;
+ bool ex;
+ bool wellknown;
+ bool added_driver;
+ bool have_driver;
+ struct spoolss_DeviceMode *devmode;
+ struct policy_handle handle;
+};
+
+static bool upload_printer_driver(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d);
+static bool remove_printer_driver(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d);
+static bool fillup_printserver_info(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct torture_driver_context *d);
+static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir);
+
+#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_UINT64(tctx, c, r, e) do {\
+ _CHECK_FIELD_SIZE(c, r, e, uint64_t); \
+ torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \
+} while(0)
+
+
+#define COMPARE_NTTIME(tctx, c, r, e) do {\
+ _CHECK_FIELD_SIZE(c, r, e, NTTIME); \
+ torture_assert_int_equal(tctx, c.e, r.e, "invalid value"); \
+} while(0)
+
+#define COMPARE_STRING_ARRAY(tctx, c,r,e) do {\
+ int __i; \
+ if (!c.e && !r.e) { \
+ break; \
+ } \
+ if (c.e && !r.e) { \
+ torture_fail(tctx, #r "." #e " field is NULL and " #c "." #e " is not\n"); \
+ } \
+ if (!c.e && r.e) { \
+ torture_fail(tctx, #c "." #e " field is NULL and " #r "." #e " is not\n"); \
+ } \
+ for (__i=0;c.e[__i] != NULL; __i++) { \
+ torture_assert_str_equal(tctx, c.e[__i], r.e[__i], "invalid value"); \
+ } \
+} while(0)
+
+#define CHECK_ALIGN(size, n) do {\
+ if (size % n) {\
+ torture_warning(tctx, "%d is *NOT* %d byte aligned, should be %d",\
+ size, n, size + n - (size % n));\
+ }\
+} while(0)
+
+#define DO_ROUND(size, n) (((size)+((n)-1)) & ~((n)-1))
+
+#define CHECK_NEEDED_SIZE_ENUM_LEVEL(fn, info, level, count, needed, align) do { \
+ if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
+ uint32_t size = ndr_size_##fn##_info(tctx, level, count, info);\
+ uint32_t round_size = DO_ROUND(size, align);\
+ if (round_size != needed) {\
+ torture_warning(tctx, __location__": "#fn" level %d (count: %d) got unexpected needed size: %d, we calculated: %d", level, count, needed, round_size);\
+ CHECK_ALIGN(size, align);\
+ }\
+ }\
+} while(0)
+
+#define CHECK_NEEDED_SIZE_ENUM(fn, info, count, needed, align) do { \
+ if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
+ uint32_t size = ndr_size_##fn##_info(tctx, count, info);\
+ uint32_t round_size = DO_ROUND(size, align);\
+ if (round_size != needed) {\
+ torture_warning(tctx, __location__": "#fn" (count: %d) got unexpected needed size: %d, we calculated: %d", count, needed, round_size);\
+ CHECK_ALIGN(size, align);\
+ }\
+ }\
+} while(0)
+
+#define CHECK_NEEDED_SIZE_LEVEL(fn, info, level, needed, align) do { \
+ if (torture_setting_bool(tctx, "spoolss_check_size", false)) {\
+ uint32_t size = ndr_size_##fn(info, level, 0);\
+ uint32_t round_size = DO_ROUND(size, align);\
+ if (round_size != needed) {\
+ torture_warning(tctx, __location__": "#fn" level %d got unexpected needed size: %d, we calculated: %d", level, needed, round_size);\
+ CHECK_ALIGN(size, align);\
+ }\
+ }\
+} while(0)
+
+static bool PrinterInfo_to_SetPrinterInfo(struct torture_context *tctx,
+ const union spoolss_PrinterInfo *i,
+ uint32_t level,
+ union spoolss_SetPrinterInfo *s)
+{
+ switch (level) {
+ case 0:
+ s->info0 = talloc(tctx, struct spoolss_SetPrinterInfo0);
+ break;
+ case 2:
+ s->info2 = talloc(tctx, struct spoolss_SetPrinterInfo2);
+ s->info2->servername = i->info2.servername;
+ s->info2->printername = i->info2.printername;
+ s->info2->sharename = i->info2.sharename;
+ s->info2->portname = i->info2.portname;
+ s->info2->drivername = i->info2.drivername;
+ s->info2->comment = i->info2.comment;
+ s->info2->location = i->info2.location;
+ s->info2->devmode_ptr = 0;
+ s->info2->sepfile = i->info2.sepfile;
+ s->info2->printprocessor = i->info2.printprocessor;
+ s->info2->datatype = i->info2.datatype;
+ s->info2->parameters = i->info2.parameters;
+ s->info2->secdesc_ptr = 0;
+ s->info2->attributes = i->info2.attributes;
+ s->info2->priority = i->info2.priority;
+ s->info2->defaultpriority = i->info2.defaultpriority;
+ s->info2->starttime = i->info2.starttime;
+ s->info2->untiltime = i->info2.untiltime;
+ s->info2->status = i->info2.status;
+ s->info2->cjobs = i->info2.cjobs;
+ s->info2->averageppm = i->info2.averageppm;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_OpenPrinter_server(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *server_handle)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter op;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ op.in.printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ op.in.datatype = NULL;
+ op.in.devmode_ctr.devmode= NULL;
+ op.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ op.out.handle = server_handle;
+
+ torture_comment(tctx, "Testing OpenPrinter(%s)\n", op.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter_r(b, tctx, &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,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PortInfo *info;
+
+ r.in.servername = "";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPorts level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPorts_r(b, 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_zero(ctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPorts_r(b, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPorts failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
+
+ torture_assert(tctx, info, "EnumPorts returned no info");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, r.in.level, count, needed, 4);
+
+ ctx->port_count[level] = count;
+ ctx->ports[level] = 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_GetPrintProcessorDirectory(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ NTSTATUS status;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_GetPrintProcessorDirectory r;
+ struct {
+ uint16_t level;
+ const char *server;
+ } levels[] = {{
+ .level = 1,
+ .server = NULL
+ },{
+ .level = 1,
+ .server = ""
+ },{
+ .level = 78,
+ .server = ""
+ },{
+ .level = 1,
+ .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
+ },{
+ .level = 1024,
+ .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
+ }
+ };
+ int i;
+ uint32_t needed;
+
+ 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 = ctx->environment;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetPrintProcessorDirectory level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_GetPrintProcessorDirectory_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_GetPrintProcessorDirectory failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INSUFFICIENT_BUFFER,
+ "GetPrintProcessorDirectory unexpected return code");
+
+ blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetPrintProcessorDirectory_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrintProcessorDirectory failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrintProcessorDirectory failed");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_PrintProcessorDirectoryInfo, r.out.info, r.in.level, needed, 2);
+ }
+
+ return true;
+}
+
+
+static bool test_GetPrinterDriverDirectory(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ NTSTATUS status;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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(tctx, "\\\\%s", dcerpc_server_name(p))
+ },{
+ .level = 1024,
+ .server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p))
+ }
+ };
+ int i;
+ uint32_t needed;
+
+ 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 = ctx->environment;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetPrinterDriverDirectory level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &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_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_GetPrinterDriverDirectory failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetPrinterDriverDirectory failed");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverDirectoryInfo, r.out.info, r.in.level, needed, 2);
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterDrivers_buffers(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *environment,
+ uint32_t level,
+ uint32_t offered,
+ uint32_t *count_p,
+ union spoolss_DriverInfo **info_p)
+{
+ struct spoolss_EnumPrinterDrivers r;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+ DATA_BLOB buffer;
+
+ if (offered > 0) {
+ buffer = data_blob_talloc_zero(tctx, offered);
+ }
+
+ r.in.server = server_name;
+ r.in.environment = environment;
+ r.in.level = level;
+ r.in.buffer = offered ? &buffer : NULL;
+ r.in.offered = offered;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinterDrivers(%s) level %u, offered: %u\n",
+ r.in.environment, r.in.level, r.in.offered);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinterDrivers_r(b, tctx, &r),
+ "EnumPrinterDrivers failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinterDrivers_r(b, tctx, &r),
+ "EnumPrinterDrivers failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "EnumPrinterDrivers failed");
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinterDrivers, info, r.in.level, count, needed, 4);
+
+ return true;
+
+}
+
+
+static bool test_EnumPrinterDrivers_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *environment,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_DriverInfo **info_p)
+{
+ return test_EnumPrinterDrivers_buffers(tctx, b, server_name,
+ environment, level, 0,
+ count_p, info_p);
+}
+
+static bool test_EnumPrinterDrivers_findone(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *environment,
+ uint32_t level,
+ const char *driver_name,
+ union spoolss_DriverInfo *info_p)
+{
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+ int i;
+ const char *environment_ret = NULL;
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_args(tctx, b, server_name, environment, level, &count, &info),
+ "failed to enumerate printer drivers");
+
+ for (i=0; i < count; i++) {
+ const char *driver_name_ret = "";
+ switch (level) {
+ case 1:
+ driver_name_ret = info[i].info1.driver_name;
+ break;
+ case 2:
+ driver_name_ret = info[i].info2.driver_name;
+ environment_ret = info[i].info2.architecture;
+ break;
+ case 3:
+ driver_name_ret = info[i].info3.driver_name;
+ environment_ret = info[i].info3.architecture;
+ break;
+ case 4:
+ driver_name_ret = info[i].info4.driver_name;
+ environment_ret = info[i].info4.architecture;
+ break;
+ case 5:
+ driver_name_ret = info[i].info5.driver_name;
+ environment_ret = info[i].info5.architecture;
+ break;
+ case 6:
+ driver_name_ret = info[i].info6.driver_name;
+ environment_ret = info[i].info6.architecture;
+ break;
+ case 7:
+ driver_name_ret = info[i].info7.driver_name;
+ break;
+ case 8:
+ driver_name_ret = info[i].info8.driver_name;
+ environment_ret = info[i].info8.architecture;
+ break;
+ default:
+ break;
+ }
+ if (environment_ret) {
+ torture_assert_str_equal(tctx, environment, environment_ret, "architecture mismatch");
+ }
+ if (strequal(driver_name, driver_name_ret)) {
+ if (info_p) {
+ *info_p = info[i];
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool test_EnumPrinterDrivers(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint16_t levels[] = { 1, 2, 3, 4, 5, 6, 8 };
+ uint16_t buffer_sizes[] = { 0, 1024, 6040, 0xffff };
+ int i, j, a;
+
+ /* FIXME: gd, come back and fix "" as server, and handle
+ * priority of returned error codes in torture test and samba 3
+ * server */
+ const char *server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ const char *environments[2];
+
+ environments[0] = SPOOLSS_ARCHITECTURE_ALL;
+ environments[1] = ctx->environment;
+
+ for (a=0;a<ARRAY_SIZE(environments);a++) {
+
+ for (i=0;i<ARRAY_SIZE(buffer_sizes);i++) {
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_buffers(tctx, b, server_name,
+ environments[a], 3,
+ buffer_sizes[i],
+ NULL, NULL),
+ "failed to enumerate drivers");
+ }
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_args(tctx, b, server_name, environments[a], level, &count, &info),
+ "failed to enumerate drivers");
+
+ ctx->driver_count[level] = count;
+ ctx->drivers[level] = 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 - 1];j++) {
+ union spoolss_DriverInfo *cur = &ctx->drivers[level - 1][j];
+ union spoolss_DriverInfo *ref = &ctx->drivers[8][j];
+
+ switch (level) {
+ case 1:
+ COMPARE_STRING(tctx, cur->info1, ref->info8, driver_name);
+ break;
+ case 2:
+ COMPARE_UINT32(tctx, cur->info2, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info2, ref->info8, config_file);
+ break;
+ case 3:
+ COMPARE_UINT32(tctx, cur->info3, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, config_file);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info3, ref->info8, dependent_files);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, monitor_name);
+ COMPARE_STRING(tctx, cur->info3, ref->info8, default_datatype);
+ break;
+ case 4:
+ COMPARE_UINT32(tctx, cur->info4, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, config_file);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info8, dependent_files);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, monitor_name);
+ COMPARE_STRING(tctx, cur->info4, ref->info8, default_datatype);
+ COMPARE_STRING_ARRAY(tctx, cur->info4, ref->info8, previous_names);
+ break;
+ case 5:
+ COMPARE_UINT32(tctx, cur->info5, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info5, ref->info8, config_file);
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info8, driver_attributes);*/
+ /*COMPARE_UINT32(tctx, cur->info5, ref->info8, config_version);*/
+ /*TODO: ! COMPARE_UINT32(tctx, cur->info5, ref->info8, driver_version); */
+ break;
+ case 6:
+ COMPARE_UINT32(tctx, cur->info6, ref->info8, version);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, driver_name);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, architecture);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, driver_path);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, data_file);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, config_file);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, help_file);
+ COMPARE_STRING_ARRAY(tctx, cur->info6, ref->info8, dependent_files);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, monitor_name);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, default_datatype);
+ COMPARE_STRING_ARRAY(tctx, cur->info6, ref->info8, previous_names);
+ COMPARE_NTTIME(tctx, cur->info6, ref->info8, driver_date);
+ COMPARE_UINT64(tctx, cur->info6, ref->info8, driver_version);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, manufacturer_name);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, manufacturer_url);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, hardware_id);
+ COMPARE_STRING(tctx, cur->info6, ref->info8, provider);
+ break;
+ case 8:
+ /* level 8 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumMonitors(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_MonitorInfo *info;
+
+ r.in.servername = "";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumMonitors level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumMonitors_r(b, 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_zero(ctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumMonitors_r(b, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumMonitors failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumMonitors failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumMonitors, info, r.in.level, count, needed, 4);
+
+ ctx->monitor_count[level] = count;
+ ctx->monitors[level] = 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:
+ torture_assert_str_equal(tctx, ref->info2.environment, ctx->environment, "invalid environment");
+ /* level 2 is our reference, and it makes no sense to compare it to itself */
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessors_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_PrintProcessorInfo **info_p,
+ WERROR expected_result)
+{
+ struct spoolss_EnumPrintProcessors r;
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrintProcessorInfo *info;
+
+ r.in.servername = "";
+ r.in.environment = environment;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrintProcessors(%s) level %u\n",
+ r.in.environment, r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrintProcessors_r(b, tctx, &r),
+ "EnumPrintProcessors failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrintProcessors_r(b, tctx, &r),
+ "EnumPrintProcessors failed");
+ }
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "EnumPrintProcessors failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessors, info, level, count, needed, 4);
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessors(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ uint16_t levels[] = {0, 1, 2, 3, 32, 256 };
+ uint16_t ok[] = {0, 1, 0, 0, 0, 0 };
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessors_level(tctx, b, "phantasy", 1, NULL, NULL, WERR_INVALID_ENVIRONMENT),
+ "test_EnumPrintProcessors_level failed");
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ union spoolss_PrintProcessorInfo *info;
+ uint32_t count;
+ WERROR expected_result = ok[i] ? WERR_OK : WERR_INVALID_LEVEL;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessors_level(tctx, b, ctx->environment, levels[i], &count, &info, expected_result),
+ "test_EnumPrintProcessors_level failed");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessorDataTypes_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *print_processor_name,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_PrintProcDataTypesInfo **info_p,
+ WERROR expected_result)
+{
+ struct spoolss_EnumPrintProcessorDataTypes r;
+ DATA_BLOB blob;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrintProcDataTypesInfo *info;
+
+ r.in.servername = "";
+ r.in.print_processor_name = print_processor_name;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrintProcessorDataTypes(%s) level %u\n",
+ r.in.print_processor_name, r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrintProcessorDataTypes_r(b, tctx, &r),
+ "EnumPrintProcessorDataTypes failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrintProcessorDataTypes_r(b, tctx, &r),
+ "EnumPrintProcessorDataTypes failed");
+ }
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "EnumPrintProcessorDataTypes failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrintProcessorDataTypes, info, level, count, needed, 4);
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static bool test_EnumPrintProcessorDataTypes(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ uint16_t levels[] = {0, 1, 2, 3, 32, 256 };
+ uint16_t ok[] = {0, 1, 0, 0, 0, 0 };
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessorDataTypes_level(tctx, b, NULL, 1, NULL, NULL, WERR_UNKNOWN_PRINTPROCESSOR),
+ "test_EnumPrintProcessorDataTypes_level failed");
+
+ torture_assert(tctx,
+ test_EnumPrintProcessorDataTypes_level(tctx, b, "nonexisting", 1, NULL, NULL, WERR_UNKNOWN_PRINTPROCESSOR),
+ "test_EnumPrintProcessorDataTypes_level failed");
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int level = levels[i];
+ uint32_t count;
+ union spoolss_PrintProcDataTypesInfo *info;
+ WERROR expected_result = ok[i] ? WERR_OK : WERR_INVALID_LEVEL;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessorDataTypes_level(tctx, b, "winprint", level, &count, &info, expected_result),
+ "test_EnumPrintProcessorDataTypes_level failed");
+ }
+
+ {
+ union spoolss_PrintProcessorInfo *info;
+ uint32_t count;
+
+ torture_assert(tctx,
+ test_EnumPrintProcessors_level(tctx, b, ctx->environment, 1, &count, &info, WERR_OK),
+ "test_EnumPrintProcessors_level failed");
+
+ for (i=0; i < count; i++) {
+ torture_assert(tctx,
+ test_EnumPrintProcessorDataTypes_level(tctx, b, info[i].info1.print_processor_name, 1, NULL, NULL, WERR_OK),
+ "test_EnumPrintProcessorDataTypes_level failed");
+ }
+ }
+
+
+ return true;
+}
+
+static bool test_EnumPrinters(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ 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;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = "";
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinters_r(b, 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_zero(ctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPrinters_r(b, ctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EnumPrinters failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
+
+ ctx->printer_count[level] = count;
+ ctx->printers[level] = 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_GetPrinterDriver2(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name,
+ const char *environment);
+
+bool test_GetPrinter_level_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ WERROR expected_werror,
+ union spoolss_PrinterInfo *info)
+{
+ struct spoolss_GetPrinter r;
+ uint32_t needed;
+
+ r.in.handle = handle;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetPrinter level %u\n", r.in.level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinter_r(b, tctx, &r),
+ "GetPrinter failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinter_r(b, tctx, &r),
+ "GetPrinter failed");
+ }
+
+ torture_assert_werr_equal(tctx,
+ r.out.result, expected_werror,
+ "GetPrinter failed");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterInfo, r.out.info, r.in.level, needed, 4);
+
+ if (info && r.out.info) {
+ *info = *r.out.info;
+ }
+
+ return true;
+}
+
+bool test_GetPrinter_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ union spoolss_PrinterInfo *info)
+{
+ return test_GetPrinter_level_exp(tctx, b, handle, level, WERR_OK, info);
+}
+
+static bool test_GetPrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *environment)
+{
+ uint32_t levels[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ union spoolss_PrinterInfo info;
+
+ ZERO_STRUCT(info);
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, levels[i], &info),
+ "failed to call GetPrinter");
+
+ if ((levels[i] == 2) && info.info2.drivername && strlen(info.info2.drivername)) {
+ torture_assert(tctx,
+ test_GetPrinterDriver2(tctx, b, handle, info.info2.drivername, environment),
+ "failed to call test_GetPrinterDriver2");
+ }
+ }
+
+ return true;
+}
+
+static bool test_SetPrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ struct spoolss_SetPrinterInfoCtr *info_ctr,
+ struct spoolss_DevmodeContainer *devmode_ctr,
+ struct sec_desc_buf *secdesc_ctr,
+ enum spoolss_PrinterControl command)
+{
+ struct spoolss_SetPrinter r;
+
+ r.in.handle = handle;
+ r.in.info_ctr = info_ctr;
+ r.in.devmode_ctr = devmode_ctr;
+ r.in.secdesc_ctr = secdesc_ctr;
+ r.in.command = command;
+
+ torture_comment(tctx, "Testing SetPrinter level %d\n", r.in.info_ctr->level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
+ "failed to call SetPrinter");
+ torture_assert(tctx, (W_ERROR_EQUAL(r.out.result, WERR_OK)
+ || W_ERROR_EQUAL(r.out.result, WERR_IO_PENDING)),
+ "SetPrinter failed");
+
+ return true;
+}
+
+static bool test_SetPrinter_errors(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct spoolss_SetPrinter r;
+ uint16_t levels[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+ int i;
+
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info_ctr.level = 0;
+ info_ctr.info.info0 = NULL;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.in.command = 0;
+
+ torture_comment(tctx, "Testing SetPrinter all zero\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
+ "failed to call SetPrinter");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "failed to call SetPrinter");
+
+ again:
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+
+ struct spoolss_SetPrinterInfo0 info0;
+ struct spoolss_SetPrinterInfo1 info1;
+ struct spoolss_SetPrinterInfo2 info2;
+ struct spoolss_SetPrinterInfo3 info3;
+ struct spoolss_SetPrinterInfo4 info4;
+ struct spoolss_SetPrinterInfo5 info5;
+ struct spoolss_SetPrinterInfo6 info6;
+ struct spoolss_SetPrinterInfo7 info7;
+ struct spoolss_SetPrinterInfo8 info8;
+ struct spoolss_SetPrinterInfo9 info9;
+
+
+ info_ctr.level = levels[i];
+ switch (levels[i]) {
+ case 0:
+ ZERO_STRUCT(info0);
+ info_ctr.info.info0 = &info0;
+ break;
+ case 1:
+ ZERO_STRUCT(info1);
+ info_ctr.info.info1 = &info1;
+ break;
+ case 2:
+ ZERO_STRUCT(info2);
+ info_ctr.info.info2 = &info2;
+ break;
+ case 3:
+ ZERO_STRUCT(info3);
+ info_ctr.info.info3 = &info3;
+ break;
+ case 4:
+ ZERO_STRUCT(info4);
+ info_ctr.info.info4 = &info4;
+ break;
+ case 5:
+ ZERO_STRUCT(info5);
+ info_ctr.info.info5 = &info5;
+ break;
+ case 6:
+ ZERO_STRUCT(info6);
+ info_ctr.info.info6 = &info6;
+ break;
+ case 7:
+ ZERO_STRUCT(info7);
+ info_ctr.info.info7 = &info7;
+ break;
+ case 8:
+ ZERO_STRUCT(info8);
+ info_ctr.info.info8 = &info8;
+ break;
+ case 9:
+ ZERO_STRUCT(info9);
+ info_ctr.info.info9 = &info9;
+ break;
+ }
+
+ torture_comment(tctx, "Testing SetPrinter level %d, command %d\n",
+ info_ctr.level, r.in.command);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
+ "failed to call SetPrinter");
+
+ switch (r.in.command) {
+ case SPOOLSS_PRINTER_CONTROL_UNPAUSE: /* 0 */
+ /* is ignored for all levels other then 0 */
+ if (info_ctr.level > 0) {
+ /* ignored then */
+ break;
+ }
+
+ FALL_THROUGH;
+ case SPOOLSS_PRINTER_CONTROL_PAUSE: /* 1 */
+ case SPOOLSS_PRINTER_CONTROL_RESUME: /* 2 */
+ case SPOOLSS_PRINTER_CONTROL_PURGE: /* 3 */
+ if (info_ctr.level > 0) {
+ /* is invalid for all levels other then 0 */
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PRINTER_COMMAND,
+ "unexpected error code returned");
+ continue;
+ } else {
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call SetPrinter with non 0 command");
+ continue;
+ }
+ break;
+
+ case SPOOLSS_PRINTER_CONTROL_SET_STATUS: /* 4 */
+ /* FIXME: gd needs further investigation */
+ default:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PRINTER_COMMAND,
+ "unexpected error code returned");
+ continue;
+ }
+
+ switch (info_ctr.level) {
+ case 1:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL,
+ "unexpected error code returned");
+ break;
+ case 2:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_UNKNOWN_PRINTER_DRIVER,
+ "unexpected error code returned");
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 7:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "unexpected error code returned");
+ break;
+ case 9:
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "unexpected error code returned");
+ break;
+ default:
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call SetPrinter");
+ break;
+ }
+ }
+
+ if (r.in.command < 5) {
+ r.in.command++;
+ goto again;
+ }
+
+ return true;
+}
+
+static void clear_info2(struct spoolss_SetPrinterInfoCtr *r)
+{
+ if ((r->level == 2) && (r->info.info2)) {
+ r->info.info2->secdesc_ptr = 0;
+ r->info.info2->devmode_ptr = 0;
+ }
+}
+
+static bool test_PrinterInfo(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter s;
+ struct spoolss_GetPrinter q;
+ struct spoolss_GetPrinter q0;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ union spoolss_PrinterInfo info;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ uint32_t needed;
+ bool ret = true;
+ int i;
+
+ uint32_t status_list[] = {
+ /* these do not stick
+ PRINTER_STATUS_PAUSED,
+ PRINTER_STATUS_ERROR,
+ PRINTER_STATUS_PENDING_DELETION, */
+ PRINTER_STATUS_PAPER_JAM,
+ PRINTER_STATUS_PAPER_OUT,
+ PRINTER_STATUS_MANUAL_FEED,
+ PRINTER_STATUS_PAPER_PROBLEM,
+ PRINTER_STATUS_OFFLINE,
+ PRINTER_STATUS_IO_ACTIVE,
+ PRINTER_STATUS_BUSY,
+ PRINTER_STATUS_PRINTING,
+ PRINTER_STATUS_OUTPUT_BIN_FULL,
+ PRINTER_STATUS_NOT_AVAILABLE,
+ PRINTER_STATUS_WAITING,
+ PRINTER_STATUS_PROCESSING,
+ PRINTER_STATUS_INITIALIZING,
+ PRINTER_STATUS_WARMING_UP,
+ PRINTER_STATUS_TONER_LOW,
+ PRINTER_STATUS_NO_TONER,
+ PRINTER_STATUS_PAGE_PUNT,
+ PRINTER_STATUS_USER_INTERVENTION,
+ PRINTER_STATUS_OUT_OF_MEMORY,
+ PRINTER_STATUS_DOOR_OPEN,
+ PRINTER_STATUS_SERVER_UNKNOWN,
+ PRINTER_STATUS_POWER_SAVE,
+ /* these do not stick
+ 0x02000000,
+ 0x04000000,
+ 0x08000000,
+ 0x10000000,
+ 0x20000000,
+ 0x40000000,
+ 0x80000000 */
+ };
+ uint32_t default_attribute = PRINTER_ATTRIBUTE_LOCAL;
+ uint32_t attribute_list[] = {
+ PRINTER_ATTRIBUTE_QUEUED,
+ /* fails with WERR_INVALID_DATATYPE:
+ PRINTER_ATTRIBUTE_DIRECT, */
+ /* does not stick
+ PRINTER_ATTRIBUTE_DEFAULT, */
+ PRINTER_ATTRIBUTE_SHARED,
+ /* does not stick
+ PRINTER_ATTRIBUTE_NETWORK, */
+ PRINTER_ATTRIBUTE_HIDDEN,
+ PRINTER_ATTRIBUTE_LOCAL,
+ PRINTER_ATTRIBUTE_ENABLE_DEVQ,
+ PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS,
+ PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST,
+ PRINTER_ATTRIBUTE_WORK_OFFLINE,
+ /* does not stick
+ PRINTER_ATTRIBUTE_ENABLE_BIDI, */
+ /* fails with WERR_INVALID_DATATYPE:
+ PRINTER_ATTRIBUTE_RAW_ONLY, */
+ /* these do not stick
+ PRINTER_ATTRIBUTE_PUBLISHED,
+ PRINTER_ATTRIBUTE_FAX,
+ PRINTER_ATTRIBUTE_TS,
+ 0x00010000,
+ 0x00020000,
+ 0x00040000,
+ 0x00080000,
+ 0x00100000,
+ 0x00200000,
+ 0x00400000,
+ 0x00800000,
+ 0x01000000,
+ 0x02000000,
+ 0x04000000,
+ 0x08000000,
+ 0x10000000,
+ 0x20000000,
+ 0x40000000,
+ 0x80000000 */
+ };
+
+ torture_skip(tctx, "Printer Info test is currently broken, skipping");
+
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ s.in.handle = handle;
+ s.in.command = 0;
+ s.in.info_ctr = &info_ctr;
+ s.in.devmode_ctr = &devmode_ctr;
+ s.in.secdesc_ctr = &secdesc_ctr;
+
+ q.in.handle = handle;
+ q.out.info = &info;
+ q0 = q;
+
+#define TESTGETCALL(call, r) \
+ r.in.buffer = NULL; \
+ r.in.offered = 0;\
+ r.out.needed = &needed; \
+ status = dcerpc_spoolss_ ##call## _r(b, tctx, &r); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.level, nt_errstr(status), __location__); \
+ ret = false; \
+ break; \
+ }\
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {\
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed); \
+ r.in.buffer = &blob; \
+ r.in.offered = needed; \
+ }\
+ status = dcerpc_spoolss_ ##call## _r(b, tctx, &r); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.level, nt_errstr(status), __location__); \
+ ret = false; \
+ break; \
+ } \
+ if (!W_ERROR_IS_OK(r.out.result)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.level, win_errstr(r.out.result), __location__); \
+ ret = false; \
+ break; \
+ }
+
+
+#define TESTSETCALL_EXP(call, r, err) \
+ clear_info2(&info_ctr);\
+ status = dcerpc_spoolss_ ##call## _r(b, tctx, &r); \
+ if (!NT_STATUS_IS_OK(status)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.info_ctr->level, nt_errstr(status), __location__); \
+ ret = false; \
+ break; \
+ } \
+ if (!W_ERROR_IS_OK(err)) { \
+ if (!W_ERROR_EQUAL(err, r.out.result)) { \
+ torture_comment(tctx, #call " level %u failed - %s, expected %s (%s)\n", \
+ r.in.info_ctr->level, win_errstr(r.out.result), win_errstr(err), __location__); \
+ ret = false; \
+ } \
+ break; \
+ } \
+ if (!W_ERROR_IS_OK(r.out.result)) { \
+ torture_comment(tctx, #call " level %u failed - %s (%s)\n", \
+ r.in.info_ctr->level, win_errstr(r.out.result), __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define TESTSETCALL(call, r) \
+ TESTSETCALL_EXP(call, r, WERR_OK)
+
+#define STRING_EQUAL(s1, s2, field) \
+ if ((s1 && !s2) || (s2 && !s1) || strcmp(s1, s2)) { \
+ torture_comment(tctx, "Failed to set %s to '%s' (%s)\n", \
+ #field, s2, __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define MEM_EQUAL(s1, s2, length, field) \
+ if ((s1 && !s2) || (s2 && !s1) || memcmp(s1, s2, length)) { \
+ torture_comment(tctx, "Failed to set %s to '%s' (%s)\n", \
+ #field, (const char *)s2, __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define INT_EQUAL(i1, i2, field) \
+ if (i1 != i2) { \
+ torture_comment(tctx, "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 SD_EQUAL(sd1, sd2, field) \
+ if (!security_descriptor_equal(sd1, sd2)) { \
+ torture_comment(tctx, "Failed to set %s (%s)\n", \
+ #field, __location__); \
+ ret = false; \
+ break; \
+ }
+
+#define TEST_PRINTERINFO_STRING_EXP_ERR(lvl1, field1, lvl2, field2, value, err) do { \
+ void *p; \
+ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
+ q.in.level = lvl1; \
+ TESTGETCALL(GetPrinter, q) \
+ info_ctr.level = lvl1; \
+ p = (void *)&q.out.info->info ## lvl1; \
+ info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
+ info_ctr.info.info ## lvl1->field1 = value;\
+ TESTSETCALL_EXP(SetPrinter, s, err) \
+ info_ctr.info.info ## lvl1->field1 = ""; \
+ TESTGETCALL(GetPrinter, q) \
+ info_ctr.info.info ## lvl1->field1 = value; \
+ STRING_EQUAL(info_ctr.info.info ## lvl1->field1, value, field1); \
+ q.in.level = lvl2; \
+ TESTGETCALL(GetPrinter, q) \
+ p = (void *)&q.out.info->info ## lvl2; \
+ info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)p; \
+ STRING_EQUAL(info_ctr.info.info ## lvl2->field2, value, field2); \
+ } while (0)
+
+#define TEST_PRINTERINFO_STRING(lvl1, field1, lvl2, field2, value) do { \
+ TEST_PRINTERINFO_STRING_EXP_ERR(lvl1, field1, lvl2, field2, value, WERR_OK); \
+ } while (0);
+
+#define TEST_PRINTERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value) do { \
+ void *p; \
+ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
+ q.in.level = lvl1; \
+ TESTGETCALL(GetPrinter, q) \
+ info_ctr.level = lvl1; \
+ p = (void *)&q.out.info->info ## lvl1; \
+ info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
+ info_ctr.info.info ## lvl1->field1 = value; \
+ TESTSETCALL(SetPrinter, s) \
+ info_ctr.info.info ## lvl1->field1 = 0; \
+ TESTGETCALL(GetPrinter, q) \
+ p = (void *)&q.out.info->info ## lvl1; \
+ info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
+ INT_EQUAL(info_ctr.info.info ## lvl1->field1, exp_value, field1); \
+ q.in.level = lvl2; \
+ TESTGETCALL(GetPrinter, q) \
+ p = (void *)&q.out.info->info ## lvl2; \
+ info_ctr.info.info ## lvl2 = (struct spoolss_SetPrinterInfo ## lvl2 *)p; \
+ INT_EQUAL(info_ctr.info.info ## lvl2->field2, exp_value, field1); \
+ } while (0)
+
+#define TEST_PRINTERINFO_INT(lvl1, field1, lvl2, field2, value) do { \
+ TEST_PRINTERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, value); \
+ } while (0)
+
+ q0.in.level = 0;
+ do { TESTGETCALL(GetPrinter, q0) } while (0);
+
+ TEST_PRINTERINFO_STRING(2, comment, 1, comment, "xx2-1 comment");
+ TEST_PRINTERINFO_STRING(2, comment, 2, comment, "xx2-2 comment");
+
+ /* level 0 printername does not stick */
+/* TEST_PRINTERINFO_STRING(2, printername, 0, printername, "xx2-0 printer"); */
+ TEST_PRINTERINFO_STRING(2, printername, 1, name, "xx2-1 printer");
+ TEST_PRINTERINFO_STRING(2, printername, 2, printername, "xx2-2 printer");
+ TEST_PRINTERINFO_STRING(2, printername, 4, printername, "xx2-4 printer");
+ TEST_PRINTERINFO_STRING(2, printername, 5, printername, "xx2-5 printer");
+/* TEST_PRINTERINFO_STRING(4, printername, 0, printername, "xx4-0 printer"); */
+ TEST_PRINTERINFO_STRING(4, printername, 1, name, "xx4-1 printer");
+ TEST_PRINTERINFO_STRING(4, printername, 2, printername, "xx4-2 printer");
+ TEST_PRINTERINFO_STRING(4, printername, 4, printername, "xx4-4 printer");
+ TEST_PRINTERINFO_STRING(4, printername, 5, printername, "xx4-5 printer");
+/* TEST_PRINTERINFO_STRING(5, printername, 0, printername, "xx5-0 printer"); */
+ TEST_PRINTERINFO_STRING(5, printername, 1, name, "xx5-1 printer");
+ TEST_PRINTERINFO_STRING(5, printername, 2, printername, "xx5-2 printer");
+ TEST_PRINTERINFO_STRING(5, printername, 4, printername, "xx5-4 printer");
+ TEST_PRINTERINFO_STRING(5, printername, 5, printername, "xx5-5 printer");
+
+ /* servername can be set but does not stick
+ TEST_PRINTERINFO_STRING(2, servername, 0, servername, "xx2-0 servername");
+ TEST_PRINTERINFO_STRING(2, servername, 2, servername, "xx2-2 servername");
+ TEST_PRINTERINFO_STRING(2, servername, 4, servername, "xx2-4 servername");
+ */
+
+ /* passing an invalid port will result in WERR_UNKNOWN_PORT */
+ TEST_PRINTERINFO_STRING_EXP_ERR(2, portname, 2, portname, "xx2-2 portname", WERR_UNKNOWN_PORT);
+ TEST_PRINTERINFO_STRING_EXP_ERR(2, portname, 5, portname, "xx2-5 portname", WERR_UNKNOWN_PORT);
+ TEST_PRINTERINFO_STRING_EXP_ERR(5, portname, 2, portname, "xx5-2 portname", WERR_UNKNOWN_PORT);
+ TEST_PRINTERINFO_STRING_EXP_ERR(5, portname, 5, portname, "xx5-5 portname", WERR_UNKNOWN_PORT);
+
+ TEST_PRINTERINFO_STRING(2, sharename, 2, sharename, "xx2-2 sharename");
+ /* passing an invalid driver will result in WERR_UNKNOWN_PRINTER_DRIVER */
+ TEST_PRINTERINFO_STRING_EXP_ERR(2, drivername, 2, drivername, "xx2-2 drivername", WERR_UNKNOWN_PRINTER_DRIVER);
+ TEST_PRINTERINFO_STRING(2, location, 2, location, "xx2-2 location");
+ /* passing an invalid sepfile will result in WERR_INVALID_SEPARATOR_FILE */
+ TEST_PRINTERINFO_STRING_EXP_ERR(2, sepfile, 2, sepfile, "xx2-2 sepfile", WERR_INVALID_SEPARATOR_FILE);
+ /* passing an invalid printprocessor will result in WERR_UNKNOWN_PRINTPROCESSOR */
+ TEST_PRINTERINFO_STRING_EXP_ERR(2, printprocessor, 2, printprocessor, "xx2-2 printprocessor", WERR_UNKNOWN_PRINTPROCESSOR);
+ TEST_PRINTERINFO_STRING(2, datatype, 2, datatype, "xx2-2 datatype");
+ TEST_PRINTERINFO_STRING(2, parameters, 2, parameters, "xx2-2 parameters");
+
+ for (i=0; i < ARRAY_SIZE(attribute_list); i++) {
+/* TEST_PRINTERINFO_INT_EXP(2, attributes, 1, flags,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ ); */
+ TEST_PRINTERINFO_INT_EXP(2, attributes, 2, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(2, attributes, 4, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(2, attributes, 5, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+/* TEST_PRINTERINFO_INT_EXP(4, attributes, 1, flags,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ ); */
+ TEST_PRINTERINFO_INT_EXP(4, attributes, 2, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(4, attributes, 4, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(4, attributes, 5, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+/* TEST_PRINTERINFO_INT_EXP(5, attributes, 1, flags,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ ); */
+ TEST_PRINTERINFO_INT_EXP(5, attributes, 2, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(5, attributes, 4, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ TEST_PRINTERINFO_INT_EXP(5, attributes, 5, attributes,
+ attribute_list[i],
+ (attribute_list[i] | default_attribute)
+ );
+ }
+
+ for (i=0; i < ARRAY_SIZE(status_list); i++) {
+ /* level 2 sets do not stick
+ TEST_PRINTERINFO_INT(2, status, 0, status, status_list[i]);
+ TEST_PRINTERINFO_INT(2, status, 2, status, status_list[i]);
+ TEST_PRINTERINFO_INT(2, status, 6, status, status_list[i]); */
+ TEST_PRINTERINFO_INT(6, status, 0, status, status_list[i]);
+ TEST_PRINTERINFO_INT(6, status, 2, status, status_list[i]);
+ TEST_PRINTERINFO_INT(6, status, 6, status, status_list[i]);
+ }
+
+ /* priorities need to be between 0 and 99
+ passing an invalid priority will result in WERR_INVALID_PRIORITY */
+ TEST_PRINTERINFO_INT(2, priority, 2, priority, 0);
+ TEST_PRINTERINFO_INT(2, priority, 2, priority, 1);
+ TEST_PRINTERINFO_INT(2, priority, 2, priority, 99);
+ /* TEST_PRINTERINFO_INT(2, priority, 2, priority, 100); */
+ TEST_PRINTERINFO_INT(2, defaultpriority,2, defaultpriority, 0);
+ TEST_PRINTERINFO_INT(2, defaultpriority,2, defaultpriority, 1);
+ TEST_PRINTERINFO_INT(2, defaultpriority,2, defaultpriority, 99);
+ /* TEST_PRINTERINFO_INT(2, defaultpriority,2, defaultpriority, 100); */
+
+ TEST_PRINTERINFO_INT(2, starttime, 2, starttime, __LINE__);
+ TEST_PRINTERINFO_INT(2, untiltime, 2, untiltime, __LINE__);
+
+ /* does not stick
+ TEST_PRINTERINFO_INT(2, cjobs, 2, cjobs, __LINE__);
+ TEST_PRINTERINFO_INT(2, averageppm, 2, averageppm, __LINE__); */
+
+ /* does not stick
+ TEST_PRINTERINFO_INT(5, device_not_selected_timeout, 5, device_not_selected_timeout, __LINE__);
+ TEST_PRINTERINFO_INT(5, transmission_retry_timeout, 5, transmission_retry_timeout, __LINE__); */
+
+ /* FIXME: gd also test devmode and secdesc behavior */
+
+ {
+ /* verify composition of level 1 description field */
+ const char *description;
+ const char *tmp;
+
+ q0.in.level = 1;
+ do { TESTGETCALL(GetPrinter, q0) } while (0);
+
+ description = talloc_strdup(tctx, q0.out.info->info1.description);
+
+ q0.in.level = 2;
+ do { TESTGETCALL(GetPrinter, q0) } while (0);
+
+ tmp = talloc_asprintf(tctx, "%s,%s,%s",
+ q0.out.info->info2.printername,
+ q0.out.info->info2.drivername,
+ q0.out.info->info2.location);
+
+ do { STRING_EQUAL(description, tmp, "description")} while (0);
+ }
+
+ return ret;
+}
+
+static bool test_security_descriptor_equal(struct torture_context *tctx,
+ const struct security_descriptor *sd1,
+ const struct security_descriptor *sd2)
+{
+ if (sd1 == sd2) {
+ return true;
+ }
+
+ if (!sd1 || !sd2) {
+ torture_comment(tctx, "%s\n", __location__);
+ return false;
+ }
+
+ torture_assert_int_equal(tctx, sd1->revision, sd2->revision, "revision mismatch");
+ torture_assert_int_equal(tctx, sd1->type, sd2->type, "type mismatch");
+
+ torture_assert_sid_equal(tctx, sd1->owner_sid, sd2->owner_sid, "owner mismatch");
+ torture_assert_sid_equal(tctx, sd1->group_sid, sd2->group_sid, "group mismatch");
+
+ if (!security_acl_equal(sd1->sacl, sd2->sacl)) {
+ torture_comment(tctx, "%s: sacl mismatch\n", __location__);
+ NDR_PRINT_DEBUG(security_acl, sd1->sacl);
+ NDR_PRINT_DEBUG(security_acl, sd2->sacl);
+ return false;
+ }
+ if (!security_acl_equal(sd1->dacl, sd2->dacl)) {
+ torture_comment(tctx, "%s: dacl mismatch\n", __location__);
+ NDR_PRINT_DEBUG(security_acl, sd1->dacl);
+ NDR_PRINT_DEBUG(security_acl, sd2->dacl);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_sd_set_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ struct security_descriptor *sd)
+{
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ union spoolss_SetPrinterInfo sinfo;
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfo3 info3;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ switch (level) {
+ case 2: {
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+ torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+ break;
+ }
+ case 3: {
+
+ info3.sec_desc_ptr = 0;
+
+ info_ctr.level = 3;
+ info_ctr.info.info3 = &info3;
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ secdesc_ctr.sd = sd;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), "");
+
+ return true;
+}
+
+static bool test_PrinterInfo_SDs(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ union spoolss_PrinterInfo info;
+ struct security_descriptor *sd1, *sd2;
+ int i;
+
+ /* just compare level 2 and level 3 */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd1 = info.info2.secdesc;
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 3, &info), "");
+
+ sd2 = info.info3.secdesc;
+
+ torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2),
+ "SD level 2 != SD level 3");
+
+
+ /* query level 2, set level 2, query level 2 */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd1 = info.info2.secdesc;
+
+ torture_assert(tctx, test_sd_set_level(tctx, b, handle, 2, sd1), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd2 = info.info2.secdesc;
+ if (sd1->type & SEC_DESC_DACL_DEFAULTED) {
+ torture_comment(tctx, "removing SEC_DESC_DACL_DEFAULTED\n");
+ sd1->type &= ~SEC_DESC_DACL_DEFAULTED;
+ }
+
+ torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2),
+ "SD level 2 != SD level 2 after SD has been set via level 2");
+
+
+ /* query level 2, set level 3, query level 2 */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd1 = info.info2.secdesc;
+
+ torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd1), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ sd2 = info.info2.secdesc;
+
+ torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2),
+ "SD level 2 != SD level 2 after SD has been set via level 3");
+
+ /* set modified sd level 3, query level 2 */
+
+ for (i=0; i < 93; i++) {
+ struct security_ace a;
+ const char *sid_string = talloc_asprintf(tctx, "S-1-5-32-9999%i", i);
+ a.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ a.flags = 0;
+ a.size = 0; /* autogenerated */
+ a.access_mask = 0;
+ a.trustee = *dom_sid_parse_talloc(tctx, sid_string);
+ torture_assert_ntstatus_ok(tctx, security_descriptor_dacl_add(sd1, &a), "");
+ }
+
+ torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd1), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+ sd2 = info.info2.secdesc;
+
+ if (sd1->type & SEC_DESC_DACL_DEFAULTED) {
+ torture_comment(tctx, "removing SEC_DESC_DACL_DEFAULTED\n");
+ sd1->type &= ~SEC_DESC_DACL_DEFAULTED;
+ }
+
+ torture_assert(tctx, test_security_descriptor_equal(tctx, sd1, sd2),
+ "modified SD level 2 != SD level 2 after SD has been set via level 3");
+
+
+ return true;
+}
+
+/*
+ * wrapper call that saves original sd, runs tests, and restores sd
+ */
+
+static bool test_PrinterInfo_SD(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ union spoolss_PrinterInfo info;
+ struct security_descriptor *sd;
+ bool ret = true;
+
+ torture_comment(tctx, "Testing Printer Security Descriptors\n");
+
+ /* save original sd */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to get initial security descriptor");
+
+ sd = security_descriptor_copy(tctx, info.info2.secdesc);
+
+ /* run tests */
+
+ ret = test_PrinterInfo_SDs(tctx, b, handle);
+
+ /* restore original sd */
+
+ torture_assert(tctx, test_sd_set_level(tctx, b, handle, 3, sd),
+ "failed to restore initial security descriptor");
+
+ torture_comment(tctx, "Printer Security Descriptors test %s\n\n",
+ ret ? "succeeded" : "failed");
+
+
+ return ret;
+}
+
+static bool test_devmode_set_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ struct spoolss_DeviceMode *devmode)
+{
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ union spoolss_SetPrinterInfo sinfo;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ switch (level) {
+ case 2: {
+ union spoolss_PrinterInfo info;
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+ torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+ break;
+ }
+ case 8: {
+ struct spoolss_SetPrinterInfo8 info8;
+
+ info8.devmode_ptr = 0;
+
+ info_ctr.level = 8;
+ info_ctr.info.info8 = &info8;
+
+ break;
+ }
+ default:
+ return false;
+ }
+
+ devmode_ctr.devmode = devmode;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0), "");
+
+ return true;
+}
+
+
+static bool test_devicemode_equal(struct torture_context *tctx,
+ const struct spoolss_DeviceMode *d1,
+ const struct spoolss_DeviceMode *d2)
+{
+ if (d1 == d2) {
+ return true;
+ }
+
+ if (!d1 || !d2) {
+ torture_comment(tctx, "%s\n", __location__);
+ return false;
+ }
+ torture_assert_str_equal(tctx, d1->devicename, d2->devicename, "devicename mismatch");
+ torture_assert_int_equal(tctx, d1->specversion, d2->specversion, "specversion mismatch");
+ torture_assert_int_equal(tctx, d1->driverversion, d2->driverversion, "driverversion mismatch");
+ torture_assert_int_equal(tctx, d1->size, d2->size, "size mismatch");
+ torture_assert_int_equal(tctx, d1->__driverextra_length, d2->__driverextra_length, "__driverextra_length mismatch");
+ torture_assert_int_equal(tctx, d1->fields, d2->fields, "fields mismatch");
+ torture_assert_int_equal(tctx, d1->orientation, d2->orientation, "orientation mismatch");
+ torture_assert_int_equal(tctx, d1->papersize, d2->papersize, "papersize mismatch");
+ torture_assert_int_equal(tctx, d1->paperlength, d2->paperlength, "paperlength mismatch");
+ torture_assert_int_equal(tctx, d1->paperwidth, d2->paperwidth, "paperwidth mismatch");
+ torture_assert_int_equal(tctx, d1->scale, d2->scale, "scale mismatch");
+ torture_assert_int_equal(tctx, d1->copies, d2->copies, "copies mismatch");
+ torture_assert_int_equal(tctx, d1->defaultsource, d2->defaultsource, "defaultsource mismatch");
+ torture_assert_int_equal(tctx, d1->printquality, d2->printquality, "printquality mismatch");
+ torture_assert_int_equal(tctx, d1->color, d2->color, "color mismatch");
+ torture_assert_int_equal(tctx, d1->duplex, d2->duplex, "duplex mismatch");
+ torture_assert_int_equal(tctx, d1->yresolution, d2->yresolution, "yresolution mismatch");
+ torture_assert_int_equal(tctx, d1->ttoption, d2->ttoption, "ttoption mismatch");
+ torture_assert_int_equal(tctx, d1->collate, d2->collate, "collate mismatch");
+ torture_assert_str_equal(tctx, d1->formname, d2->formname, "formname mismatch");
+ torture_assert_int_equal(tctx, d1->logpixels, d2->logpixels, "logpixels mismatch");
+ torture_assert_int_equal(tctx, d1->bitsperpel, d2->bitsperpel, "bitsperpel mismatch");
+ torture_assert_int_equal(tctx, d1->pelswidth, d2->pelswidth, "pelswidth mismatch");
+ torture_assert_int_equal(tctx, d1->pelsheight, d2->pelsheight, "pelsheight mismatch");
+ torture_assert_int_equal(tctx, d1->displayflags, d2->displayflags, "displayflags mismatch");
+ torture_assert_int_equal(tctx, d1->displayfrequency, d2->displayfrequency, "displayfrequency mismatch");
+ torture_assert_int_equal(tctx, d1->icmmethod, d2->icmmethod, "icmmethod mismatch");
+ torture_assert_int_equal(tctx, d1->icmintent, d2->icmintent, "icmintent mismatch");
+ torture_assert_int_equal(tctx, d1->mediatype, d2->mediatype, "mediatype mismatch");
+ torture_assert_int_equal(tctx, d1->dithertype, d2->dithertype, "dithertype mismatch");
+ torture_assert_int_equal(tctx, d1->reserved1, d2->reserved1, "reserved1 mismatch");
+ torture_assert_int_equal(tctx, d1->reserved2, d2->reserved2, "reserved2 mismatch");
+ torture_assert_int_equal(tctx, d1->panningwidth, d2->panningwidth, "panningwidth mismatch");
+ torture_assert_int_equal(tctx, d1->panningheight, d2->panningheight, "panningheight mismatch");
+ torture_assert_data_blob_equal(tctx, d1->driverextra_data, d2->driverextra_data, "driverextra_data mismatch");
+
+ return true;
+}
+
+static bool test_devicemode_full(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct spoolss_SetPrinter s;
+ struct spoolss_GetPrinter q;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo8 info8;
+ union spoolss_PrinterInfo info;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ uint32_t needed;
+ bool ret = true;
+ NTSTATUS status;
+
+#define TEST_DEVMODE_INT_EXP_RESULT(lvl1, field1, lvl2, field2, value, exp_value, expected_result) do { \
+ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \
+ q.in.level = lvl1; \
+ TESTGETCALL(GetPrinter, q) \
+ info_ctr.level = lvl1; \
+ if (lvl1 == 2) {\
+ void *p = (void *)&q.out.info->info ## lvl1; \
+ info_ctr.info.info ## lvl1 = (struct spoolss_SetPrinterInfo ## lvl1 *)p; \
+ } else if (lvl1 == 8) {\
+ info_ctr.info.info ## lvl1 = &info8; \
+ }\
+ devmode_ctr.devmode = q.out.info->info ## lvl1.devmode; \
+ devmode_ctr.devmode->field1 = value; \
+ TESTSETCALL_EXP(SetPrinter, s, expected_result) \
+ if (W_ERROR_IS_OK(expected_result)) { \
+ TESTGETCALL(GetPrinter, q) \
+ INT_EQUAL(q.out.info->info ## lvl1.devmode->field1, exp_value, field1); \
+ q.in.level = lvl2; \
+ TESTGETCALL(GetPrinter, q) \
+ INT_EQUAL(q.out.info->info ## lvl2.devmode->field2, exp_value, field1); \
+ }\
+ } while (0)
+
+#define TEST_DEVMODE_INT_EXP(lvl1, field1, lvl2, field2, value, expected_result) do { \
+ TEST_DEVMODE_INT_EXP_RESULT(lvl1, field1, lvl2, field2, value, value, expected_result); \
+ } while (0)
+
+#define TEST_DEVMODE_INT(lvl1, field1, lvl2, field2, value) do { \
+ TEST_DEVMODE_INT_EXP_RESULT(lvl1, field1, lvl2, field2, value, value, WERR_OK); \
+ } while (0)
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ ZERO_STRUCT(info8);
+
+ s.in.handle = handle;
+ s.in.command = 0;
+ s.in.info_ctr = &info_ctr;
+ s.in.devmode_ctr = &devmode_ctr;
+ s.in.secdesc_ctr = &secdesc_ctr;
+
+ q.in.handle = handle;
+ q.out.info = &info;
+
+#if 0
+ const char *devicename;/* [charset(UTF16)] */
+ enum spoolss_DeviceModeSpecVersion specversion;
+ uint16_t driverversion;
+ uint16_t __driverextra_length;/* [value(r->driverextra_data.length)] */
+ uint32_t fields;
+#endif
+ TEST_DEVMODE_INT_EXP(8, size, 8, size, __LINE__, WERR_INVALID_PARAMETER);
+ TEST_DEVMODE_INT_EXP(8, size, 8, size, 0, WERR_INVALID_PARAMETER);
+ TEST_DEVMODE_INT_EXP(8, size, 8, size, 0xffff, WERR_INVALID_PARAMETER);
+ TEST_DEVMODE_INT_EXP(8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAMETER : WERR_OK);
+ TEST_DEVMODE_INT(8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length);
+
+ devmode_ctr.devmode->driverextra_data = data_blob_string_const("foobar");
+ torture_assert(tctx,
+ test_devmode_set_level(tctx, b, handle, 8, devmode_ctr.devmode),
+ "failed to set devmode");
+
+ TEST_DEVMODE_INT_EXP(8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0), (devmode_ctr.devmode->__driverextra_length > 0 ) ? WERR_INVALID_PARAMETER : WERR_OK);
+ TEST_DEVMODE_INT(8, size, 8, size, ndr_size_spoolss_DeviceMode(devmode_ctr.devmode, 0) - devmode_ctr.devmode->__driverextra_length);
+
+ TEST_DEVMODE_INT(8, orientation, 8, orientation, __LINE__);
+ TEST_DEVMODE_INT(8, papersize, 8, papersize, __LINE__);
+ TEST_DEVMODE_INT(8, paperlength, 8, paperlength, __LINE__);
+ TEST_DEVMODE_INT(8, paperwidth, 8, paperwidth, __LINE__);
+ TEST_DEVMODE_INT(8, scale, 8, scale, __LINE__);
+ TEST_DEVMODE_INT(8, copies, 8, copies, __LINE__);
+ TEST_DEVMODE_INT(8, defaultsource, 8, defaultsource, __LINE__);
+ TEST_DEVMODE_INT(8, printquality, 8, printquality, __LINE__);
+ TEST_DEVMODE_INT(8, color, 8, color, __LINE__);
+ TEST_DEVMODE_INT(8, duplex, 8, duplex, __LINE__);
+ TEST_DEVMODE_INT(8, yresolution, 8, yresolution, __LINE__);
+ TEST_DEVMODE_INT(8, ttoption, 8, ttoption, __LINE__);
+ TEST_DEVMODE_INT(8, collate, 8, collate, __LINE__);
+#if 0
+ const char *formname;/* [charset(UTF16)] */
+#endif
+ TEST_DEVMODE_INT(8, logpixels, 8, logpixels, __LINE__);
+ TEST_DEVMODE_INT(8, bitsperpel, 8, bitsperpel, __LINE__);
+ TEST_DEVMODE_INT(8, pelswidth, 8, pelswidth, __LINE__);
+ TEST_DEVMODE_INT(8, pelsheight, 8, pelsheight, __LINE__);
+ TEST_DEVMODE_INT(8, displayflags, 8, displayflags, __LINE__);
+ TEST_DEVMODE_INT(8, displayfrequency, 8, displayfrequency, __LINE__);
+ TEST_DEVMODE_INT(8, icmmethod, 8, icmmethod, __LINE__);
+ TEST_DEVMODE_INT(8, icmintent, 8, icmintent, __LINE__);
+ TEST_DEVMODE_INT(8, mediatype, 8, mediatype, __LINE__);
+ TEST_DEVMODE_INT(8, dithertype, 8, dithertype, __LINE__);
+ TEST_DEVMODE_INT(8, reserved1, 8, reserved1, __LINE__);
+ TEST_DEVMODE_INT(8, reserved2, 8, reserved2, __LINE__);
+ TEST_DEVMODE_INT(8, panningwidth, 8, panningwidth, __LINE__);
+ TEST_DEVMODE_INT(8, panningheight, 8, panningheight, __LINE__);
+
+ return ret;
+}
+
+static bool call_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ struct spoolss_DeviceMode *devmode,
+ struct policy_handle *handle);
+
+static bool test_PrinterInfo_DevModes(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *name)
+{
+ union spoolss_PrinterInfo info;
+ struct spoolss_DeviceMode *devmode;
+ struct spoolss_DeviceMode *devmode2;
+ struct policy_handle handle_devmode;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ /* simply compare level8 and level2 devmode */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), "");
+
+ devmode = info.info8.devmode;
+
+ if (devmode && devmode->size == 0) {
+ torture_fail(tctx,
+ "devmode of zero size!");
+ }
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ devmode2 = info.info2.devmode;
+
+ if (devmode2 && devmode2->size == 0) {
+ torture_fail(tctx,
+ "devmode of zero size!");
+ }
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "DM level 8 != DM level 2");
+
+
+ /* set devicemode level 8 and see if it persists */
+
+ devmode->copies = 93;
+ devmode->formname = talloc_strdup(tctx, "Legal");
+
+ torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 8, devmode), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), "");
+
+ devmode2 = info.info8.devmode;
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "modified DM level 8 != DM level 8 after DM has been set via level 8");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ devmode2 = info.info2.devmode;
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "modified DM level 8 != DM level 2");
+
+
+ /* set devicemode level 2 and see if it persists */
+
+ devmode->copies = 39;
+ devmode->formname = talloc_strdup(tctx, "Executive");
+
+ torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 2, devmode), "");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info), "");
+
+ devmode2 = info.info8.devmode;
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "modified DM level 8 != DM level 8 after DM has been set via level 2");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ devmode2 = info.info2.devmode;
+
+ torture_assert(tctx, test_devicemode_equal(tctx, devmode, devmode2),
+ "modified DM level 8 != DM level 2");
+
+
+ /* check every single bit in public part of devicemode */
+
+ torture_assert(tctx, test_devicemode_full(tctx, b, handle),
+ "failed to set every single devicemode component");
+
+
+ /* change formname upon open and see if it persists in getprinter calls */
+
+ devmode->formname = talloc_strdup(tctx, "A4");
+ devmode->copies = 42;
+
+ torture_assert(tctx, call_OpenPrinterEx(tctx, p, name, devmode, &handle_devmode),
+ "failed to open printer handle");
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, &handle_devmode, 8, &info), "");
+
+ devmode2 = info.info8.devmode;
+
+ if (strequal(devmode->devicename, devmode2->devicename)) {
+ torture_warning(tctx, "devicenames are the same\n");
+ } else {
+ torture_comment(tctx, "devicename passed in for open: %s\n", devmode->devicename);
+ torture_comment(tctx, "devicename after level 8 get: %s\n", devmode2->devicename);
+ }
+
+ if (strequal(devmode->formname, devmode2->formname)) {
+ torture_warning(tctx, "formname are the same\n");
+ } else {
+ torture_comment(tctx, "formname passed in for open: %s\n", devmode->formname);
+ torture_comment(tctx, "formname after level 8 get: %s\n", devmode2->formname);
+ }
+
+ if (devmode->copies == devmode2->copies) {
+ torture_warning(tctx, "copies are the same\n");
+ } else {
+ torture_comment(tctx, "copies passed in for open: %d\n", devmode->copies);
+ torture_comment(tctx, "copies after level 8 get: %d\n", devmode2->copies);
+ }
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, &handle_devmode, 2, &info), "");
+
+ devmode2 = info.info2.devmode;
+
+ if (strequal(devmode->devicename, devmode2->devicename)) {
+ torture_warning(tctx, "devicenames are the same\n");
+ } else {
+ torture_comment(tctx, "devicename passed in for open: %s\n", devmode->devicename);
+ torture_comment(tctx, "devicename after level 2 get: %s\n", devmode2->devicename);
+ }
+
+ if (strequal(devmode->formname, devmode2->formname)) {
+ torture_warning(tctx, "formname is the same\n");
+ } else {
+ torture_comment(tctx, "formname passed in for open: %s\n", devmode->formname);
+ torture_comment(tctx, "formname after level 2 get: %s\n", devmode2->formname);
+ }
+
+ if (devmode->copies == devmode2->copies) {
+ torture_warning(tctx, "copies are the same\n");
+ } else {
+ torture_comment(tctx, "copies passed in for open: %d\n", devmode->copies);
+ torture_comment(tctx, "copies after level 2 get: %d\n", devmode2->copies);
+ }
+
+ test_ClosePrinter(tctx, b, &handle_devmode);
+
+ return true;
+}
+
+/*
+ * wrapper call that saves original devmode, runs tests, and restores devmode
+ */
+
+static bool test_PrinterInfo_DevMode(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *name,
+ struct spoolss_DeviceMode *addprinter_devmode)
+{
+ union spoolss_PrinterInfo info;
+ struct spoolss_DeviceMode *devmode;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing Printer Devicemodes\n");
+
+ /* save original devmode */
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 8, &info),
+ "failed to get initial global devicemode");
+
+ devmode = info.info8.devmode;
+
+ if (devmode && devmode->size == 0) {
+ torture_fail(tctx,
+ "devmode of zero size!");
+ }
+
+ if (addprinter_devmode) {
+ if (!test_devicemode_equal(tctx, devmode, addprinter_devmode)) {
+ torture_warning(tctx, "current global DM is != DM provided in addprinter");
+ }
+ }
+
+ /* run tests */
+
+ ret = test_PrinterInfo_DevModes(tctx, p, handle, name);
+
+ /* restore original devmode */
+
+ torture_assert(tctx, test_devmode_set_level(tctx, b, handle, 8, devmode),
+ "failed to restore initial global device mode");
+
+ torture_comment(tctx, "Printer Devicemodes test %s\n\n",
+ ret ? "succeeded" : "failed");
+
+
+ return ret;
+}
+
+bool test_ClosePrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+ torture_assert_werr_ok(tctx, r.out.result, "ClosePrinter failed");
+
+ return true;
+}
+
+static bool test_GetForm_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level,
+ union spoolss_FormInfo *info_p)
+{
+ NTSTATUS status;
+ struct spoolss_GetForm r;
+ uint32_t needed;
+
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetForm(%s) level %d\n", form_name, r.in.level);
+
+ status = dcerpc_spoolss_GetForm_r(b, 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_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ status = dcerpc_spoolss_GetForm_r(b, 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");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_FormInfo, r.out.info, r.in.level, needed, 4);
+
+ if (info_p) {
+ *info_p = *r.out.info;
+ }
+
+ return true;
+}
+
+static bool test_GetForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level)
+{
+ return test_GetForm_args(tctx, b, handle, form_name, level, NULL);
+}
+
+static bool test_EnumForms(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_FormInfo **info_p)
+{
+ struct spoolss_EnumForms r;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_FormInfo *info;
+
+ r.in.handle = handle;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumForms level %d\n", r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumForms_r(b, tctx, &r),
+ "EnumForms failed");
+
+ if ((r.in.level == 2) && (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL))) {
+ torture_skip(tctx, "EnumForms level 2 not supported");
+ }
+
+ if (print_server && W_ERROR_EQUAL(r.out.result, WERR_INVALID_HANDLE)) {
+ torture_fail(tctx, "EnumForms on the PrintServer isn't supported by test server (NT4)");
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumForms_r(b, tctx, &r),
+ "EnumForms failed");
+
+ torture_assert(tctx, info, "No forms returned");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumForms failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumForms, info, r.in.level, count, needed, 4);
+
+ if (info_p) {
+ *info_p = info;
+ }
+ if (count_p) {
+ *count_p = count;
+ }
+
+ return true;
+}
+
+static bool test_EnumForms_all(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server)
+{
+ uint32_t levels[] = { 1, 2 };
+ int i, j;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ uint32_t count = 0;
+ union spoolss_FormInfo *info = NULL;
+
+ torture_assert(tctx,
+ test_EnumForms(tctx, b, handle, print_server, levels[i], &count, &info),
+ "failed to enum forms");
+
+ for (j = 0; j < count; j++) {
+ if (!print_server) {
+ torture_assert(tctx,
+ test_GetForm(tctx, b, handle, info[j].info1.form_name, levels[i]),
+ "failed to get form");
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_EnumForms_find_one(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *form_name)
+{
+ union spoolss_FormInfo *info = NULL;
+ uint32_t count = 0;
+ bool found = false;
+ int i;
+
+ torture_assert(tctx,
+ test_EnumForms(tctx, b, handle, print_server, 1, &count, &info),
+ "failed to enumerate forms");
+
+ for (i=0; i<count; i++) {
+ if (strequal(form_name, info[i].info1.form_name)) {
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static bool test_DeleteForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ WERROR expected_result)
+{
+ struct spoolss_DeleteForm r;
+
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+
+ torture_comment(tctx, "Testing DeleteForm(%s)\n", form_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeleteForm_r(b, tctx, &r),
+ "DeleteForm failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "DeleteForm gave unexpected result");
+ if (W_ERROR_IS_OK(r.out.result)) {
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeleteForm_r(b, tctx, &r),
+ "2nd DeleteForm failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_FORM_NAME,
+ "2nd DeleteForm failed");
+ }
+
+ return true;
+}
+
+static bool test_AddForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ union spoolss_AddFormInfo *info,
+ WERROR expected_result)
+{
+ struct spoolss_AddForm r;
+ struct spoolss_AddFormInfoCtr info_ctr;
+
+ info_ctr.level = level;
+ info_ctr.info = *info;
+
+ if (level != 1) {
+ torture_skip(tctx, "only level 1 supported");
+ }
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+
+ torture_comment(tctx, "Testing AddForm(%s) level %d, type %d\n",
+ r.in.info_ctr->info.info1->form_name, level,
+ r.in.info_ctr->info.info1->flags);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r),
+ "AddForm failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "AddForm gave unexpected result");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_AddForm_r(b, tctx, &r),
+ "2nd AddForm failed");
+ if (W_ERROR_EQUAL(expected_result, WERR_INVALID_PARAMETER)) {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "2nd AddForm gave unexpected result");
+ } else {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_FILE_EXISTS,
+ "2nd AddForm gave unexpected result");
+ }
+
+ return true;
+}
+
+static bool test_SetForm(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *form_name,
+ uint32_t level,
+ union spoolss_AddFormInfo *info)
+{
+ struct spoolss_SetForm r;
+ struct spoolss_AddFormInfoCtr info_ctr;
+
+ info_ctr.level = level;
+ info_ctr.info = *info;
+
+ r.in.handle = handle;
+ r.in.form_name = form_name;
+ r.in.info_ctr = &info_ctr;
+
+ torture_comment(tctx, "Testing SetForm(%s) level %d\n",
+ form_name, level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetForm_r(b, tctx, &r),
+ "SetForm failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "SetForm failed");
+
+ return true;
+}
+
+static bool test_GetForm_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *form_name,
+ enum winreg_Type *w_type,
+ uint32_t *w_size,
+ uint32_t *w_length,
+ uint8_t **w_data);
+
+static bool test_Forms_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *printer_name,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle,
+ const char *form_name,
+ struct spoolss_AddFormInfo1 *info1,
+ WERROR expected_add_result,
+ WERROR expected_delete_result)
+{
+ union spoolss_FormInfo info;
+ union spoolss_AddFormInfo add_info;
+
+ enum winreg_Type w_type;
+ uint32_t w_size;
+ uint32_t w_length;
+ uint8_t *w_data;
+
+ add_info.info1 = info1;
+
+ torture_assert(tctx,
+ test_AddForm(tctx, b, handle, 1, &add_info, expected_add_result),
+ "failed to add form");
+
+ if (winreg_handle && hive_handle && W_ERROR_IS_OK(expected_add_result)) {
+
+ struct spoolss_FormInfo1 i1;
+
+ torture_assert(tctx,
+ test_GetForm_winreg(tctx, winreg_handle, hive_handle, TOP_LEVEL_CONTROL_FORMS_KEY, form_name, &w_type, &w_size, &w_length, &w_data),
+ "failed to get form via winreg");
+
+ i1.size.width = IVAL(w_data, 0);
+ i1.size.height = IVAL(w_data, 4);
+ i1.area.left = IVAL(w_data, 8);
+ i1.area.top = IVAL(w_data, 12);
+ i1.area.right = IVAL(w_data, 16);
+ i1.area.bottom = IVAL(w_data, 20);
+ /* skip index here */
+ i1.flags = IVAL(w_data, 28);
+
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");
+ torture_assert_int_equal(tctx, w_size, 0x20, "unexpected size");
+ torture_assert_int_equal(tctx, w_length, 0x20, "unexpected length");
+ torture_assert_int_equal(tctx, i1.size.width, add_info.info1->size.width, "width mismatch");
+ torture_assert_int_equal(tctx, i1.size.height, add_info.info1->size.height, "height mismatch");
+ torture_assert_int_equal(tctx, i1.area.left, add_info.info1->area.left, "left mismatch");
+ torture_assert_int_equal(tctx, i1.area.top, add_info.info1->area.top, "top mismatch");
+ torture_assert_int_equal(tctx, i1.area.right, add_info.info1->area.right, "right mismatch");
+ torture_assert_int_equal(tctx, i1.area.bottom, add_info.info1->area.bottom, "bottom mismatch");
+ torture_assert_int_equal(tctx, i1.flags, add_info.info1->flags, "flags mismatch");
+ }
+
+ if (!print_server && W_ERROR_IS_OK(expected_add_result)) {
+ torture_assert(tctx,
+ test_GetForm_args(tctx, b, handle, form_name, 1, &info),
+ "failed to get added form");
+
+ torture_assert_int_equal(tctx, info.info1.size.width, add_info.info1->size.width, "width mismatch");
+ torture_assert_int_equal(tctx, info.info1.size.height, add_info.info1->size.height, "height mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.left, add_info.info1->area.left, "left mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.top, add_info.info1->area.top, "top mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.right, add_info.info1->area.right, "right mismatch");
+ torture_assert_int_equal(tctx, info.info1.area.bottom, add_info.info1->area.bottom, "bottom mismatch");
+ torture_assert_int_equal(tctx, info.info1.flags, add_info.info1->flags, "flags mismatch");
+
+ if (winreg_handle && hive_handle) {
+
+ struct spoolss_FormInfo1 i1;
+
+ i1.size.width = IVAL(w_data, 0);
+ i1.size.height = IVAL(w_data, 4);
+ i1.area.left = IVAL(w_data, 8);
+ i1.area.top = IVAL(w_data, 12);
+ i1.area.right = IVAL(w_data, 16);
+ i1.area.bottom = IVAL(w_data, 20);
+ /* skip index here */
+ i1.flags = IVAL(w_data, 28);
+
+ torture_assert_int_equal(tctx, i1.size.width, info.info1.size.width, "width mismatch");
+ torture_assert_int_equal(tctx, i1.size.height, info.info1.size.height, "height mismatch");
+ torture_assert_int_equal(tctx, i1.area.left, info.info1.area.left, "left mismatch");
+ torture_assert_int_equal(tctx, i1.area.top, info.info1.area.top, "top mismatch");
+ torture_assert_int_equal(tctx, i1.area.right, info.info1.area.right, "right mismatch");
+ torture_assert_int_equal(tctx, i1.area.bottom, info.info1.area.bottom, "bottom mismatch");
+ torture_assert_int_equal(tctx, i1.flags, info.info1.flags, "flags mismatch");
+ }
+
+ add_info.info1->size.width = 1234;
+
+ torture_assert(tctx,
+ test_SetForm(tctx, b, handle, form_name, 1, &add_info),
+ "failed to set form");
+ torture_assert(tctx,
+ test_GetForm_args(tctx, b, handle, form_name, 1, &info),
+ "failed to get set form");
+
+ torture_assert_int_equal(tctx, info.info1.size.width, add_info.info1->size.width, "width mismatch");
+ }
+
+ if (!W_ERROR_EQUAL(expected_add_result, WERR_INVALID_PARAMETER)) {
+ torture_assert(tctx,
+ test_EnumForms_find_one(tctx, b, handle, print_server, form_name),
+ "Newly added form not found in enum call");
+ }
+
+ torture_assert(tctx,
+ test_DeleteForm(tctx, b, handle, form_name, expected_delete_result),
+ "failed to delete form");
+
+ return true;
+}
+
+static bool test_Forms(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *printer_name,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ const struct spoolss_FormSize size = {
+ .width = 50,
+ .height = 25
+ };
+ const struct spoolss_FormArea area = {
+ .left = 5,
+ .top = 10,
+ .right = 45,
+ .bottom = 15
+ };
+ int i;
+
+ struct {
+ struct spoolss_AddFormInfo1 info1;
+ WERROR expected_add_result;
+ WERROR expected_delete_result;
+ } forms[] = {
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_USER,
+ .form_name = "testform_user",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_OK,
+ .expected_delete_result = WERR_OK
+ },
+/*
+ weird, we can add a builtin form but we can never remove it
+ again - gd
+
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_BUILTIN,
+ .form_name = "testform_builtin",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_OK,
+ .expected_delete_result = WERR_INVALID_PARAMETER,
+ },
+*/
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_PRINTER,
+ .form_name = "testform_printer",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_OK,
+ .expected_delete_result = WERR_OK
+ },
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_USER,
+ .form_name = "Letter",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_FILE_EXISTS,
+ .expected_delete_result = WERR_INVALID_PARAMETER
+ },
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_BUILTIN,
+ .form_name = "Letter",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_FILE_EXISTS,
+ .expected_delete_result = WERR_INVALID_PARAMETER
+ },
+ {
+ .info1 = {
+ .flags = SPOOLSS_FORM_PRINTER,
+ .form_name = "Letter",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_FILE_EXISTS,
+ .expected_delete_result = WERR_INVALID_PARAMETER
+ },
+ {
+ .info1 = {
+ .flags = 12345,
+ .form_name = "invalid_flags",
+ .size = size,
+ .area = area,
+ },
+ .expected_add_result = WERR_INVALID_PARAMETER,
+ .expected_delete_result = WERR_INVALID_FORM_NAME
+ }
+
+ };
+
+ for (i=0; i < ARRAY_SIZE(forms); i++) {
+ torture_assert(tctx,
+ test_Forms_args(tctx, b, handle, print_server, printer_name,
+ winreg_handle, hive_handle,
+ forms[i].info1.form_name,
+ &forms[i].info1,
+ forms[i].expected_add_result,
+ forms[i].expected_delete_result),
+ talloc_asprintf(tctx, "failed to test form '%s'", forms[i].info1.form_name));
+ }
+
+ return true;
+}
+
+static bool test_EnumPorts_old(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ NTSTATUS status;
+ struct spoolss_EnumPorts r;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PortInfo *info;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.servername = talloc_asprintf(tctx, "\\\\%s",
+ dcerpc_server_name(p));
+ r.in.level = 2;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPorts\n");
+
+ status = dcerpc_spoolss_EnumPorts_r(b, 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_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumPorts_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPorts failed");
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
+
+ torture_assert(tctx, info, "No ports returned");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPorts failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPorts, info, 2, count, needed, 4);
+
+ return true;
+}
+
+static bool test_AddPort(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ NTSTATUS status;
+ struct spoolss_AddPort r;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ uint32_t level,
+ union spoolss_JobInfo *info_p)
+{
+ NTSTATUS status;
+ struct spoolss_GetJob r;
+ union spoolss_JobInfo info;
+ uint32_t needed;
+
+ r.in.handle = handle;
+ r.in.job_id = job_id;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing GetJob(%d), level %d\n", job_id, r.in.level);
+
+ status = dcerpc_spoolss_GetJob_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+ if (level == 0) {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "Unexpected return code");
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_GetJob_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetJob failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetJob failed");
+ torture_assert(tctx, r.out.info, "No job info returned");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_JobInfo, r.out.info, r.in.level, needed, 4);
+
+ if (info_p) {
+ *info_p = *r.out.info;
+ }
+
+ return true;
+}
+
+#if 0
+static bool test_GetJob(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id)
+{
+ uint32_t levels[] = {0, 1, 2 /* 3, 4 */};
+ uint32_t i;
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+ torture_assert(tctx,
+ test_GetJob_args(tctx, b, handle, job_id, levels[i], NULL),
+ "GetJob failed");
+ }
+
+ return true;
+}
+#endif
+
+static bool test_SetJob(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ struct spoolss_JobInfoContainer *ctr,
+ enum spoolss_JobControl command)
+{
+ NTSTATUS status;
+ struct spoolss_SetJob r;
+
+ r.in.handle = handle;
+ r.in.job_id = job_id;
+ r.in.ctr = ctr;
+ r.in.command = command;
+
+ switch (command) {
+ case SPOOLSS_JOB_CONTROL_PAUSE:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_PAUSE\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_RESUME:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RESUME\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_CANCEL:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_CANCEL\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_RESTART:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RESTART\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_DELETE:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_DELETE\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_SEND_TO_PRINTER:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_SEND_TO_PRINTER\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_LAST_PAGE_EJECTED:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_LAST_PAGE_EJECTED\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_RETAIN:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RETAIN\n", job_id);
+ break;
+ case SPOOLSS_JOB_CONTROL_RELEASE:
+ torture_comment(tctx, "Testing SetJob(%d), SPOOLSS_JOB_CONTROL_RELEASE\n", job_id);
+ break;
+ default:
+ torture_comment(tctx, "Testing SetJob(%d)\n", job_id);
+ break;
+ }
+
+ status = dcerpc_spoolss_SetJob_r(b, 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_AddJob(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_AddJob r;
+ uint32_t needed;
+
+ r.in.level = 0;
+ r.in.handle = handle;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.in.buffer = r.out.buffer = NULL;
+
+ torture_comment(tctx, "Testing AddJob\n");
+
+ status = dcerpc_spoolss_AddJob_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "AddJob failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_LEVEL, "AddJob failed");
+
+ r.in.level = 1;
+
+ status = dcerpc_spoolss_AddJob_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "AddJob failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER, "AddJob failed");
+
+ return true;
+}
+
+
+static bool test_EnumJobs_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t level,
+ WERROR werr_expected,
+ uint32_t *count_p,
+ union spoolss_JobInfo **info_p)
+{
+ NTSTATUS status;
+ struct spoolss_EnumJobs r;
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_JobInfo *info;
+
+ r.in.handle = handle;
+ r.in.firstjob = 0;
+ r.in.numjobs = 0xffffffff;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumJobs level %d\n", level);
+
+ status = dcerpc_spoolss_EnumJobs_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ status = dcerpc_spoolss_EnumJobs_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
+ torture_assert_werr_equal(tctx, r.out.result, werr_expected,
+ "EnumJobs failed");
+ torture_assert(tctx, info, "No jobs returned");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumJobs, *r.out.info, r.in.level, count, needed, 4);
+
+ } else {
+ torture_assert_werr_equal(tctx, r.out.result, werr_expected,
+ "EnumJobs failed");
+ }
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static bool test_JobPropertiesEnum(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id)
+{
+ struct spoolss_EnumJobNamedProperties r;
+ uint32_t pcProperties = 0;
+ struct spoolss_PrintNamedProperty *ppProperties = NULL;
+
+ r.in.hPrinter = handle;
+ r.in.JobId = job_id;
+ r.out.pcProperties = &pcProperties;
+ r.out.ppProperties = &ppProperties;
+
+ torture_comment(tctx, "Testing EnumJobNamedProperties(%d)\n", job_id);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumJobNamedProperties_r(b, tctx, &r),
+ "spoolss_EnumJobNamedProperties failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_EnumJobNamedProperties failed");
+
+ return true;
+}
+
+static bool test_JobPropertySet(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ struct spoolss_PrintNamedProperty *property)
+{
+ struct spoolss_SetJobNamedProperty r;
+
+ r.in.hPrinter = handle;
+ r.in.JobId = job_id;
+ r.in.pProperty = property;
+
+ torture_comment(tctx, "Testing SetJobNamedProperty(%d) %s - %d\n",
+ job_id, property->propertyName,
+ property->propertyValue.ePropertyType);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_SetJobNamedProperty_r(b, tctx, &r),
+ "spoolss_SetJobNamedProperty failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_SetJobNamedProperty failed");
+
+ return true;
+}
+
+static bool test_JobPropertyGetValue(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ const char *property_name,
+ struct spoolss_PrintPropertyValue *value)
+{
+ struct spoolss_GetJobNamedPropertyValue r;
+
+ r.in.hPrinter = handle;
+ r.in.JobId = job_id;
+ r.in.pszName = property_name;
+ r.out.pValue = value;
+
+ torture_comment(tctx, "Testing GetJobNamedPropertyValue(%d) %s\n",
+ job_id, property_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetJobNamedPropertyValue_r(b, tctx, &r),
+ "spoolss_GetJobNamedPropertyValue failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_GetJobNamedPropertyValue failed");
+
+ return true;
+}
+
+static bool test_JobPropertyDelete(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id,
+ const char *property_name)
+{
+ struct spoolss_DeleteJobNamedProperty r;
+
+ r.in.hPrinter = handle;
+ r.in.JobId = job_id;
+ r.in.pszName = property_name;
+
+ torture_comment(tctx, "Testing DeleteJobNamedProperty(%d) %s\n",
+ job_id, property_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeleteJobNamedProperty_r(b, tctx, &r),
+ "spoolss_DeleteJobNamedProperty failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_DeleteJobNamedProperty failed");
+
+ return true;
+}
+
+static bool test_DoPrintTest_add_one_job_common(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *document_name,
+ const char *datatype,
+ uint32_t *job_id)
+{
+ NTSTATUS status;
+ struct spoolss_StartDocPrinter s;
+ struct spoolss_DocumentInfoCtr info_ctr;
+ struct spoolss_DocumentInfo1 info1;
+ struct spoolss_StartPagePrinter sp;
+ struct spoolss_WritePrinter w;
+ struct spoolss_EndPagePrinter ep;
+ struct spoolss_EndDocPrinter e;
+ int i;
+ uint32_t num_written;
+
+ torture_comment(tctx, "Testing StartDocPrinter\n");
+
+ s.in.handle = handle;
+ s.in.info_ctr = &info_ctr;
+ s.out.job_id = job_id;
+
+ info1.document_name = document_name;
+ info1.output_file = NULL;
+ info1.datatype = datatype;
+
+ info_ctr.level = 1;
+ info_ctr.info.info1 = &info1;
+
+ status = dcerpc_spoolss_StartDocPrinter_r(b, tctx, &s);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_StartDocPrinter failed");
+ torture_assert_werr_ok(tctx, s.out.result, "StartDocPrinter failed");
+
+ for (i=1; i < 4; i++) {
+ union spoolss_JobInfo ginfo;
+ bool ok;
+
+ torture_comment(tctx, "Testing StartPagePrinter: Page[%d], JobId[%d]\n", i, *job_id);
+
+ sp.in.handle = handle;
+
+ status = dcerpc_spoolss_StartPagePrinter_r(b, tctx, &sp);
+ torture_assert_ntstatus_ok(tctx, status,
+ "dcerpc_spoolss_StartPagePrinter failed");
+ torture_assert_werr_ok(tctx, sp.out.result, "StartPagePrinter failed");
+
+ ok = test_GetJob_args(tctx, b, handle, *job_id, 1, &ginfo);
+ if (!ok) {
+ torture_comment(tctx, "test_GetJob failed for JobId[%d]\n", *job_id);
+ }
+
+ torture_comment(tctx, "Testing WritePrinter: Page[%d], JobId[%d]\n", i, *job_id);
+
+ w.in.handle = handle;
+ w.in.data = data_blob_string_const(talloc_asprintf(tctx,"TortureTestPage: %d\nData\n",i));
+ w.out.num_written = &num_written;
+
+ status = dcerpc_spoolss_WritePrinter_r(b, 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], JobId[%d]\n", i, *job_id);
+
+ ep.in.handle = handle;
+
+ status = dcerpc_spoolss_EndPagePrinter_r(b, 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: JobId[%d]\n", *job_id);
+
+ e.in.handle = handle;
+
+ status = dcerpc_spoolss_EndDocPrinter_r(b, tctx, &e);
+ torture_assert_ntstatus_ok(tctx, status, "dcerpc_spoolss_EndDocPrinter failed");
+ torture_assert_werr_ok(tctx, e.out.result, "EndDocPrinter failed");
+
+ return true;
+}
+
+static bool test_DoPrintTest_add_one_job(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *document_name,
+ uint32_t *job_id)
+{
+ test_DoPrintTest_add_one_job_common(tctx, b, handle, document_name, "RAW", job_id);
+
+ return true;
+}
+
+static bool test_DoPrintTest_add_one_job_v4(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *document_name,
+ uint32_t *job_id)
+{
+ test_DoPrintTest_add_one_job_common(tctx, b, handle, document_name, "XPS_PASS", job_id);
+
+ return true;
+}
+
+
+static bool test_DoPrintTest_check_jobs(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t num_jobs,
+ uint32_t *job_ids)
+{
+ uint32_t count;
+ union spoolss_JobInfo *info = NULL;
+ int i;
+
+ torture_assert(tctx,
+ test_AddJob(tctx, b, handle),
+ "AddJob failed");
+
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, handle, 1, WERR_OK, &count, &info),
+ "EnumJobs level 1 failed");
+
+ torture_assert_int_equal(tctx, count, num_jobs, "unexpected number of jobs in queue");
+
+ for (i=0; i < num_jobs; i++) {
+ union spoolss_JobInfo ginfo;
+ const char *document_name;
+ const char *new_document_name = "any_other_docname";
+ struct spoolss_JobInfoContainer ctr;
+ struct spoolss_SetJobInfo1 info1;
+
+ torture_assert_int_equal(tctx, info[i].info1.job_id, job_ids[i], "job id mismatch");
+
+ torture_assert(tctx,
+ test_GetJob_args(tctx, b, handle, info[i].info1.job_id, 1, &ginfo),
+ "failed to call test_GetJob");
+
+ torture_assert_int_equal(tctx, ginfo.info1.job_id, info[i].info1.job_id, "job id mismatch");
+
+ document_name = ginfo.info1.document_name;
+
+ info1.job_id = ginfo.info1.job_id;
+ info1.printer_name = ginfo.info1.printer_name;
+ info1.server_name = ginfo.info1.server_name;
+ info1.user_name = ginfo.info1.user_name;
+ info1.document_name = new_document_name;
+ info1.data_type = ginfo.info1.data_type;
+ info1.text_status = ginfo.info1.text_status;
+ info1.status = ginfo.info1.status;
+ info1.priority = ginfo.info1.priority;
+ info1.position = ginfo.info1.position;
+ info1.total_pages = ginfo.info1.total_pages;
+ info1.pages_printed = ginfo.info1.pages_printed;
+ info1.submitted = ginfo.info1.submitted;
+
+ ctr.level = 1;
+ ctr.info.info1 = &info1;
+
+ torture_assert(tctx,
+ test_SetJob(tctx, b, handle, info[i].info1.job_id, &ctr, 0),
+ "failed to call test_SetJob level 1");
+
+ torture_assert(tctx,
+ test_GetJob_args(tctx, b, handle, info[i].info1.job_id, 1, &ginfo),
+ "failed to call test_GetJob");
+
+ if (strequal(ginfo.info1.document_name, document_name)) {
+ torture_warning(tctx,
+ "document_name did *NOT* change from '%s' to '%s'\n",
+ document_name, new_document_name);
+ }
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ if (!test_SetJob(tctx, b, handle, info[i].info1.job_id, NULL, SPOOLSS_JOB_CONTROL_PAUSE)) {
+ torture_warning(tctx, "failed to pause printjob\n");
+ }
+ if (!test_SetJob(tctx, b, handle, info[i].info1.job_id, NULL, SPOOLSS_JOB_CONTROL_RESUME)) {
+ torture_warning(tctx, "failed to resume printjob\n");
+ }
+ }
+
+ return true;
+}
+
+static bool test_DoPrintTest(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ bool ret = true;
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+
+ torture_comment(tctx, "Testing real print operations\n");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]);
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_DoPrintTest_add_one_job_v4(tctx, b, handle, "TorturePrintJob v4", &job_ids[i]);
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
+ }
+
+ if (ret == true) {
+ torture_comment(tctx, "real print operations test succeeded\n\n");
+ }
+
+ return ret;
+}
+
+static bool test_DoPrintTest_extended(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ bool ret = true;
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+ torture_comment(tctx, "Testing real print operations (extended)\n");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]);
+ }
+
+ ret &= test_DoPrintTest_check_jobs(tctx, b, handle, num_jobs, job_ids);
+
+ for (i=0; i < num_jobs; i++) {
+ ret &= test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE);
+ }
+
+ if (ret == true) {
+ torture_comment(tctx, "real print operations (extended) test succeeded\n\n");
+ }
+
+ return ret;
+}
+
+static bool test_JobPrintProperties_equal(struct torture_context *tctx,
+ struct spoolss_PrintPropertyValue *got,
+ struct spoolss_PrintNamedProperty *exp)
+{
+ torture_assert_int_equal(tctx,
+ got->ePropertyType,
+ exp->propertyValue.ePropertyType,
+ "ePropertyType");
+
+ switch (exp->propertyValue.ePropertyType) {
+ case kRpcPropertyTypeString:
+ torture_assert_str_equal(tctx,
+ got->value.propertyString,
+ exp->propertyValue.value.propertyString,
+ "propertyString");
+ break;
+ case kRpcPropertyTypeInt32:
+ torture_assert_int_equal(tctx,
+ got->value.propertyInt32,
+ exp->propertyValue.value.propertyInt32,
+ "propertyInt32");
+ break;
+ case kRpcPropertyTypeInt64:
+ torture_assert_u64_equal(tctx,
+ got->value.propertyInt64,
+ exp->propertyValue.value.propertyInt64,
+ "propertyInt64");
+ break;
+ case kRpcPropertyTypeByte:
+ torture_assert_int_equal(tctx,
+ got->value.propertyByte,
+ exp->propertyValue.value.propertyByte,
+ "propertyByte");
+ break;
+ case kRpcPropertyTypeBuffer:
+ torture_assert_int_equal(tctx,
+ got->value.propertyBlob.cbBuf,
+ exp->propertyValue.value.propertyBlob.cbBuf,
+ "propertyBlob.cbBuf");
+ torture_assert_mem_equal(tctx,
+ got->value.propertyBlob.pBuf,
+ exp->propertyValue.value.propertyBlob.pBuf,
+ exp->propertyValue.value.propertyBlob.cbBuf,
+ "propertyBlob.pBuf");
+
+ break;
+
+ }
+
+ return true;
+}
+
+static bool test_JobPrintProperties(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t job_id)
+{
+ struct spoolss_PrintNamedProperty in;
+ struct spoolss_PrintPropertyValue out;
+ int i;
+ DATA_BLOB blob = data_blob_string_const("blob");
+ struct {
+ const char *property_name;
+ enum spoolss_EPrintPropertyType type;
+ union spoolss_PrintPropertyValueUnion value;
+ WERROR expected_result;
+ } tests[] = {
+ {
+ .property_name = "torture_property_string",
+ .type = kRpcPropertyTypeString,
+ .value.propertyString = "torture_property_value_string",
+ },{
+ .property_name = "torture_property_int32",
+ .type = kRpcPropertyTypeInt32,
+ .value.propertyInt32 = 42,
+ },{
+ .property_name = "torture_property_int64",
+ .type = kRpcPropertyTypeInt64,
+ .value.propertyInt64 = 0xaffe,
+ },{
+ .property_name = "torture_property_byte",
+ .type = kRpcPropertyTypeByte,
+ .value.propertyByte = 0xab,
+ },{
+ .property_name = "torture_property_buffer",
+ .type = kRpcPropertyTypeBuffer,
+ .value.propertyBlob.cbBuf = blob.length,
+ .value.propertyBlob.pBuf = blob.data,
+ }
+ };
+
+ torture_assert(tctx,
+ test_JobPropertiesEnum(tctx, b, handle, job_id),
+ "failed to enum properties");
+
+ for (i=0; i <ARRAY_SIZE(tests); i++) {
+
+ in.propertyName = tests[i].property_name;
+ in.propertyValue.ePropertyType = tests[i].type;
+ in.propertyValue.value = tests[i].value;
+
+ torture_assert(tctx,
+ test_JobPropertySet(tctx, b, handle, job_id, &in),
+ "failed to set property");
+
+ torture_assert(tctx,
+ test_JobPropertyGetValue(tctx, b, handle, job_id, in.propertyName, &out),
+ "failed to get property");
+
+ torture_assert(tctx,
+ test_JobPrintProperties_equal(tctx, &out, &in),
+ "property unequal");
+
+ torture_assert(tctx,
+ test_JobPropertiesEnum(tctx, b, handle, job_id),
+ "failed to enum properties");
+
+ torture_assert(tctx,
+ test_JobPropertyDelete(tctx, b, handle, job_id, in.propertyName),
+ "failed to delete job property");
+ }
+
+ torture_assert(tctx,
+ test_JobPropertiesEnum(tctx, b, handle, job_id),
+ "failed to enum properties");
+
+ return true;
+}
+
+static bool test_DoPrintTest_properties(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+ torture_comment(tctx, "Testing real print operations (properties)\n");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+ for (i=0; i < num_jobs; i++) {
+ torture_assert(tctx,
+ test_DoPrintTest_add_one_job(tctx, b, handle, "TorturePrintJob", &job_ids[i]),
+ "failed to create print job");
+ }
+
+ for (i=0; i < num_jobs; i++) {
+ torture_assert(tctx,
+ test_JobPrintProperties(tctx, b, handle, job_ids[i]),
+ "failed to test job properties");
+ }
+
+
+ for (i=0; i < num_jobs; i++) {
+ torture_assert(tctx,
+ test_SetJob(tctx, b, handle, job_ids[i], NULL, SPOOLSS_JOB_CONTROL_DELETE),
+ "failed to delete printjob");
+ }
+
+ torture_comment(tctx, "real print operations (properties) test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_PausePrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info_ctr.level = 0;
+ info_ctr.info.info0 = NULL;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.in.command = SPOOLSS_PRINTER_CONTROL_PAUSE;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_PAUSE\n");
+
+ status = dcerpc_spoolss_SetPrinter_r(b, 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_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info_ctr.level = 0;
+ info_ctr.info.info0 = NULL;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.in.command = SPOOLSS_PRINTER_CONTROL_RESUME;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_RESUME\n");
+
+ status = dcerpc_spoolss_SetPrinter_r(b, 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_printer_purge(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinter r;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info_ctr.level = 0;
+ info_ctr.info.info0 = NULL;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.in.command = SPOOLSS_PRINTER_CONTROL_PURGE;
+
+ torture_comment(tctx, "Testing SetPrinter: SPOOLSS_PRINTER_CONTROL_PURGE\n");
+
+ status = dcerpc_spoolss_SetPrinter_r(b, 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_checktype(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type *expected_type,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterData r;
+ uint32_t needed;
+ enum winreg_Type type;
+ union spoolss_PrinterData data;
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.type = &type;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
+
+ torture_comment(tctx, "Testing GetPrinterData(%s)\n", r.in.value_name);
+
+ status = dcerpc_spoolss_GetPrinterData_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ if (expected_type) {
+ torture_assert_int_equal(tctx, type, *expected_type, "unexpected type");
+ }
+ r.in.offered = needed;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
+ status = dcerpc_spoolss_GetPrinterData_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterData failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "GetPrinterData(%s) failed", r.in.value_name));
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, needed, 1);
+
+ if (type_p) {
+ *type_p = type;
+ }
+
+ if (data_p) {
+ *data_p = r.out.data;
+ }
+
+ if (needed_p) {
+ *needed_p = needed;
+ }
+
+ return true;
+}
+
+static bool test_GetPrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ return test_GetPrinterData_checktype(tctx, b, handle, value_name,
+ NULL, type_p, data_p, needed_p);
+}
+
+static bool test_GetPrinterDataEx_checktype(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *value_name,
+ enum winreg_Type *expected_type,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterDataEx r;
+ enum winreg_Type type;
+ uint32_t needed;
+ union spoolss_PrinterData data;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.in.value_name = value_name;
+ r.in.offered = 0;
+ r.out.type = &type;
+ r.out.needed = &needed;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
+
+ torture_comment(tctx, "Testing GetPrinterDataEx(%s - %s)\n",
+ r.in.key_name, r.in.value_name);
+
+ status = dcerpc_spoolss_GetPrinterDataEx_r(b, tctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE)) {
+ 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)) {
+ if (expected_type) {
+ torture_assert_int_equal(tctx, type, *expected_type, "unexpected type");
+ }
+ r.in.offered = needed;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.offered);
+ status = dcerpc_spoolss_GetPrinterDataEx_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterDataEx failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ talloc_asprintf(tctx, "GetPrinterDataEx(%s - %s) failed", r.in.key_name, r.in.value_name));
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_PrinterData, &data, type, needed, 1);
+
+ if (type_p) {
+ *type_p = type;
+ }
+
+ if (data_p) {
+ *data_p = r.out.data;
+ }
+
+ if (needed_p) {
+ *needed_p = needed;
+ }
+
+ 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,
+ enum winreg_Type *type_p,
+ uint8_t **data_p,
+ uint32_t *needed_p)
+{
+ return test_GetPrinterDataEx_checktype(tctx, p, handle, key_name, value_name,
+ NULL, type_p, data_p, needed_p);
+}
+
+static bool test_get_environment(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char **architecture)
+{
+ DATA_BLOB blob;
+ enum winreg_Type type;
+ uint8_t *data;
+ uint32_t needed;
+
+ torture_assert(tctx,
+ test_GetPrinterData(tctx, b, handle, "Architecture", &type, &data, &needed),
+ "failed to get Architecture");
+
+ torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");
+
+ blob = data_blob_const(data, needed);
+ *architecture = reg_val_data_string(tctx, REG_SZ, blob);
+
+ return true;
+}
+
+static bool test_GetPrinterData_list(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *list[] = {
+ "W3SvcInstalled",
+ "BeepEnabled",
+ "EventLog",
+ /* "NetPopup", not on w2k8 */
+ /* "NetPopupToComputer", not on w2k8 */
+ "MajorVersion",
+ "MinorVersion",
+ "DefaultSpoolDirectory",
+ "Architecture",
+ "DsPresent",
+ "OSVersion",
+ /* "OSVersionEx", not on s3 */
+ "DNSMachineName"
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(list); i++) {
+ enum winreg_Type type = REG_NONE;
+ enum winreg_Type type_ex1 = REG_NONE;
+ enum winreg_Type type_ex2 = REG_NONE;
+ uint8_t *data;
+ uint8_t *data_ex1 = NULL;
+ uint8_t *data_ex2 = NULL;
+ uint32_t needed;
+ uint32_t needed_ex1 = 0;
+ uint32_t needed_ex2 = 0;
+
+ torture_assert(tctx, test_GetPrinterData(tctx, b, &ctx->server_handle, list[i], &type, &data, &needed),
+ talloc_asprintf(tctx, "GetPrinterData failed on %s\n", list[i]));
+ torture_assert(tctx, test_GetPrinterDataEx(tctx, p, &ctx->server_handle, "random_string", list[i], &type_ex1, &data_ex1, &needed_ex1),
+ talloc_asprintf(tctx, "GetPrinterDataEx failed on %s\n", list[i]));
+ torture_assert(tctx, test_GetPrinterDataEx(tctx, p, &ctx->server_handle, "", list[i], &type_ex2, &data_ex2, &needed_ex2),
+ talloc_asprintf(tctx, "GetPrinterDataEx failed on %s\n", list[i]));
+ torture_assert_int_equal(tctx, type, type_ex1, "type mismatch");
+ torture_assert_int_equal(tctx, type, type_ex2, "type mismatch");
+ torture_assert_int_equal(tctx, needed, needed_ex1, "needed mismatch");
+ torture_assert_int_equal(tctx, needed, needed_ex2, "needed mismatch");
+ torture_assert_mem_equal(tctx, data, data_ex1, needed, "data mismatch");
+ torture_assert_mem_equal(tctx, data, data_ex2, needed, "data mismatch");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterData(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ uint32_t enum_index,
+ uint32_t value_offered,
+ uint32_t data_offered,
+ enum winreg_Type *type_p,
+ uint32_t *value_needed_p,
+ uint32_t *data_needed_p,
+ const char **value_name_p,
+ uint8_t **data_p,
+ WERROR *result_p)
+{
+ struct spoolss_EnumPrinterData r;
+ uint32_t data_needed;
+ uint32_t value_needed;
+ enum winreg_Type type;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.handle = handle;
+ r.in.enum_index = enum_index;
+ r.in.value_offered = value_offered;
+ r.in.data_offered = data_offered;
+ r.out.data_needed = &data_needed;
+ r.out.value_needed = &value_needed;
+ r.out.type = &type;
+ r.out.data = talloc_zero_array(tctx, uint8_t, r.in.data_offered);
+ r.out.value_name = talloc_zero_array(tctx, const char, r.in.value_offered);
+
+ torture_comment(tctx, "Testing EnumPrinterData(%d)\n", enum_index);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinterData_r(b, tctx, &r),
+ "EnumPrinterData failed");
+
+ if (type_p) {
+ *type_p = type;
+ }
+ if (value_needed_p) {
+ *value_needed_p = value_needed;
+ }
+ if (data_needed_p) {
+ *data_needed_p = data_needed;
+ }
+ if (value_name_p) {
+ *value_name_p = r.out.value_name;
+ }
+ if (data_p) {
+ *data_p = r.out.data;
+ }
+ if (result_p) {
+ *result_p = r.out.result;
+ }
+
+ return true;
+}
+
+
+static bool test_EnumPrinterData_all(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ uint32_t enum_index = 0;
+ enum winreg_Type type;
+ uint32_t value_needed;
+ uint32_t data_needed;
+ uint8_t *data;
+ const char *value_name;
+ WERROR result;
+
+ torture_comment(tctx, "Testing EnumPrinterData\n");
+
+ do {
+ torture_assert(tctx,
+ test_EnumPrinterData(tctx, p, handle, enum_index, 0, 0,
+ &type, &value_needed, &data_needed,
+ &value_name, &data, &result),
+ "EnumPrinterData failed");
+
+ if (W_ERROR_EQUAL(result, WERR_NO_MORE_ITEMS)) {
+ break;
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterData(tctx, p, handle, enum_index, value_needed, data_needed,
+ &type, &value_needed, &data_needed,
+ &value_name, &data, &result),
+ "EnumPrinterData failed");
+
+ if (W_ERROR_EQUAL(result, WERR_NO_MORE_ITEMS)) {
+ break;
+ }
+
+ enum_index++;
+
+ } while (W_ERROR_IS_OK(result));
+
+ torture_comment(tctx, "EnumPrinterData test succeeded\n");
+
+ return true;
+}
+
+static bool test_EnumPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ uint32_t *count_p,
+ struct spoolss_PrinterEnumValues **info_p)
+{
+ struct spoolss_EnumPrinterDataEx r;
+ struct spoolss_PrinterEnumValues *info;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinterDataEx(%s)\n", key_name);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &r),
+ "EnumPrinterDataEx failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &r),
+ "EnumPrinterDataEx failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinterDataEx failed");
+
+ CHECK_NEEDED_SIZE_ENUM(spoolss_EnumPrinterDataEx, info, count, needed, 1);
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static bool test_SetPrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t offered);
+static bool test_DeletePrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name);
+
+static bool test_EnumPrinterData_consistency(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ uint32_t count;
+ struct spoolss_PrinterEnumValues *info;
+ int i;
+ uint32_t value_needed, data_needed;
+ uint32_t value_offered, data_offered;
+ WERROR result;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ enum winreg_Type type;
+ DATA_BLOB blob;
+
+ torture_comment(tctx, "Testing EnumPrinterData vs EnumPrinterDataEx consistency\n");
+
+ torture_assert(tctx, push_reg_sz(tctx, &blob, "torture_data1"), "");
+ type = REG_SZ;
+
+ torture_assert(tctx,
+ test_SetPrinterData(tctx, b, handle, "torture_value1", type, blob.data, blob.length),
+ "SetPrinterData failed");
+
+ blob = data_blob_string_const("torture_data2");
+
+ torture_assert(tctx,
+ test_SetPrinterData(tctx, b, handle, "torture_value2", REG_BINARY, blob.data, blob.length),
+ "SetPrinterData failed");
+
+ blob = data_blob_talloc(tctx, NULL, 4);
+ SIVAL(blob.data, 0, 0x11223344);
+
+ torture_assert(tctx,
+ test_SetPrinterData(tctx, b, handle, "torture_value3", type, blob.data, blob.length),
+ "SetPrinterData failed");
+
+ torture_assert(tctx,
+ test_EnumPrinterDataEx(tctx, b, handle, "PrinterDriverData", &count, &info),
+ "failed to call EnumPrinterDataEx");
+
+ /* get the max sizes for value and data */
+
+ torture_assert(tctx,
+ test_EnumPrinterData(tctx, p, handle, 0, 0, 0,
+ NULL, &value_needed, &data_needed,
+ NULL, NULL, &result),
+ "EnumPrinterData failed");
+ torture_assert_werr_ok(tctx, result, "unexpected result");
+
+ /* check if the reply from the EnumPrinterData really matches max values */
+
+ for (i=0; i < count; i++) {
+ if (info[i].value_name_len > value_needed) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "EnumPrinterDataEx gave a reply with value length %d which is larger then expected max value length %d from EnumPrinterData",
+ info[i].value_name_len, value_needed));
+ }
+ if (info[i].data_length > data_needed) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx,
+ "EnumPrinterDataEx gave a reply with data length %d which is larger then expected max data length %d from EnumPrinterData",
+ info[i].data_length, data_needed));
+ }
+ }
+
+ /* assuming that both EnumPrinterData and EnumPrinterDataEx do either
+ * sort or not sort the replies by value name, we should be able to do
+ * the following entry comparison */
+
+ data_offered = data_needed;
+ value_offered = value_needed;
+
+ for (i=0; i < count; i++) {
+
+ const char *value_name;
+ uint8_t *data;
+
+ torture_assert(tctx,
+ test_EnumPrinterData(tctx, p, handle, i, value_offered, data_offered,
+ &type, &value_needed, &data_needed,
+ &value_name, &data, &result),
+ "EnumPrinterData failed");
+
+ if (i -1 == count) {
+ torture_assert_werr_equal(tctx, result, WERR_NO_MORE_ITEMS,
+ "unexpected result");
+ break;
+ } else {
+ torture_assert_werr_ok(tctx, result, "unexpected result");
+ }
+
+ torture_assert_int_equal(tctx, type, info[i].type, "type mismatch");
+ torture_assert_int_equal(tctx, value_needed, info[i].value_name_len, "value name length mismatch");
+ torture_assert_str_equal(tctx, value_name, info[i].value_name, "value name mismatch");
+ torture_assert_int_equal(tctx, data_needed, info[i].data_length, "data length mismatch");
+ torture_assert_mem_equal(tctx, data, info[i].data->data, info[i].data_length, "data mismatch");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterData(tctx, b, handle, "torture_value1"),
+ "DeletePrinterData failed");
+ torture_assert(tctx,
+ test_DeletePrinterData(tctx, b, handle, "torture_value2"),
+ "DeletePrinterData failed");
+ torture_assert(tctx,
+ test_DeletePrinterData(tctx, b, handle, "torture_value3"),
+ "DeletePrinterData failed");
+
+ torture_comment(tctx, "EnumPrinterData vs EnumPrinterDataEx consistency test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_DeletePrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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(%s)\n",
+ r.in.value_name);
+
+ status = dcerpc_spoolss_DeletePrinterData_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "DeletePrinterData failed");
+ torture_assert_werr_ok(tctx, r.out.result, "DeletePrinterData failed");
+
+ return true;
+}
+
+static bool test_DeletePrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *value_name)
+{
+ struct spoolss_DeletePrinterDataEx r;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.in.value_name = value_name;
+
+ torture_comment(tctx, "Testing DeletePrinterDataEx(%s - %s)\n",
+ r.in.key_name, r.in.value_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterDataEx_r(b, tctx, &r),
+ "DeletePrinterDataEx failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "DeletePrinterDataEx failed");
+
+ return true;
+}
+
+static bool test_DeletePrinterKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name)
+{
+ struct spoolss_DeletePrinterKey r;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+
+ torture_comment(tctx, "Testing DeletePrinterKey(%s)\n", r.in.key_name);
+
+ if (strequal(key_name, "") && !torture_setting_bool(tctx, "dangerous", false)) {
+ torture_skip(tctx, "not wiping out printer registry - enable dangerous tests to use\n");
+ return true;
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterKey_r(b, tctx, &r),
+ "DeletePrinterKey failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "DeletePrinterKey failed");
+
+ return true;
+}
+
+static bool test_winreg_OpenHKLM(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct winreg_OpenHKLM r;
+
+ r.in.system_name = NULL;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = handle;
+
+ torture_comment(tctx, "Testing winreg_OpenHKLM\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenHKLM_r(b, tctx, &r), "OpenHKLM failed");
+ torture_assert_werr_ok(tctx, r.out.result, "OpenHKLM failed");
+
+ return true;
+}
+
+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_winreg_OpenKey_opts(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *hive_handle,
+ const char *keyname,
+ uint32_t options,
+ struct policy_handle *key_handle)
+{
+ struct winreg_OpenKey r;
+
+ r.in.parent_handle = hive_handle;
+ init_winreg_String(&r.in.keyname, keyname);
+ r.in.options = options;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = key_handle;
+
+ torture_comment(tctx, "Testing winreg_OpenKey(%s)\n", keyname);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenKey_r(b, tctx, &r), "OpenKey failed");
+ torture_assert_werr_ok(tctx, r.out.result, "OpenKey failed");
+
+ return true;
+}
+
+static bool test_winreg_OpenKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *hive_handle,
+ const char *keyname,
+ struct policy_handle *key_handle)
+{
+ return test_winreg_OpenKey_opts(tctx, b, hive_handle, keyname,
+ REG_OPTION_NON_VOLATILE, key_handle);
+}
+
+static bool test_winreg_CloseKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct winreg_CloseKey r;
+
+ r.in.handle = handle;
+ r.out.handle = handle;
+
+ torture_comment(tctx, "Testing winreg_CloseKey\n");
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CloseKey_r(b, tctx, &r), "CloseKey failed");
+ torture_assert_werr_ok(tctx, r.out.result, "CloseKey failed");
+
+ return true;
+}
+
+bool test_winreg_QueryValue(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type *type_p,
+ uint32_t *data_size_p,
+ uint32_t *data_length_p,
+ uint8_t **data_p)
+{
+ struct winreg_QueryValue r;
+ enum winreg_Type type = REG_NONE;
+ uint32_t data_size = 0;
+ uint32_t data_length = 0;
+ struct winreg_String valuename;
+ uint8_t *data = NULL;
+
+ init_winreg_String(&valuename, value_name);
+
+ data = talloc_zero_array(tctx, uint8_t, 0);
+
+ r.in.handle = handle;
+ r.in.value_name = &valuename;
+ r.in.type = &type;
+ r.in.data_size = &data_size;
+ r.in.data_length = &data_length;
+ r.in.data = data;
+ r.out.type = &type;
+ r.out.data = data;
+ r.out.data_size = &data_size;
+ r.out.data_length = &data_length;
+
+ torture_comment(tctx, "Testing winreg_QueryValue(%s)\n", value_name);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ *r.in.data_size = *r.out.data_size;
+ data = talloc_zero_array(tctx, uint8_t, *r.in.data_size);
+ r.in.data = data;
+ r.out.data = data;
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed");
+ }
+ torture_assert_werr_ok(tctx, r.out.result, "QueryValue failed");
+
+ if (type_p) {
+ *type_p = *r.out.type;
+ }
+ if (data_size_p) {
+ *data_size_p = *r.out.data_size;
+ }
+ if (data_length_p) {
+ *data_length_p = *r.out.data_length;
+ }
+ if (data_p) {
+ *data_p = r.out.data;
+ }
+
+ return true;
+}
+
+static bool test_winreg_query_printerdata(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *printer_name,
+ const char *key_name,
+ const char *value_name,
+ enum winreg_Type *w_type,
+ uint32_t *w_size,
+ uint32_t *w_length,
+ uint8_t **w_data)
+{
+ const char *printer_key;
+ struct policy_handle key_handle;
+
+ printer_key = talloc_asprintf(tctx, "%s\\%s\\%s",
+ TOP_LEVEL_PRINT_PRINTERS_KEY, printer_name, key_name);
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, b, handle, printer_key, &key_handle), "");
+
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, &key_handle, value_name, w_type, w_size, w_length, w_data), "");
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, b, &key_handle), "");
+
+ return true;
+}
+
+static bool test_GetForm_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *form_name,
+ enum winreg_Type *w_type,
+ uint32_t *w_size,
+ uint32_t *w_length,
+ uint8_t **w_data)
+{
+ struct policy_handle key_handle;
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, b, handle, key_name, &key_handle), "");
+
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, &key_handle, form_name, w_type, w_size, w_length, w_data), "");
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, b, &key_handle), "");
+
+ return true;
+}
+
+static bool test_winreg_symbolic_link(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *symlink_keyname,
+ const char *symlink_destination)
+{
+ /* check if the first key is a symlink to the second key */
+
+ enum winreg_Type w_type;
+ uint32_t w_size;
+ uint32_t w_length;
+ uint8_t *w_data;
+ struct policy_handle key_handle;
+ DATA_BLOB blob;
+ const char *str;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skip winreg symlink test against samba");
+ }
+
+ torture_assert(tctx,
+ test_winreg_OpenKey_opts(tctx, b, handle, symlink_keyname, REG_OPTION_OPEN_LINK, &key_handle),
+ "failed to open key link");
+
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, &key_handle,
+ "SymbolicLinkValue",
+ &w_type, &w_size, &w_length, &w_data),
+ "failed to query for 'SymbolicLinkValue' attribute");
+
+ torture_assert_int_equal(tctx, w_type, REG_LINK, "unexpected type");
+
+ blob = data_blob(w_data, w_size);
+ str = reg_val_data_string(tctx, REG_SZ, blob);
+
+ torture_assert_str_equal(tctx, str, symlink_destination, "unexpected symlink target string");
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, b, &key_handle),
+ "failed to close key link");
+
+ return true;
+}
+
+static const char *strip_unc(const char *unc)
+{
+ char *name;
+
+ if (!unc) {
+ return NULL;
+ }
+
+ if (unc[0] == '\\' && unc[1] == '\\') {
+ unc +=2;
+ }
+
+ name = strchr(unc, '\\');
+ if (name) {
+ return name+1;
+ }
+
+ return unc;
+}
+
+static bool test_GetPrinterInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *printer_name,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ union spoolss_PrinterInfo info;
+ const char *keys[] = {
+ TOP_LEVEL_CONTROL_PRINTERS_KEY,
+ TOP_LEVEL_PRINT_PRINTERS_KEY
+ };
+ int i;
+ const char *printername, *sharename;
+
+ torture_comment(tctx, "Testing Printer Info and winreg consistency\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to get printer info level 2");
+
+ printername = strip_unc(info.info2.printername);
+ sharename = strip_unc(info.info2.sharename);
+
+#define test_sz(wname, iname) \
+do {\
+ DATA_BLOB blob;\
+ const char *str;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_SZ, "unexpected type");\
+ blob = data_blob(w_data, w_size);\
+ str = reg_val_data_string(tctx, REG_SZ, blob);\
+ if (w_size == 2 && iname == NULL) {\
+ /*torture_comment(tctx, "%s: \"\", %s: (null)\n", #wname, #iname);\ */\
+ } else {\
+ torture_assert_str_equal(tctx, str, iname,\
+ talloc_asprintf(tctx, "%s - %s mismatch", #wname, #iname));\
+ }\
+} while(0);
+
+#define test_dword(wname, iname) \
+do {\
+ uint32_t value;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_DWORD, "unexpected type");\
+ torture_assert_int_equal(tctx, w_size, 4, "unexpected size");\
+ torture_assert_int_equal(tctx, w_length, 4, "unexpected length");\
+ value = IVAL(w_data, 0);\
+ torture_assert_int_equal(tctx, value, iname,\
+ talloc_asprintf(tctx, "%s - %s mismatch", #wname, #iname));\
+} while(0);
+
+#define test_binary(wname, iname) \
+do {\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
+ torture_assert_int_equal(tctx, w_size, iname.length, "unexpected length");\
+ torture_assert_mem_equal(tctx, w_data, iname.data, w_size, \
+ "binary unequal");\
+} while(0);
+
+
+#define test_dm(wname, iname) \
+do {\
+ DATA_BLOB blob;\
+ struct spoolss_DeviceMode dm;\
+ enum ndr_err_code ndr_err;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
+ blob = data_blob(w_data, w_size);\
+ ndr_err = ndr_pull_struct_blob(&blob, tctx, &dm,\
+ (ndr_pull_flags_fn_t)ndr_pull_spoolss_DeviceMode);\
+ torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall dm");\
+ torture_assert(tctx, test_devicemode_equal(tctx, &dm, iname),\
+ "dm unequal");\
+} while(0);
+
+#define test_sd(wname, iname) \
+do {\
+ DATA_BLOB blob;\
+ struct security_descriptor sd;\
+ enum ndr_err_code ndr_err;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "unexpected type");\
+ blob = data_blob(w_data, w_size);\
+ ndr_err = ndr_pull_struct_blob(&blob, tctx, &sd,\
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);\
+ torture_assert_ndr_success(tctx, ndr_err, "failed to unmarshall sd");\
+ torture_assert(tctx, test_security_descriptor_equal(tctx, &sd, iname),\
+ "sd unequal");\
+} while(0);
+
+#define test_multi_sz(wname, iname) \
+do {\
+ DATA_BLOB blob;\
+ const char **array;\
+ enum winreg_Type w_type;\
+ uint32_t w_size;\
+ uint32_t w_length;\
+ uint8_t *w_data;\
+ int i;\
+ torture_assert(tctx,\
+ test_winreg_QueryValue(tctx, winreg_handle, &key_handle, wname,\
+ &w_type, &w_size, &w_length, &w_data),\
+ "failed to query winreg");\
+ torture_assert_int_equal(tctx, w_type, REG_MULTI_SZ, "unexpected type");\
+ blob = data_blob(w_data, w_size);\
+ torture_assert(tctx, \
+ pull_reg_multi_sz(tctx, &blob, &array),\
+ "failed to pull multi sz");\
+ for (i=0; array[i] != NULL; i++) {\
+ torture_assert_str_equal(tctx, array[i], iname[i],\
+ talloc_asprintf(tctx, "%s - %s mismatch", #wname, iname[i]));\
+ }\
+} while(0);
+
+ if (!test_winreg_symbolic_link(tctx, winreg_handle, hive_handle,
+ TOP_LEVEL_CONTROL_PRINTERS_KEY,
+ "\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers"))
+ {
+ torture_warning(tctx, "failed to check for winreg symlink");
+ }
+
+ for (i=0; i < ARRAY_SIZE(keys); i++) {
+
+ const char *printer_key;
+ struct policy_handle key_handle;
+
+ printer_key = talloc_asprintf(tctx, "%s\\%s",
+ keys[i], printer_name);
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, winreg_handle, hive_handle, printer_key, &key_handle), "");
+
+ test_sz("Name", printername);
+ test_sz("Share Name", sharename);
+ test_sz("Port", info.info2.portname);
+ test_sz("Printer Driver", info.info2.drivername);
+ test_sz("Description", info.info2.comment);
+ test_sz("Location", info.info2.location);
+ test_sz("Separator File", info.info2.sepfile);
+ test_sz("Print Processor", info.info2.printprocessor);
+ test_sz("Datatype", info.info2.datatype);
+ test_sz("Parameters", info.info2.parameters);
+ /* winreg: 0, spoolss not */
+/* test_dword("Attributes", info.info2.attributes); */
+ test_dword("Priority", info.info2.priority);
+ test_dword("Default Priority", info.info2.defaultpriority);
+ /* winreg: 60, spoolss: 0 */
+/* test_dword("StartTime", info.info2.starttime); */
+/* test_dword("UntilTime", info.info2.untiltime); */
+ /* winreg != spoolss */
+/* test_dword("Status", info.info2.status); */
+ test_dm("Default DevMode", info.info2.devmode);
+ test_sd("Security", info.info2.secdesc);
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+ }
+
+#undef test_dm
+
+ torture_comment(tctx, "Printer Info and winreg consistency test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_GetPrintserverInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ union spoolss_PrinterInfo info;
+ struct policy_handle key_handle;
+
+ torture_comment(tctx,
+ "Testing Printserver Info and winreg consistency\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 3, &info),
+ "failed to get printer info level 2");
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, winreg_handle, hive_handle,
+ TOP_LEVEL_CONTROL_KEY, &key_handle), "");
+
+ test_sd("ServerSecurityDescriptor", info.info3.secdesc);
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+
+#undef test_sd
+
+ torture_comment(tctx,
+ "Printserver Info and winreg consistency test succeeded\n\n");
+
+ return true;
+}
+
+
+static bool test_PrintProcessors(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ union spoolss_PrintProcessorInfo *info;
+ uint32_t count;
+ int i;
+
+ torture_comment(tctx, "Testing Print Processor Info and winreg consistency\n");
+
+ torture_assert(tctx,
+ test_EnumPrintProcessors_level(tctx, b, environment, 1, &count, &info, WERR_OK),
+ "failed to enum print processors level 1");
+
+ for (i=0; i < count; i++) {
+
+ const char *processor_key;
+ struct policy_handle key_handle;
+
+ processor_key = talloc_asprintf(tctx, "%s\\%s\\Print Processors\\%s",
+ TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY,
+ environment,
+ info[i].info1.print_processor_name);
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, winreg_handle, hive_handle, processor_key, &key_handle), "");
+
+ /* nothing to check in there so far */
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+ }
+
+ torture_comment(tctx, "Print Processor Info and winreg consistency test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_GetPrinterDriver2_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name,
+ const char *architecture,
+ uint32_t level,
+ uint32_t client_major_version,
+ uint32_t client_minor_version,
+ union spoolss_DriverInfo *info_p,
+ WERROR *result);
+
+static const char *strip_path(const char *path)
+{
+ char *p;
+
+ if (path == NULL) {
+ return NULL;
+ }
+
+ p = strrchr(path, '\\');
+ if (p) {
+ return p+1;
+ }
+
+ return path;
+}
+
+static const char **strip_paths(const char **path_array)
+{
+ int i;
+
+ if (path_array == NULL) {
+ return NULL;
+ }
+
+ for (i=0; path_array[i] != NULL; i++) {
+ path_array[i] = strip_path(path_array[i]);
+ }
+
+ return path_array;
+}
+
+static const char *driver_winreg_date(TALLOC_CTX *mem_ctx, NTTIME nt)
+{
+ time_t t;
+ struct tm *tm;
+
+ if (nt == 0) {
+ return talloc_strdup(mem_ctx, "01/01/1601");
+ }
+
+ t = nt_time_to_unix(nt);
+ tm = localtime(&t);
+
+ return talloc_asprintf(mem_ctx, "%02d/%02d/%04d",
+ tm->tm_mon + 1, tm->tm_mday, tm->tm_year + 1900);
+}
+
+static const char *driver_winreg_version(TALLOC_CTX *mem_ctx, uint64_t v)
+{
+ return talloc_asprintf(mem_ctx, "%u.%u.%u.%u",
+ (unsigned)((v >> 48) & 0xFFFF),
+ (unsigned)((v >> 32) & 0xFFFF),
+ (unsigned)((v >> 16) & 0xFFFF),
+ (unsigned)(v & 0xFFFF));
+}
+
+static bool test_GetDriverInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *printer_name,
+ const char *driver_name,
+ const char *environment,
+ enum spoolss_DriverOSVersion version,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle,
+ const char *server_name_slash)
+{
+ WERROR result = WERR_OK;
+ union spoolss_DriverInfo info;
+ const char *driver_key;
+ struct policy_handle key_handle;
+
+ const char *driver_path;
+ const char *data_file;
+ const char *config_file;
+ const char *help_file;
+ const char **dependent_files;
+
+ const char *driver_date;
+ const char *inbox_driver_date;
+
+ const char *driver_version;
+ const char *inbox_driver_version;
+
+ ZERO_STRUCT(key_handle);
+
+ torture_comment(tctx, "Testing Driver Info and winreg consistency\n");
+
+ driver_key = talloc_asprintf(tctx, "%s\\%s\\Drivers\\Version-%d\\%s",
+ TOP_LEVEL_CONTROL_ENVIRONMENTS_KEY,
+ environment,
+ version,
+ driver_name);
+
+ torture_assert(tctx,
+ test_winreg_OpenKey(tctx, winreg_handle, hive_handle, driver_key, &key_handle),
+ "failed to open driver key");
+
+ if (torture_setting_bool(tctx, "samba3", false) ||
+ torture_setting_bool(tctx, "w2k3", false)) {
+ goto try_level6;
+ }
+
+ if (handle) {
+ torture_assert(tctx,
+ test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 8, version, 0, &info, &result),
+ "failed to get driver info level 8");
+ } else {
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 8, driver_name, &info),
+ "failed to get driver info level 8");
+ }
+
+ if (W_ERROR_EQUAL(result, WERR_INVALID_LEVEL)) {
+ goto try_level6;
+ }
+
+ driver_path = strip_path(info.info8.driver_path);
+ data_file = strip_path(info.info8.data_file);
+ config_file = strip_path(info.info8.config_file);
+ help_file = strip_path(info.info8.help_file);
+ dependent_files = strip_paths(info.info8.dependent_files);
+
+ driver_date = driver_winreg_date(tctx, info.info8.driver_date);
+ inbox_driver_date = driver_winreg_date(tctx, info.info8.min_inbox_driver_ver_date);
+
+ driver_version = driver_winreg_version(tctx, info.info8.driver_version);
+ inbox_driver_version = driver_winreg_version(tctx, info.info8.min_inbox_driver_ver_version);
+
+ test_sz("Configuration File", config_file);
+ test_sz("Data File", data_file);
+ test_sz("Datatype", info.info8.default_datatype);
+ test_sz("Driver", driver_path);
+ test_sz("DriverDate", driver_date);
+ test_sz("DriverVersion", driver_version);
+ test_sz("HardwareID", info.info8.hardware_id);
+ test_sz("Help File", help_file);
+ test_sz("InfPath", info.info8.inf_path);
+ test_sz("Manufacturer", info.info8.manufacturer_name);
+ test_sz("MinInboxDriverVerDate", inbox_driver_date);
+ test_sz("MinInboxDriverVerVersion", inbox_driver_version);
+ test_sz("Monitor", info.info8.monitor_name);
+ test_sz("OEM URL", info.info8.manufacturer_url);
+ test_sz("Print Processor", info.info8.print_processor);
+ test_sz("Provider", info.info8.provider);
+ test_sz("VendorSetup", info.info8.vendor_setup);
+ test_multi_sz("ColorProfiles", info.info8.color_profiles);
+ test_multi_sz("Dependent Files", dependent_files);
+ test_multi_sz("CoreDependencies", info.info8.core_driver_dependencies);
+ test_multi_sz("Previous Names", info.info8.previous_names);
+/* test_dword("Attributes", ?); */
+ test_dword("PrinterDriverAttributes", info.info8.printer_driver_attributes);
+ test_dword("Version", info.info8.version);
+/* test_dword("TempDir", ?); */
+
+ try_level6:
+
+ if (handle) {
+ torture_assert(tctx,
+ test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 6, version, 0, &info, &result),
+ "failed to get driver info level 6");
+ } else {
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 6, driver_name, &info),
+ "failed to get driver info level 6");
+ }
+
+ driver_path = strip_path(info.info6.driver_path);
+ data_file = strip_path(info.info6.data_file);
+ config_file = strip_path(info.info6.config_file);
+ help_file = strip_path(info.info6.help_file);
+ dependent_files = strip_paths(info.info6.dependent_files);
+
+ driver_date = driver_winreg_date(tctx, info.info6.driver_date);
+
+ driver_version = driver_winreg_version(tctx, info.info6.driver_version);
+
+ test_sz("Configuration File", config_file);
+ test_sz("Data File", data_file);
+ test_sz("Datatype", info.info6.default_datatype);
+ test_sz("Driver", driver_path);
+ if (torture_setting_bool(tctx, "w2k3", false)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, 8);
+ push_nttime(blob.data, 0, info.info6.driver_date);
+ test_binary("DriverDate", blob);
+ SBVAL(blob.data, 0, info.info6.driver_version);
+ test_binary("DriverVersion", blob);
+ } else {
+ test_sz("DriverDate", driver_date);
+ test_sz("DriverVersion", driver_version);
+ }
+ test_sz("HardwareID", info.info6.hardware_id);
+ test_sz("Help File", help_file);
+ test_sz("Manufacturer", info.info6.manufacturer_name);
+ test_sz("Monitor", info.info6.monitor_name);
+ test_sz("OEM URL", info.info6.manufacturer_url);
+ test_sz("Provider", info.info6.provider);
+ test_multi_sz("Dependent Files", dependent_files);
+ test_multi_sz("Previous Names", info.info6.previous_names);
+/* test_dword("Attributes", ?); */
+ test_dword("Version", info.info6.version);
+/* test_dword("TempDir", ?); */
+
+ if (handle) {
+ torture_assert(tctx,
+ test_GetPrinterDriver2_level(tctx, b, handle, driver_name, environment, 3, version, 0, &info, &result),
+ "failed to get driver info level 3");
+ } else {
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, environment, 3, driver_name, &info),
+ "failed to get driver info level 3");
+ }
+
+ driver_path = strip_path(info.info3.driver_path);
+ data_file = strip_path(info.info3.data_file);
+ config_file = strip_path(info.info3.config_file);
+ help_file = strip_path(info.info3.help_file);
+ dependent_files = strip_paths(info.info3.dependent_files);
+
+ test_sz("Configuration File", config_file);
+ test_sz("Data File", data_file);
+ test_sz("Datatype", info.info3.default_datatype);
+ test_sz("Driver", driver_path);
+ test_sz("Help File", help_file);
+ test_sz("Monitor", info.info3.monitor_name);
+ test_multi_sz("Dependent Files", dependent_files);
+/* test_dword("Attributes", ?); */
+ test_dword("Version", info.info3.version);
+/* test_dword("TempDir", ?); */
+
+
+ torture_assert(tctx,
+ test_winreg_CloseKey(tctx, winreg_handle, &key_handle), "");
+
+ torture_comment(tctx, "Driver Info and winreg consistency test succeeded\n\n");
+
+ return true;
+}
+
+#undef test_sz
+#undef test_dword
+
+static bool test_SetPrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t offered)
+{
+ struct spoolss_SetPrinterData r;
+
+ r.in.handle = handle;
+ r.in.value_name = value_name;
+ r.in.type = type;
+ r.in.data = data;
+ r.in.offered = offered;
+
+ torture_comment(tctx, "Testing SetPrinterData(%s)\n",
+ r.in.value_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_SetPrinterData_r(b, tctx, &r),
+ "SetPrinterData failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "SetPrinterData failed");
+
+ return true;
+}
+
+static bool test_SetPrinterData_matrix(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *printer_name,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ const char *values[] = {
+ "spootyfoot",
+ "spooty\\foot",
+#if 0
+ /* FIXME: not working with s3 atm. */
+ "spooty,foot",
+ "spooty,fo,ot",
+#endif
+ "spooty foot",
+#if 0
+ /* FIXME: not working with s3 atm. */
+ "spooty\\fo,ot",
+ "spooty,fo\\ot"
+#endif
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(values); i++) {
+
+ enum winreg_Type type, expected_type = REG_SZ;
+ DATA_BLOB blob;
+ uint8_t *data;
+ uint32_t needed;
+
+ torture_assert(tctx, push_reg_sz(tctx, &blob, "dog"), "");
+ type = REG_SZ;
+
+ torture_assert(tctx,
+ test_SetPrinterData(tctx, b, handle, values[i], REG_SZ, blob.data, blob.length),
+ "SetPrinterData failed");
+
+ torture_assert(tctx,
+ test_GetPrinterData_checktype(tctx, b, handle, values[i], &expected_type, &type, &data, &needed),
+ "GetPrinterData failed");
+
+ torture_assert_int_equal(tctx, type, REG_SZ, "type mismatch");
+ torture_assert_int_equal(tctx, needed, blob.length, "size mismatch");
+ torture_assert_mem_equal(tctx, data, blob.data, blob.length, "buffer mismatch");
+
+ if (winreg_handle && hive_handle) {
+
+ enum winreg_Type w_type;
+ uint32_t w_size;
+ uint32_t w_length;
+ uint8_t *w_data;
+
+ torture_assert(tctx,
+ test_winreg_query_printerdata(tctx, winreg_handle, hive_handle,
+ printer_name, "PrinterDriverData", values[i],
+ &w_type, &w_size, &w_length, &w_data), "");
+
+ torture_assert_int_equal(tctx, w_type, REG_SZ, "winreg type mismatch");
+ torture_assert_int_equal(tctx, w_size, blob.length, "winreg size mismatch");
+ torture_assert_int_equal(tctx, w_length, blob.length, "winreg length mismatch");
+ torture_assert_mem_equal(tctx, w_data, blob.data, blob.length, "winreg buffer mismatch");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterData(tctx, b, handle, values[i]),
+ "DeletePrinterData failed");
+ }
+
+ return true;
+}
+
+
+static bool test_EnumPrinterKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char ***array);
+
+static bool test_SetPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char *value_name,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t offered)
+{
+ NTSTATUS status;
+ struct spoolss_SetPrinterDataEx r;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.in.value_name = value_name;
+ r.in.type = type;
+ r.in.data = data;
+ r.in.offered = offered;
+
+ torture_comment(tctx, "Testing SetPrinterDataEx(%s - %s) type: %s, offered: 0x%08x\n",
+ r.in.key_name, r.in.value_name, str_regtype(r.in.type), r.in.offered);
+
+ status = dcerpc_spoolss_SetPrinterDataEx_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "SetPrinterDataEx failed");
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinterDataEx failed");
+
+ return true;
+}
+
+static bool test_SetPrinterDataEx_keys(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *value_name = "dog";
+ const char *keys[] = {
+ "torturedataex",
+ "torture data ex",
+ "torturedataex_with_subkey\\subkey",
+ "torturedataex_with_subkey\\subkey:0",
+ "torturedataex_with_subkey\\subkey:1",
+ "torturedataex_with_subkey\\subkey\\subsubkey",
+ "torturedataex_with_subkey\\subkey\\subsubkey:0",
+ "torturedataex_with_subkey\\subkey\\subsubkey:1",
+ "torture,data",
+ "torture,data,ex",
+ "torture,data\\ex",
+ "torture\\data,ex",
+ "torture/data",
+ "torture/data ex",
+ "torture/data ex/sub",
+ "torture//data",
+ "torture//data ex",
+ "torture//data ex/sub",
+ "torture//data ex//sub",
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(keys); i++) {
+
+ char *c;
+ const char *key;
+ enum winreg_Type type;
+ DATA_BLOB blob_in, blob_out;
+ const char **subkeys;
+ uint32_t ecount;
+ struct spoolss_PrinterEnumValues *einfo;
+ uint32_t needed;
+
+ blob_in = data_blob_talloc(tctx, NULL, 42);
+
+ generate_random_buffer(blob_in.data, blob_in.length);
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx(tctx, b, handle, keys[i], value_name, REG_BINARY, blob_in.data, blob_in.length),
+ "failed to call SetPrinterDataEx");
+
+ torture_assert(tctx,
+ test_GetPrinterDataEx(tctx, p, handle, keys[i], value_name, &type, &blob_out.data, &needed),
+ "failed to call GetPrinterDataEx");
+
+ blob_out.length = needed;
+ torture_assert(tctx,
+ test_EnumPrinterDataEx(tctx, b, handle, keys[i], &ecount, &einfo),
+ "failed to call EnumPrinterDataEx");
+
+ torture_assert_int_equal(tctx, type, REG_BINARY, "type mismatch");
+ torture_assert_int_equal(tctx, blob_out.length, blob_in.length, "size mismatch");
+ torture_assert_mem_equal(tctx, blob_out.data, blob_in.data, blob_in.length, "buffer mismatch");
+
+ torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count");
+ torture_assert_str_equal(tctx, einfo[0].value_name, value_name, "value_name mismatch");
+ torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(value_name)*2, "unexpected value_name_len");
+ torture_assert_int_equal(tctx, einfo[0].type, REG_BINARY, "type mismatch");
+ torture_assert_int_equal(tctx, einfo[0].data_length, blob_in.length, "size mismatch");
+ if (einfo[0].data_length > 0) {
+ torture_assert_mem_equal(tctx, einfo[0].data->data, blob_in.data, blob_in.length, "buffer mismatch");
+ }
+
+ key = talloc_strdup(tctx, keys[i]);
+
+ if (!test_DeletePrinterDataEx(tctx, b, handle, keys[i], value_name)) {
+ return false;
+ }
+
+ c = strchr(key, '\\');
+ if (c) {
+ int k;
+
+ /* we have subkeys */
+
+ *c = 0;
+
+ if (!test_EnumPrinterKey(tctx, b, handle, key, &subkeys)) {
+ return false;
+ }
+
+ for (k=0; subkeys && subkeys[k]; k++) {
+
+ const char *current_key = talloc_asprintf(tctx, "%s\\%s", key, subkeys[k]);
+
+ if (!test_DeletePrinterKey(tctx, b, handle, current_key)) {
+ return false;
+ }
+ }
+
+ if (!test_DeletePrinterKey(tctx, b, handle, key)) {
+ return false;
+ }
+
+ } else {
+ if (!test_DeletePrinterKey(tctx, b, handle, key)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_SetPrinterDataEx_values(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *key = "torturedataex";
+ const char *values[] = {
+ "torture_value",
+ "torture value",
+ "torture,value",
+ "torture/value",
+ "torture\\value",
+ "torture\\\\value"
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(values); i++) {
+
+ enum winreg_Type type = REG_NONE;
+ DATA_BLOB blob_in = data_blob_null;
+ DATA_BLOB blob_out = data_blob_null;
+ uint32_t ecount;
+ struct spoolss_PrinterEnumValues *einfo;
+ uint32_t needed = 0;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ char *q;
+ q = strrchr(values[i], ',');
+ if (q) {
+ torture_comment(tctx, "skipping valuename '%s' including ',' character against Samba3\n",
+ values[i]);
+ continue;
+ }
+ }
+
+ blob_in = data_blob_talloc(tctx, NULL, 42);
+
+ generate_random_buffer(blob_in.data, blob_in.length);
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx(tctx, b, handle, key, values[i], REG_BINARY, blob_in.data, blob_in.length),
+ "failed to call SetPrinterDataEx");
+
+ torture_assert(tctx,
+ test_GetPrinterDataEx(tctx, p, handle, key, values[i], &type, &blob_out.data, &needed),
+ "failed to call GetPrinterDataEx");
+
+ blob_out.length = needed;
+ torture_assert(tctx,
+ test_EnumPrinterDataEx(tctx, b, handle, key, &ecount, &einfo),
+ "failed to call EnumPrinterDataEx");
+
+ torture_assert_int_equal(tctx, type, REG_BINARY, "type mismatch");
+ torture_assert_int_equal(tctx, blob_out.length, blob_in.length, "size mismatch");
+ torture_assert_mem_equal(tctx, blob_out.data, blob_in.data, blob_in.length, "buffer mismatch");
+
+ torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count");
+ torture_assert_str_equal(tctx, einfo[0].value_name, values[i], "value_name mismatch");
+ torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(values[i])*2, "unexpected value_name_len");
+ torture_assert_int_equal(tctx, einfo[0].type, REG_BINARY, "type mismatch");
+ torture_assert_int_equal(tctx, einfo[0].data_length, blob_in.length, "size mismatch");
+ if (einfo[0].data_length > 0) {
+ torture_assert_mem_equal(tctx, einfo[0].data->data, blob_in.data, blob_in.length, "buffer mismatch");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDataEx(tctx, b, handle, key, values[i]),
+ "failed to call DeletePrinterDataEx");
+ }
+
+ return true;
+}
+
+
+static bool test_SetPrinterDataEx_matrix(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printername,
+ struct dcerpc_binding_handle *winreg_handle,
+ struct policy_handle *hive_handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *value_name = "dog";
+ const char *key_name = "torturedataex";
+ enum winreg_Type types[] = {
+ REG_SZ,
+ REG_MULTI_SZ,
+ REG_DWORD,
+ REG_BINARY
+ };
+ const char *str = "abcdefghi";
+ size_t t, s;
+
+ for (t=0; t < ARRAY_SIZE(types); t++) {
+ for (s=0; s < strlen(str); s++) {
+
+ enum winreg_Type type;
+ const char *string = talloc_strndup(tctx, str, s);
+ const char *array[2];
+ DATA_BLOB blob = data_blob_string_const(string);
+ DATA_BLOB data;
+ uint8_t *data_out;
+ uint32_t needed, offered = 0;
+ uint32_t ecount;
+ struct spoolss_PrinterEnumValues *einfo;
+
+ array[0] = talloc_strdup(tctx, string);
+ array[1] = NULL;
+
+ if (types[t] == REG_DWORD) {
+ s = 0xffff;
+ }
+
+ switch (types[t]) {
+ case REG_BINARY:
+ data = blob;
+ offered = blob.length;
+ break;
+ case REG_DWORD:
+ data = data_blob_talloc(tctx, NULL, 4);
+ SIVAL(data.data, 0, 0x12345678);
+ offered = 4;
+ break;
+ case REG_SZ:
+ torture_assert(tctx, push_reg_sz(tctx, &data, string), "");
+ type = REG_SZ;
+ offered = data.length;
+ /*strlen_m_term(data.string)*2;*/
+ break;
+ case REG_MULTI_SZ:
+ torture_assert(tctx, push_reg_multi_sz(tctx, &data, array), "");
+ type = REG_MULTI_SZ;
+ offered = data.length;
+ break;
+ default:
+ torture_fail(tctx, talloc_asprintf(tctx, "type %d untested\n", types[t]));
+ }
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx(tctx, b, handle, key_name, value_name, types[t], data.data, offered),
+ "failed to call SetPrinterDataEx");
+
+ torture_assert(tctx,
+ test_GetPrinterDataEx_checktype(tctx, p, handle, key_name, value_name, &types[t], &type, &data_out, &needed),
+ "failed to call GetPrinterDataEx");
+
+ torture_assert(tctx,
+ test_EnumPrinterDataEx(tctx, b, handle, key_name, &ecount, &einfo),
+ "failed to call EnumPrinterDataEx");
+
+ torture_assert_int_equal(tctx, types[t], type, "type mismatch");
+ torture_assert_int_equal(tctx, needed, offered, "size mismatch");
+ torture_assert_mem_equal(tctx, data_out, data.data, offered, "buffer mismatch");
+
+ torture_assert_int_equal(tctx, ecount, 1, "unexpected enum count");
+ torture_assert_str_equal(tctx, einfo[0].value_name, value_name, "value_name mismatch");
+ torture_assert_int_equal(tctx, einfo[0].value_name_len, strlen_m_term(value_name)*2, "unexpected value_name_len");
+ torture_assert_int_equal(tctx, einfo[0].type, types[t], "type mismatch");
+ torture_assert_int_equal(tctx, einfo[0].data_length, offered, "size mismatch");
+ if (einfo[0].data_length > 0) {
+ torture_assert_mem_equal(tctx, einfo[0].data->data, data.data, offered, "buffer mismatch");
+ }
+
+ if (winreg_handle && hive_handle) {
+ enum winreg_Type w_type;
+ uint32_t w_size;
+ uint32_t w_length;
+ uint8_t *w_data;
+
+ torture_assert(tctx,
+ test_winreg_query_printerdata(tctx, winreg_handle, hive_handle,
+ printername, key_name, value_name,
+ &w_type, &w_size, &w_length, &w_data), "");
+
+ torture_assert_int_equal(tctx, w_type, types[t], "winreg type mismatch");
+ torture_assert_int_equal(tctx, w_size, offered, "winreg size mismatch");
+ torture_assert_int_equal(tctx, w_length, offered, "winreg length mismatch");
+ torture_assert_mem_equal(tctx, w_data, data.data, offered, "winreg buffer mismatch");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDataEx(tctx, b, handle, key_name, value_name),
+ "failed to call DeletePrinterDataEx");
+ }
+ }
+
+ return true;
+}
+
+static bool test_PrinterData_winreg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printer_name)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret &= test_SetPrinterData_matrix(tctx, b, handle, printer_name, b2, &hive_handle);
+ ret &= test_SetPrinterDataEx_matrix(tctx, p, handle, printer_name, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_Forms_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ bool print_server,
+ const char *printer_name)
+{
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_Forms(tctx, b, handle, print_server, printer_name, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_PrinterInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printer_name)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_GetPrinterInfo_winreg(tctx, b, handle, printer_name, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_PrintserverInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_GetPrintserverInfo_winreg(tctx, b, handle, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+
+static bool test_DriverInfo_winreg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printer_name,
+ const char *driver_name,
+ const char *environment,
+ enum spoolss_DriverOSVersion version)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_GetDriverInfo_winreg(tctx, b, handle, printer_name, driver_name, environment, version, b2, &hive_handle, NULL);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_PrintProcessors_winreg(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment)
+{
+ struct dcerpc_pipe *p2;
+ bool ret = true;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_PrintProcessors(tctx, b, environment, b2, &hive_handle);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+
+ return ret;
+}
+
+static bool test_PrinterData_DsSpooler(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printer_name)
+{
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ union spoolss_SetPrinterInfo sinfo;
+ union spoolss_PrinterInfo info;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *pname;
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ torture_comment(tctx, "Testing DsSpooler <-> SetPrinter relations\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to query Printer level 2");
+
+ torture_assert(tctx,
+ PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo),
+ "failed to convert");
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+#define TEST_SZ(wname, iname) \
+do {\
+ enum winreg_Type type;\
+ uint8_t *data;\
+ uint32_t needed;\
+ DATA_BLOB blob;\
+ const char *str;\
+ torture_assert(tctx,\
+ test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+ "failed to query");\
+ torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\
+ blob = data_blob_const(data, needed);\
+ torture_assert(tctx,\
+ pull_reg_sz(tctx, &blob, &str),\
+ "failed to pull REG_SZ");\
+ torture_assert_str_equal(tctx, str, iname, "unexpected result");\
+} while(0);
+
+
+#define TEST_SET_SZ(wname, iname, val) \
+do {\
+ enum winreg_Type type;\
+ uint8_t *data;\
+ uint32_t needed;\
+ DATA_BLOB blob;\
+ const char *str;\
+ sinfo.info2->iname = val;\
+ torture_assert(tctx,\
+ test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\
+ "failed to call SetPrinter");\
+ torture_assert(tctx,\
+ test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+ "failed to query");\
+ torture_assert_int_equal(tctx, type, REG_SZ, "unexpected type");\
+ blob = data_blob_const(data, needed);\
+ torture_assert(tctx,\
+ pull_reg_sz(tctx, &blob, &str),\
+ "failed to pull REG_SZ");\
+ torture_assert_str_equal(tctx, str, val, "unexpected result");\
+} while(0);
+
+#define TEST_SET_DWORD(wname, iname, val) \
+do {\
+ enum winreg_Type type;\
+ uint8_t *data;\
+ uint32_t needed;\
+ uint32_t value;\
+ sinfo.info2->iname = val;\
+ torture_assert(tctx,\
+ test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),\
+ "failed to call SetPrinter");\
+ torture_assert(tctx,\
+ test_GetPrinterDataEx(tctx, p, handle, "DsSpooler", wname, &type, &data, &needed),\
+ "failed to query");\
+ torture_assert_int_equal(tctx, type, REG_DWORD, "unexpected type");\
+ torture_assert_int_equal(tctx, needed, 4, "unexpected length");\
+ value = IVAL(data, 0); \
+ torture_assert_int_equal(tctx, value, val, "unexpected result");\
+} while(0);
+
+ TEST_SET_SZ("description", comment, "newval");
+ TEST_SET_SZ("location", location, "newval");
+ TEST_SET_SZ("driverName", drivername, "newval");
+/* TEST_SET_DWORD("priority", priority, 25); */
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to query Printer level 2");
+
+ TEST_SZ("description", info.info2.comment);
+ TEST_SZ("driverName", info.info2.drivername);
+ TEST_SZ("location", info.info2.location);
+
+ pname = strrchr(info.info2.printername, '\\');
+ if (pname == NULL) {
+ pname = info.info2.printername;
+ } else {
+ pname++;
+ }
+ TEST_SZ("printerName", pname);
+ /* TEST_SZ("printSeparatorFile", info.info2.sepfile); */
+ /* TEST_SZ("printShareName", info.info2.sharename); */
+
+ /* FIXME gd: complete the list */
+
+#undef TEST_SZ
+#undef TEST_SET_SZ
+#undef TEST_DWORD
+
+ torture_comment(tctx, "DsSpooler <-> SetPrinter relations test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_print_processors_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_PrintProcessors_winreg(tctx, b, ctx->environment);
+}
+
+static bool test_AddPrintProcessor(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment,
+ const char *path_name,
+ const char *print_processor_name,
+ WERROR expected_error)
+{
+ struct spoolss_AddPrintProcessor r;
+
+ r.in.server = NULL;
+ r.in.architecture = environment;
+ r.in.path_name = path_name;
+ r.in.print_processor_name = print_processor_name;
+
+ torture_comment(tctx, "Testing AddPrintProcessor(%s)\n",
+ print_processor_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_AddPrintProcessor_r(b, tctx, &r),
+ "spoolss_AddPrintProcessor failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ "spoolss_AddPrintProcessor failed");
+
+ return true;
+}
+
+static bool test_DeletePrintProcessor(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *environment,
+ const char *print_processor_name,
+ WERROR expected_error)
+{
+ struct spoolss_DeletePrintProcessor r;
+
+ r.in.server = NULL;
+ r.in.architecture = environment;
+ r.in.print_processor_name = print_processor_name;
+
+ torture_comment(tctx, "Testing DeletePrintProcessor(%s)\n",
+ print_processor_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrintProcessor_r(b, tctx, &r),
+ "spoolss_DeletePrintProcessor failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ "spoolss_DeletePrintProcessor failed");
+
+ return true;
+}
+
+static bool test_add_print_processor(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ int i;
+
+ struct {
+ const char *environment;
+ const char *path_name;
+ const char *print_processor_name;
+ WERROR expected_add_result;
+ WERROR expected_del_result;
+ } tests[] = {
+ {
+ .environment = ctx->environment,
+ .path_name = "",
+ .print_processor_name = "winprint",
+ .expected_add_result = WERR_PRINT_PROCESSOR_ALREADY_INSTALLED,
+ .expected_del_result = WERR_CAN_NOT_COMPLETE
+ },{
+ .environment = ctx->environment,
+ .path_name = "",
+ .print_processor_name = "unknown",
+ .expected_add_result = WERR_MOD_NOT_FOUND,
+ .expected_del_result = WERR_UNKNOWN_PRINTPROCESSOR
+ }
+ };
+
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+ torture_assert(tctx,
+ test_AddPrintProcessor(tctx, b,
+ tests[i].environment,
+ tests[i].path_name,
+ tests[i].print_processor_name,
+ tests[i].expected_add_result),
+ "add print processor failed");
+ torture_assert(tctx,
+ test_DeletePrintProcessor(tctx, b,
+ tests[i].environment,
+ tests[i].print_processor_name,
+ tests[i].expected_del_result),
+ "delete print processor failed");
+ }
+
+ return true;
+}
+
+static bool test_AddPerMachineConnection(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ const char *printername,
+ const char *printserver,
+ const char *provider,
+ WERROR expected_error)
+{
+ struct spoolss_AddPerMachineConnection r;
+ const char *composed_printername = printername;
+
+ if (servername != NULL) {
+ composed_printername = talloc_asprintf(tctx, "%s\\%s",
+ servername,
+ printername);
+ }
+ r.in.server = servername;
+ r.in.printername = composed_printername;
+ r.in.printserver = printserver;
+ r.in.provider = provider;
+
+ torture_comment(tctx, "Testing AddPerMachineConnection(%s|%s|%s)\n",
+ printername, printserver, provider);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_AddPerMachineConnection_r(b, tctx, &r),
+ "spoolss_AddPerMachineConnection failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ "spoolss_AddPerMachineConnection failed");
+
+ return true;
+}
+
+static bool test_DeletePerMachineConnection(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ const char *printername,
+ WERROR expected_error)
+{
+ struct spoolss_DeletePerMachineConnection r;
+ const char *composed_printername = printername;
+
+ if (servername != NULL) {
+ composed_printername = talloc_asprintf(tctx, "%s\\%s",
+ servername,
+ printername);
+ }
+
+ r.in.server = servername;
+ r.in.printername = composed_printername;
+
+ torture_comment(tctx, "Testing DeletePerMachineConnection(%s)\n",
+ printername);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePerMachineConnection_r(b, tctx, &r),
+ "spoolss_DeletePerMachineConnection failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ "spoolss_DeletePerMachineConnection failed");
+
+ return true;
+}
+
+static bool test_EnumPerMachineConnections(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername)
+{
+ struct spoolss_EnumPerMachineConnections r;
+ DATA_BLOB blob = data_blob_null;
+ struct spoolss_PrinterInfo4 *info;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.server = servername;
+ r.in.buffer = &blob;
+ r.in.offered = 0;
+
+ r.out.info = &info;
+ r.out.needed = &needed;
+ r.out.count = &count;
+
+ torture_comment(tctx, "Testing EnumPerMachineConnections(%s)\n",
+ servername);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPerMachineConnections_r(b, tctx, &r),
+ "spoolss_EnumPerMachineConnections failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPerMachineConnections_r(b, tctx, &r),
+ "spoolss_EnumPerMachineConnections failed");
+ }
+ torture_assert_werr_ok(tctx, r.out.result,
+ "spoolss_EnumPerMachineConnections failed");
+
+ return true;
+}
+
+static bool test_addpermachineconnection(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ int i;
+
+ struct {
+ const char *servername;
+ const char *printername;
+ const char *printserver;
+ const char *provider;
+ WERROR expected_add_result;
+ WERROR expected_del_result;
+ } tests[] = {
+ {
+ .servername = NULL,
+ .printername = "foo",
+ .printserver = "",
+ .provider = "unknown",
+ .expected_add_result = WERR_INVALID_PRINTER_NAME,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = NULL,
+ .printername = "Microsoft Print to PDF",
+ .printserver = "samba.org",
+ .provider = "unknown",
+ .expected_add_result = WERR_INVALID_PRINTER_NAME,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = NULL,
+ .printername = "Microsoft Print to PDF",
+ .printserver = "samba.org",
+ .provider = "",
+ .expected_add_result = WERR_INVALID_PRINTER_NAME,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = server_name_slash,
+ .printername = "foo",
+ .printserver = "",
+ .provider = "unknown",
+ .expected_add_result = WERR_FILE_NOT_FOUND,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = server_name_slash,
+ .printername = "foo",
+ .printserver = "",
+ .provider = "",
+ .expected_add_result = WERR_OK,
+ .expected_del_result = WERR_OK
+ },{
+ .servername = server_name_slash,
+ .printername = "Microsoft Print to PDF",
+ .printserver = "samba.org",
+ .provider = "unknown",
+ .expected_add_result = WERR_FILE_NOT_FOUND,
+ .expected_del_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .servername = server_name_slash,
+ .printername = "Microsoft Print to PDF",
+ .printserver = "samba.org",
+ .provider = "",
+ .expected_add_result = WERR_OK,
+ .expected_del_result = WERR_OK
+ }
+ };
+
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+ torture_assert(tctx,
+ test_AddPerMachineConnection(tctx, b,
+ tests[i].servername,
+ tests[i].printername,
+ tests[i].printserver,
+ tests[i].provider,
+ tests[i].expected_add_result),
+ "add per machine connection failed");
+ torture_assert(tctx,
+ test_EnumPerMachineConnections(tctx, b,
+ tests[i].servername),
+ "enum per machine connections failed");
+ torture_assert(tctx,
+ test_DeletePerMachineConnection(tctx, b,
+ tests[i].servername,
+ tests[i].printername,
+ tests[i].expected_del_result),
+ "delete per machine connection failed");
+ torture_assert(tctx,
+ test_EnumPerMachineConnections(tctx, b,
+ tests[i].servername),
+ "enum per machine connections failed");
+ }
+
+ return true;
+}
+
+static bool test_GetChangeID_PrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t *change_id)
+{
+ enum winreg_Type type;
+ uint8_t *data;
+ uint32_t needed;
+
+ torture_assert(tctx,
+ test_GetPrinterData(tctx, b, handle, "ChangeID", &type, &data, &needed),
+ "failed to call GetPrinterData");
+
+ torture_assert(tctx, type == REG_DWORD, "unexpected type");
+ torture_assert_int_equal(tctx, needed, 4, "unexpected size");
+
+ *change_id = IVAL(data, 0);
+
+ return true;
+}
+
+static bool test_GetChangeID_PrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ uint32_t *change_id)
+{
+ enum winreg_Type type = REG_NONE;
+ uint8_t *data = NULL;
+ uint32_t needed = 0;
+
+ torture_assert(tctx,
+ test_GetPrinterDataEx(tctx, p, handle, "PrinterDriverData", "ChangeID", &type, &data, &needed),
+ "failed to call GetPrinterData");
+
+ torture_assert(tctx, type == REG_DWORD, "unexpected type");
+ torture_assert_int_equal(tctx, needed, 4, "unexpected size");
+
+ *change_id = IVAL(data, 0);
+
+ return true;
+}
+
+static bool test_GetChangeID_PrinterInfo(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ uint32_t *change_id)
+{
+ union spoolss_PrinterInfo info;
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 0, &info),
+ "failed to query Printer level 0");
+
+ *change_id = info.info0.change_id;
+
+ return true;
+}
+
+static bool test_ChangeID(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ uint32_t change_id, change_id_ex, change_id_info;
+ uint32_t change_id2, change_id_ex2, change_id_info2;
+ union spoolss_PrinterInfo info;
+ const char *comment;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing ChangeID: id change test #1\n");
+
+ torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
+ "failed to query for ChangeID");
+
+ torture_assert_int_equal(tctx, change_id, change_id_ex,
+ "change_ids should all be equal");
+ torture_assert_int_equal(tctx, change_id_ex, change_id_info,
+ "change_ids should all be equal");
+
+
+ torture_comment(tctx, "Testing ChangeID: id change test #2\n");
+
+ torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to query Printer level 2");
+ torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
+ "failed to query for ChangeID");
+ torture_assert_int_equal(tctx, change_id, change_id_ex,
+ "change_id should not have changed");
+ torture_assert_int_equal(tctx, change_id_ex, change_id_info,
+ "change_id should not have changed");
+
+
+ torture_comment(tctx, "Testing ChangeID: id change test #3\n");
+
+ torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "failed to query Printer level 2");
+ comment = talloc_strdup(tctx, info.info2.comment);
+
+ {
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ union spoolss_SetPrinterInfo sinfo;
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+
+ torture_assert(tctx, PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
+ sinfo.info2->comment = "torture_comment";
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+ torture_assert(tctx, test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter");
+
+ sinfo.info2->comment = comment;
+
+ torture_assert(tctx, test_SetPrinter(tctx, b, handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter");
+
+ }
+
+ torture_assert(tctx, test_GetChangeID_PrinterData(tctx, b, handle, &change_id2),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterDataEx(tctx, p, handle, &change_id_ex2),
+ "failed to query for ChangeID");
+ torture_assert(tctx, test_GetChangeID_PrinterInfo(tctx, b, handle, &change_id_info2),
+ "failed to query for ChangeID");
+
+ torture_assert_int_equal(tctx, change_id2, change_id_ex2,
+ "change_ids should all be equal");
+ torture_assert_int_equal(tctx, change_id_ex2, change_id_info2,
+ "change_ids should all be equal");
+
+ torture_assert(tctx, (change_id < change_id2),
+ talloc_asprintf(tctx, "change_id %d needs to be larger than change_id %d",
+ change_id2, change_id));
+ torture_assert(tctx, (change_id_ex < change_id_ex2),
+ talloc_asprintf(tctx, "change_id %d needs to be larger than change_id %d",
+ change_id_ex2, change_id_ex));
+ torture_assert(tctx, (change_id_info < change_id_info2),
+ talloc_asprintf(tctx, "change_id %d needs to be larger than change_id %d",
+ change_id_info2, change_id_info));
+
+ torture_comment(tctx, "ChangeID tests succeeded\n\n");
+
+ return true;
+}
+
+static bool test_SecondaryClosePrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct cli_credentials *anon_creds;
+ const struct dcerpc_binding *binding2;
+ 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");
+
+ anon_creds = cli_credentials_init_anon(tctx);
+ torture_assert(tctx, anon_creds != NULL, "cli_credentials_init_anon failed");
+
+ binding2 = p->binding;
+ status = dcerpc_secondary_auth_connection(p, binding2, &ndr_table_spoolss,
+ anon_creds, tctx->lp_ctx,
+ tctx, &p2);
+ torture_assert_ntstatus_ok(tctx, status, "Failed to create secondary connection");
+
+ cp.in.handle = handle;
+ cp.out.handle = handle;
+
+ status = dcerpc_spoolss_ClosePrinter_r(p2->binding_handle, tctx, &cp);
+ torture_assert_ntstatus_equal(tctx, status, NT_STATUS_RPC_SS_CONTEXT_MISMATCH,
+ "ERROR: Allowed close on secondary connection");
+
+ talloc_free(p2);
+
+ return true;
+}
+
+static bool test_OpenPrinter_badname(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b, 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, "Testing OpenPrinter(%s) with bad name\n", op.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinter_r(b, tctx, &op);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+ torture_assert_werr_equal(tctx, op.out.result, WERR_INVALID_PRINTER_NAME,
+ "unexpected result");
+
+ if (W_ERROR_IS_OK(op.out.result)) {
+ ret &=test_ClosePrinter(tctx, b, &handle);
+ }
+
+ opEx.in.printername = name;
+ opEx.in.datatype = NULL;
+ opEx.in.devmode_ctr.devmode = NULL;
+ opEx.in.access_mask = 0;
+ opEx.in.userlevel_ctr.level = 1;
+ opEx.in.userlevel_ctr.user_info.level1 = NULL;
+ opEx.out.handle = &handle;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s) with bad name\n", opEx.in.printername);
+
+ status = dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &opEx);
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinterEx failed");
+ torture_assert_werr_equal(tctx, opEx.out.result, WERR_INVALID_PARAMETER,
+ "unexpected result");
+
+ if (W_ERROR_IS_OK(opEx.out.result)) {
+ ret &=test_ClosePrinter(tctx, b, &handle);
+ }
+
+ return ret;
+}
+
+static bool test_OpenPrinter_badname_list(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ const char *badnames[] = {
+ "__INVALID_PRINTER__",
+ "\\\\__INVALID_HOST__",
+ "",
+ "\\\\\\",
+ "\\\\\\__INVALID_PRINTER__"
+ };
+ const char *badname;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ const char *server_name = dcerpc_server_name(p);
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(badnames); i++) {
+ torture_assert(tctx,
+ test_OpenPrinter_badname(tctx, b, badnames[i]),
+ "");
+ }
+
+ badname = talloc_asprintf(tctx, "\\\\%s\\", server_name);
+ torture_assert(tctx,
+ test_OpenPrinter_badname(tctx, b, badname),
+ "");
+
+ badname = talloc_asprintf(tctx, "\\\\%s\\__INVALID_PRINTER__", server_name);
+ torture_assert(tctx,
+ test_OpenPrinter_badname(tctx, b, badname),
+ "");
+
+ return true;
+}
+
+static bool test_OpenPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ const char *environment,
+ bool open_only)
+{
+ NTSTATUS status;
+ struct spoolss_OpenPrinter r;
+ struct policy_handle handle;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.printername = 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_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "OpenPrinter failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "OpenPrinter failed");
+
+ if (open_only) {
+ goto close_printer;
+ }
+
+ if (!test_GetPrinter(tctx, b, &handle, environment)) {
+ ret = false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+ }
+
+ close_printer:
+ if (!test_ClosePrinter(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *printername,
+ const char *datatype,
+ struct spoolss_DeviceMode *devmode,
+ uint32_t access_mask,
+ struct spoolss_UserLevelCtr *userlevel_ctr,
+ struct policy_handle *handle,
+ WERROR expected_result)
+{
+ struct spoolss_OpenPrinterEx r;
+
+ r.in.printername = printername;
+ r.in.datatype = datatype;
+ r.in.devmode_ctr.devmode= devmode;
+ r.in.access_mask = access_mask;
+ r.in.userlevel_ctr = *userlevel_ctr;
+ r.out.handle = handle;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s)\n", r.in.printername);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r),
+ "OpenPrinterEx failed");
+
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "OpenPrinterEx failed");
+
+ return true;
+}
+
+static bool call_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ struct spoolss_DeviceMode *devmode,
+ struct policy_handle *handle)
+{
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct spoolss_UserLevel1 userlevel1;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ userlevel1.size = 1234;
+ userlevel1.client = "hello";
+ userlevel1.user = "spottyfoot!";
+ userlevel1.build = 1;
+ userlevel1.major = 2;
+ userlevel1.minor = 3;
+ userlevel1.processor = 4;
+
+ userlevel_ctr.level = 1;
+ userlevel_ctr.user_info.level1 = &userlevel1;
+
+ return test_OpenPrinterEx(tctx, b, name, NULL, devmode,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &userlevel_ctr,
+ handle,
+ WERR_OK);
+}
+
+static bool test_printer_rename(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ bool ret = true;
+ union spoolss_PrinterInfo info;
+ union spoolss_SetPrinterInfo sinfo;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ const char *printer_name;
+ const char *printer_name_orig;
+ const char *printer_name_new = "SAMBA smbtorture Test Printer (Copy 2)";
+ struct policy_handle new_handle;
+ const char *q;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ torture_comment(tctx, "Testing Printer rename operations\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 2, &info),
+ "failed to call GetPrinter level 2");
+
+ printer_name_orig = talloc_strdup(tctx, info.info2.printername);
+
+ q = strrchr(info.info2.printername, '\\');
+ if (q) {
+ torture_warning(tctx,
+ "server returns printername %s incl. servername although we did not set servername", info.info2.printername);
+ }
+
+ torture_assert(tctx,
+ PrinterInfo_to_SetPrinterInfo(tctx, &info, 2, &sinfo), "");
+
+ sinfo.info2->printername = printer_name_new;
+
+ info_ctr.level = 2;
+ info_ctr.info = sinfo;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, &t->handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter level 2");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 2, &info),
+ "failed to call GetPrinter level 2");
+
+ printer_name = talloc_strdup(tctx, info.info2.printername);
+
+ q = strrchr(info.info2.printername, '\\');
+ if (q) {
+ torture_warning(tctx,
+ "server returns printername %s incl. servername although we did not set servername", info.info2.printername);
+ q++;
+ printer_name = q;
+ }
+
+ torture_assert_str_equal(tctx, printer_name, printer_name_new,
+ "new printer name was not set");
+
+ /* samba currently cannot fully rename printers */
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ torture_assert(tctx,
+ test_OpenPrinter_badname(tctx, b, printer_name_orig),
+ "still can open printer with oldname after rename");
+ } else {
+ torture_warning(tctx, "*not* checking for open with oldname after rename for samba3");
+ }
+
+ torture_assert(tctx,
+ call_OpenPrinterEx(tctx, p, printer_name_new, NULL, &new_handle),
+ "failed to open printer with new name");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &new_handle, 2, &info),
+ "failed to call GetPrinter level 2");
+
+ torture_assert_str_equal(tctx, info.info2.printername, printer_name_new,
+ "new printer name was not set");
+
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &new_handle),
+ "failed to close printer");
+
+ torture_comment(tctx, "Printer rename operations test succeeded\n\n");
+
+ return ret;
+}
+
+static bool test_openprinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *real_printername)
+{
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct policy_handle handle;
+ struct spoolss_UserLevel1 userlevel1;
+ const char *printername = NULL;
+ int i;
+
+ struct {
+ const char *suffix;
+ WERROR expected_result;
+ } tests[] = {
+ {
+ .suffix = "rubbish",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", LocalOnl",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", localOnly",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", localonl",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",LocalOnl",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",localOnl2",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", DrvConver2t",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", drvconvert",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",drvconvert",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ", DrvConvert",
+ .expected_result = WERR_OK
+ },{
+ .suffix = " , DrvConvert",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",DrvConvert",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ", DrvConvertsadfasdf",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ",DrvConvertasdfasd",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ", LocalOnly",
+ .expected_result = WERR_OK
+ },{
+ .suffix = " , LocalOnly",
+ .expected_result = WERR_INVALID_PRINTER_NAME
+ },{
+ .suffix = ",LocalOnly",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ", LocalOnlysagi4gjfkd",
+ .expected_result = WERR_OK
+ },{
+ .suffix = ",LocalOnlysagi4gjfkd",
+ .expected_result = WERR_OK
+ }
+ };
+
+ userlevel1.size = 1234;
+ userlevel1.client = "hello";
+ userlevel1.user = "spottyfoot!";
+ userlevel1.build = 1;
+ userlevel1.major = 2;
+ userlevel1.minor = 3;
+ userlevel1.processor = 4;
+
+ userlevel_ctr.level = 1;
+ userlevel_ctr.user_info.level1 = &userlevel1;
+
+ torture_comment(tctx, "Testing openprinterex printername pattern\n");
+
+ torture_assert(tctx,
+ test_OpenPrinterEx(tctx, b, real_printername, NULL, NULL, 0,
+ &userlevel_ctr, &handle,
+ WERR_OK),
+ "OpenPrinterEx failed");
+ test_ClosePrinter(tctx, b, &handle);
+
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+
+ printername = talloc_asprintf(tctx, "%s%s",
+ real_printername,
+ tests[i].suffix);
+
+ torture_assert(tctx,
+ test_OpenPrinterEx(tctx, b, printername, NULL, NULL, 0,
+ &userlevel_ctr, &handle,
+ tests[i].expected_result),
+ "OpenPrinterEx failed");
+ if (W_ERROR_IS_OK(tests[i].expected_result)) {
+ test_ClosePrinter(tctx, b, &handle);
+ }
+ }
+
+ return true;
+}
+
+
+static bool test_existing_printer_openprinterex(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ const char *environment)
+{
+ struct policy_handle handle;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_openprinter(tctx, b, name)) {
+ return false;
+ }
+
+ if (!call_OpenPrinterEx(tctx, p, name, NULL, &handle)) {
+ return false;
+ }
+
+ if (!test_PrinterInfo_SD(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_GetPrinter(tctx, b, &handle, environment)) {
+ ret = false;
+ }
+
+ if (!test_EnumForms_all(tctx, b, &handle, false)) {
+ ret = false;
+ }
+
+ if (!test_Forms(tctx, b, &handle, false, name, NULL, NULL)) {
+ ret = false;
+ }
+
+ if (!test_Forms_winreg(tctx, b, &handle, false, name)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterData_all(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterDataEx(tctx, b, &handle, "PrinterDriverData", NULL, NULL)) {
+ ret = false;
+ }
+
+ if (!test_EnumPrinterData_consistency(tctx, p, &handle)) {
+ ret = false;
+ }
+
+ if (!test_printer_all_keys(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_PausePrinter(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_DoPrintTest(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_ResumePrinter(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ if (!test_SetPrinterData_matrix(tctx, b, &handle, name, NULL, NULL)) {
+ ret = false;
+ }
+
+ if (!test_SetPrinterDataEx_matrix(tctx, p, &handle, name, NULL, NULL)) {
+ ret = false;
+ }
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ if (!test_SecondaryClosePrinter(tctx, p, &handle)) {
+ ret = false;
+ }
+ }
+
+ if (!test_ClosePrinter(tctx, b, &handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrinters_old(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct spoolss_EnumPrinters r;
+ NTSTATUS status;
+ uint16_t levels[] = {1, 2, 4, 5};
+ int i;
+ bool ret = true;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ union spoolss_PrinterInfo *info;
+ int j;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = "";
+ r.in.level = levels[i];
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinters level %u\n", r.in.level);
+
+ status = dcerpc_spoolss_EnumPrinters_r(b, 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_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ status = dcerpc_spoolss_EnumPrinters_r(b, tctx, &r);
+ }
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinters failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
+
+ if (!info) {
+ torture_comment(tctx, "No printers returned\n");
+ return true;
+ }
+
+ for (j=0;j<count;j++) {
+ if (r.in.level == 1) {
+ char *unc = talloc_strdup(tctx, info[j].info1.name);
+ char *slash, *name, *full_name;
+ name = unc;
+ if (unc[0] == '\\' && unc[1] == '\\') {
+ unc +=2;
+ }
+ slash = strchr(unc, '\\');
+ if (slash) {
+ slash++;
+ name = slash;
+ }
+ full_name = talloc_asprintf(tctx, "\\\\%s\\%s",
+ dcerpc_server_name(p), name);
+ if (!test_OpenPrinter(tctx, p, name, ctx->environment, true)) {
+ ret = false;
+ }
+ if (!test_OpenPrinter(tctx, p, full_name, ctx->environment, true)) {
+ ret = false;
+ }
+ if (!test_OpenPrinter(tctx, p, name, ctx->environment, false)) {
+ ret = false;
+ }
+ if (!test_existing_printer_openprinterex(tctx, p, name, ctx->environment)) {
+ ret = false;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrinters_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t flags,
+ const char *servername,
+ uint32_t level,
+ uint32_t *count_p,
+ union spoolss_PrinterInfo **info_p)
+{
+ struct spoolss_EnumPrinters r;
+ union spoolss_PrinterInfo *info;
+ uint32_t needed;
+ uint32_t count;
+
+ r.in.flags = flags;
+ r.in.server = servername;
+ r.in.level = level;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+ r.out.count = &count;
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing EnumPrinters(%s) level %u\n",
+ r.in.server, r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+ "EnumPrinters failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+ "EnumPrinters failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "EnumPrinters failed");
+
+ CHECK_NEEDED_SIZE_ENUM_LEVEL(spoolss_EnumPrinters, info, r.in.level, count, needed, 4);
+
+ if (count_p) {
+ *count_p = count;
+ }
+ if (info_p) {
+ *info_p = info;
+ }
+
+ return true;
+}
+
+static const char *get_short_printername(struct torture_context *tctx,
+ const char *name)
+{
+ const char *short_name;
+
+ if (name[0] == '\\' && name[1] == '\\') {
+ name += 2;
+ short_name = strchr(name, '\\');
+ if (short_name) {
+ return talloc_strdup(tctx, short_name+1);
+ }
+ }
+
+ return name;
+}
+
+static const char *get_full_printername(struct torture_context *tctx,
+ const char *name)
+{
+ const char *full_name = talloc_strdup(tctx, name);
+ char *p;
+
+ if (name && name[0] == '\\' && name[1] == '\\') {
+ name += 2;
+ p = strchr(name, '\\');
+ if (p) {
+ return full_name;
+ }
+ }
+
+ return NULL;
+}
+
+static bool test_OnePrinter_servername(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ const char *printername)
+{
+ union spoolss_PrinterInfo info;
+ const char *short_name = get_short_printername(tctx, printername);
+ const char *full_name = get_full_printername(tctx, printername);
+
+ if (short_name) {
+ struct policy_handle handle;
+ torture_assert(tctx,
+ call_OpenPrinterEx(tctx, p, short_name, NULL, &handle),
+ "failed to open printer");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &handle, 2, &info),
+ "failed to get printer info");
+
+ torture_assert_casestr_equal(tctx, info.info2.servername, NULL,
+ "unexpected servername");
+ torture_assert_casestr_equal(tctx, info.info2.printername, short_name,
+ "unexpected printername");
+
+ if (info.info2.devmode) {
+ const char *expected_devicename;
+ expected_devicename = talloc_strndup(tctx, short_name, MIN(strlen(short_name), 31));
+ torture_assert_casestr_equal(tctx, info.info2.devmode->devicename, expected_devicename,
+ "unexpected devicemode devicename");
+ }
+
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &handle),
+ "failed to close printer");
+ }
+
+ if (full_name) {
+ struct policy_handle handle;
+
+ torture_assert(tctx,
+ call_OpenPrinterEx(tctx, p, full_name, NULL, &handle),
+ "failed to open printer");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &handle, 2, &info),
+ "failed to get printer info");
+
+ torture_assert_casestr_equal(tctx, info.info2.servername, servername,
+ "unexpected servername");
+ torture_assert_casestr_equal(tctx, info.info2.printername, full_name,
+ "unexpected printername");
+
+ if (info.info2.devmode) {
+ const char *expected_devicename;
+ expected_devicename = talloc_strndup(tctx, full_name, MIN(strlen(full_name), 31));
+ torture_assert_casestr_equal(tctx, info.info2.devmode->devicename, expected_devicename,
+ "unexpected devicemode devicename");
+ }
+
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &handle),
+ "failed to close printer");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinters_servername(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+ const char *servername;
+ uint32_t flags = PRINTER_ENUM_NAME|PRINTER_ENUM_LOCAL;
+
+ torture_comment(tctx, "Testing servername behaviour in EnumPrinters and GetPrinters\n");
+
+ servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ torture_assert(tctx,
+ test_EnumPrinters_level(tctx, b, flags, servername, 2, &count, &info),
+ "failed to enumerate printers");
+
+ for (i=0; i < count; i++) {
+
+ torture_assert_casestr_equal(tctx, info[i].info2.servername, servername,
+ "unexpected servername");
+
+ torture_assert(tctx,
+ test_OnePrinter_servername(tctx, p, b, servername, info[i].info2.printername),
+ "failed to check printer");
+ }
+
+ servername = "";
+
+ torture_assert(tctx,
+ test_EnumPrinters_level(tctx, b, flags, servername, 2, &count, &info),
+ "failed to enumerate printers");
+
+ for (i=0; i < count; i++) {
+
+ torture_assert_casestr_equal(tctx, info[i].info2.servername, NULL,
+ "unexpected servername");
+
+ torture_assert(tctx,
+ test_OnePrinter_servername(tctx, p, b, servername, info[i].info2.printername),
+ "failed to check printer");
+ }
+
+
+ return true;
+}
+
+#if 0
+static bool test_GetPrinterDriver(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name)
+{
+ struct spoolss_GetPrinterDriver r;
+ uint32_t needed;
+
+ r.in.handle = handle;
+ r.in.architecture = "W32X86";
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_comment(tctx, "Testing GetPrinterDriver level %d\n", r.in.level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver_r(b, tctx, &r),
+ "failed to call GetPrinterDriver");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_GetPrinterDriver_r(b, tctx, &r),
+ "failed to call GetPrinterDriver");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call GetPrinterDriver");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4);
+
+ return true;
+}
+#endif
+
+static bool test_GetPrinterDriver2_level(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name,
+ const char *architecture,
+ uint32_t level,
+ uint32_t client_major_version,
+ uint32_t client_minor_version,
+ union spoolss_DriverInfo *info_p,
+ WERROR *result_p)
+
+{
+ struct spoolss_GetPrinterDriver2 r;
+ uint32_t needed;
+ uint32_t server_major_version;
+ uint32_t server_minor_version;
+
+ r.in.handle = handle;
+ r.in.architecture = architecture;
+ r.in.client_major_version = client_major_version;
+ r.in.client_minor_version = client_minor_version;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.in.level = level;
+ r.out.needed = &needed;
+ r.out.server_major_version = &server_major_version;
+ r.out.server_minor_version = &server_minor_version;
+
+ torture_comment(tctx, "Testing GetPrinterDriver2(%s) level %d\n",
+ driver_name, r.in.level);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriver2_r(b, tctx, &r),
+ "failed to call GetPrinterDriver2");
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriver2_r(b, tctx, &r),
+ "failed to call GetPrinterDriver2");
+ }
+
+ if (result_p) {
+ *result_p = r.out.result;
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INVALID_LEVEL)) {
+ switch (r.in.level) {
+ case 101:
+ case 8:
+ torture_comment(tctx,
+ "level %d not implemented, not considering as an error\n",
+ r.in.level);
+ return true;
+ default:
+ break;
+ }
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call GetPrinterDriver2");
+
+ CHECK_NEEDED_SIZE_LEVEL(spoolss_DriverInfo, r.out.info, r.in.level, needed, 4);
+
+ if (info_p) {
+ *info_p = *r.out.info;
+ }
+
+ return true;
+}
+
+static bool test_GetPrinterDriver2(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *driver_name,
+ const char *architecture)
+{
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6, 8, 101 };
+ int i;
+
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ torture_assert(tctx,
+ test_GetPrinterDriver2_level(tctx, b, handle, driver_name, architecture, levels[i], 3, 0, NULL, NULL),
+ "");
+ }
+
+ return true;
+}
+
+static bool test_EnumPrinterDrivers_old(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ uint16_t levels[] = {1, 2, 3, 4, 5, 6};
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ uint32_t count;
+ union spoolss_DriverInfo *info;
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_args(tctx, b, server_name, ctx->environment, levels[i], &count, &info),
+ "failed to enumerate drivers");
+
+ if (!info) {
+ torture_comment(tctx, "No printer drivers returned\n");
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool test_DeletePrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct spoolss_DeletePrinter r;
+
+ torture_comment(tctx, "Testing DeletePrinter\n");
+
+ r.in.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_DeletePrinter_r(b, tctx, &r),
+ "failed to delete printer");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to delete printer");
+
+ return true;
+}
+
+static bool test_EnumPrinters_findname(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t flags,
+ uint32_t level,
+ const char *name,
+ bool *found)
+{
+ struct spoolss_EnumPrinters e;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+ uint32_t needed;
+ int i;
+
+ *found = false;
+
+ e.in.flags = flags;
+ e.in.server = NULL;
+ e.in.level = level;
+ e.in.buffer = NULL;
+ e.in.offered = 0;
+ e.out.count = &count;
+ e.out.info = &info;
+ e.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e),
+ "failed to enum printers");
+
+ if (W_ERROR_EQUAL(e.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ e.in.buffer = &blob;
+ e.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e),
+ "failed to enum printers");
+ }
+
+ torture_assert_werr_ok(tctx, e.out.result,
+ "failed to enum printers");
+
+ for (i=0; i < count; i++) {
+
+ const char *current = NULL;
+ const char *q;
+
+ switch (level) {
+ case 1:
+ current = info[i].info1.name;
+ break;
+ }
+
+ if (strequal(current, name)) {
+ *found = true;
+ break;
+ }
+
+ q = strrchr(current, '\\');
+ if (q) {
+ if (!e.in.server) {
+ torture_warning(tctx,
+ "server returns printername %s incl. servername although we did not set servername", current);
+ }
+ q++;
+ if (strequal(q, name)) {
+ *found = true;
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool test_AddPrinter_wellknown(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *printername,
+ bool ex)
+{
+ WERROR result;
+ struct spoolss_AddPrinter r;
+ struct spoolss_AddPrinterEx rex;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo1 info1;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct policy_handle handle;
+ bool found = false;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ ZERO_STRUCT(userlevel_ctr);
+ ZERO_STRUCT(info1);
+
+ torture_comment(tctx, "Testing AddPrinter%s(%s) level 1\n",
+ ex ? "Ex":"", printername);
+
+ /* try to add printer to wellknown printer list (level 1) */
+
+ userlevel_ctr.level = 1;
+
+ info_ctr.info.info1 = &info1;
+ info_ctr.level = 1;
+
+ rex.in.server = NULL;
+ rex.in.info_ctr = &info_ctr;
+ rex.in.devmode_ctr = &devmode_ctr;
+ rex.in.secdesc_ctr = &secdesc_ctr;
+ rex.in.userlevel_ctr = &userlevel_ctr;
+ rex.out.handle = &handle;
+
+ r.in.server = NULL;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_INVALID_PRINTER_NAME,
+ "unexpected result code");
+
+ info1.name = printername;
+ info1.flags = PRINTER_ATTRIBUTE_SHARED;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS,
+ "unexpected result code");
+
+ /* bizarre protocol, WERR_PRINTER_ALREADY_EXISTS means success here,
+ better do a real check to see the printer is really there */
+
+ torture_assert(tctx, test_EnumPrinters_findname(tctx, b,
+ PRINTER_ENUM_NETWORK, 1,
+ printername,
+ &found),
+ "failed to enum printers");
+
+ torture_assert(tctx, found, "failed to find newly added printer");
+
+ info1.flags = 0;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS,
+ "unexpected result code");
+
+ /* bizarre protocol, WERR_PRINTER_ALREADY_EXISTS means success here,
+ better do a real check to see the printer has really been removed
+ from the well known printer list */
+
+ found = false;
+
+ torture_assert(tctx, test_EnumPrinters_findname(tctx, b,
+ PRINTER_ENUM_NETWORK, 1,
+ printername,
+ &found),
+ "failed to enum printers");
+#if 0
+ torture_assert(tctx, !found, "printer still in well known printer list");
+#endif
+ return true;
+}
+
+static bool test_AddPrinter_normal(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle_p,
+ const char *printername,
+ const char *drivername,
+ const char *portname,
+ struct spoolss_DeviceMode *devmode,
+ bool ex)
+{
+ WERROR result;
+ struct spoolss_AddPrinter r;
+ struct spoolss_AddPrinterEx rex;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo2 info2;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct spoolss_UserLevelCtr userlevel_ctr;
+ struct policy_handle handle;
+ bool found = false;
+ bool existing_printer_deleted = false;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ ZERO_STRUCT(userlevel_ctr);
+
+ torture_comment(tctx, "Testing AddPrinter%s(%s) level 2\n",
+ ex ? "Ex":"", printername);
+
+ devmode_ctr.devmode = devmode;
+
+ userlevel_ctr.level = 1;
+
+ rex.in.server = NULL;
+ rex.in.info_ctr = &info_ctr;
+ rex.in.devmode_ctr = &devmode_ctr;
+ rex.in.secdesc_ctr = &secdesc_ctr;
+ rex.in.userlevel_ctr = &userlevel_ctr;
+ rex.out.handle = &handle;
+
+ r.in.server = NULL;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.out.handle = &handle;
+
+ again:
+
+ /* try to add printer to printer list (level 2) */
+
+ ZERO_STRUCT(info2);
+
+ info_ctr.info.info2 = &info2;
+ info_ctr.level = 2;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_INVALID_PRINTER_NAME,
+ "unexpected result code");
+
+ info2.printername = printername;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+
+ if (W_ERROR_EQUAL(result, WERR_PRINTER_ALREADY_EXISTS)) {
+ struct policy_handle printer_handle;
+
+ if (existing_printer_deleted) {
+ torture_fail(tctx, "already deleted printer still existing?");
+ }
+
+ torture_assert(tctx, call_OpenPrinterEx(tctx, p, printername, NULL, &printer_handle),
+ "failed to open printer handle");
+
+ torture_assert(tctx, test_DeletePrinter(tctx, b, &printer_handle),
+ "failed to delete printer");
+
+ torture_assert(tctx, test_ClosePrinter(tctx, b, &printer_handle),
+ "failed to close server handle");
+
+ existing_printer_deleted = true;
+
+ goto again;
+ }
+
+ torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PORT,
+ "unexpected result code");
+
+ info2.portname = portname;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTER_DRIVER,
+ "unexpected result code");
+
+ info2.drivername = drivername;
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+
+ /* w2k8r2 allows one to add printer w/o defining printprocessor */
+
+ if (!W_ERROR_IS_OK(result)) {
+ torture_assert_werr_equal(tctx, result, WERR_UNKNOWN_PRINTPROCESSOR,
+ "unexpected result code");
+
+ info2.printprocessor = "winprint";
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_ok(tctx, result,
+ "failed to add printer");
+ }
+
+ *handle_p = handle;
+
+ /* we are paranoid, really check if the printer is there now */
+
+ torture_assert(tctx, test_EnumPrinters_findname(tctx, b,
+ PRINTER_ENUM_LOCAL, 1,
+ printername,
+ &found),
+ "failed to enum printers");
+ torture_assert(tctx, found, "failed to find newly added printer");
+
+ torture_assert_ntstatus_ok(tctx, ex ? dcerpc_spoolss_AddPrinterEx_r(b, tctx, &rex) :
+ dcerpc_spoolss_AddPrinter_r(b, tctx, &r),
+ "failed to add printer");
+ result = ex ? rex.out.result : r.out.result;
+ torture_assert_werr_equal(tctx, result, WERR_PRINTER_ALREADY_EXISTS,
+ "unexpected result code");
+
+ return true;
+}
+
+static bool test_printer_info(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ bool ret = true;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping printer info cross tests against samba 3");
+ }
+
+ if (!test_PrinterInfo(tctx, b, &t->handle)) {
+ ret = false;
+ }
+
+ if (!test_SetPrinter_errors(tctx, b, &t->handle)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_EnumPrinterKey(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key_name,
+ const char ***array)
+{
+ struct spoolss_EnumPrinterKey r;
+ uint32_t needed = 0;
+ union spoolss_KeyNames key_buffer;
+ int32_t offered[] = { 0, 1, 2, 3, 4, 5, -1, -2, -3, -4, -5, 256, 512, 1024, 2048 };
+ uint32_t _ndr_size;
+ int i;
+
+ r.in.handle = handle;
+ r.in.key_name = key_name;
+ r.out.key_buffer = &key_buffer;
+ r.out.needed = &needed;
+ r.out._ndr_size = &_ndr_size;
+
+ for (i=0; i < ARRAY_SIZE(offered); i++) {
+
+ if (offered[i] < 0 && needed) {
+ if (needed <= 4) {
+ continue;
+ }
+ r.in.offered = needed + offered[i];
+ } else {
+ r.in.offered = offered[i];
+ }
+
+ ZERO_STRUCT(key_buffer);
+
+ torture_comment(tctx, "Testing EnumPrinterKey(%s) with %d offered\n", r.in.key_name, r.in.offered);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r),
+ "failed to call EnumPrinterKey");
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+
+ torture_assert(tctx, (_ndr_size == r.in.offered/2),
+ talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)",
+ _ndr_size, r.in.offered/2));
+
+ r.in.offered = needed;
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &r),
+ "failed to call EnumPrinterKey");
+ }
+
+ if (offered[i] > 0) {
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call EnumPrinterKey");
+ }
+
+ torture_assert(tctx, (_ndr_size == r.in.offered/2),
+ talloc_asprintf(tctx, "EnumPrinterKey size mismatch, _ndr_size %d (expected %d)",
+ _ndr_size, r.in.offered/2));
+
+ torture_assert(tctx, (*r.out.needed <= r.in.offered),
+ talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= offered %d", *r.out.needed, r.in.offered));
+
+ torture_assert(tctx, (*r.out.needed <= _ndr_size * 2),
+ talloc_asprintf(tctx, "EnumPrinterKey size mismatch: needed %d is not <= _ndr_size %d * 2", *r.out.needed, _ndr_size));
+
+ if (key_buffer.string_array) {
+ uint32_t calc_needed = 0;
+ int s;
+ for (s=0; key_buffer.string_array[s]; s++) {
+ calc_needed += strlen_m_term(key_buffer.string_array[s])*2;
+ }
+ if (!key_buffer.string_array[0]) {
+ calc_needed += 2;
+ }
+ calc_needed += 2;
+
+ torture_assert_int_equal(tctx, *r.out.needed, calc_needed,
+ "EnumPrinterKey unexpected size");
+ }
+ }
+
+ if (array) {
+ *array = key_buffer.string_array;
+ }
+
+ return true;
+}
+
+bool test_printer_all_keys(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ const char **key_array = NULL;
+ int i;
+
+ torture_comment(tctx, "Testing Printer Keys\n");
+
+ torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, "", &key_array),
+ "failed to call test_EnumPrinterKey");
+
+ for (i=0; key_array && key_array[i]; i++) {
+ torture_assert(tctx, test_EnumPrinterKey(tctx, b, handle, key_array[i], NULL),
+ "failed to call test_EnumPrinterKey");
+ }
+ for (i=0; key_array && key_array[i]; i++) {
+ torture_assert(tctx, test_EnumPrinterDataEx(tctx, b, handle, key_array[i], NULL, NULL),
+ "failed to call test_EnumPrinterDataEx");
+ }
+
+ torture_comment(tctx, "Printer Keys test succeeded\n\n");
+
+ return true;
+}
+
+static bool test_openprinter_wrap(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *printername = t->info2.printername;
+
+ return test_openprinter(tctx, b, printername);
+}
+
+static bool test_csetprinter(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ const char *printername = talloc_asprintf(tctx, "%s2", t->info2.printername);
+ const char *drivername = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
+ const char *portname = t->info2.portname;
+
+ union spoolss_PrinterInfo info;
+ struct policy_handle new_handle, new_handle2;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "Testing c_setprinter\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 0, &info),
+ "failed to get level 0 printer info");
+ torture_comment(tctx, "csetprinter on initial printer handle: %d\n",
+ info.info0.c_setprinter);
+
+ /* check if c_setprinter on 1st handle increases after a printer has
+ * been added */
+
+ torture_assert(tctx,
+ test_AddPrinter_normal(tctx, p, &new_handle, printername, drivername, portname, NULL, false),
+ "failed to add new printer");
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 0, &info),
+ "failed to get level 0 printer info");
+ torture_comment(tctx, "csetprinter on initial printer handle (after add): %d\n",
+ info.info0.c_setprinter);
+
+ /* check if c_setprinter on new handle increases after a printer has
+ * been added */
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &new_handle, 0, &info),
+ "failed to get level 0 printer info");
+ torture_comment(tctx, "csetprinter on created handle: %d\n",
+ info.info0.c_setprinter);
+
+ /* open the new printer and check if c_setprinter increases */
+
+ torture_assert(tctx,
+ call_OpenPrinterEx(tctx, p, printername, NULL, &new_handle2),
+ "failed to open created printer");
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &new_handle2, 0, &info),
+ "failed to get level 0 printer info");
+ torture_comment(tctx, "csetprinter on new handle (after openprinter): %d\n",
+ info.info0.c_setprinter);
+
+ /* cleanup */
+
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &new_handle2),
+ "failed to close printer");
+ torture_assert(tctx,
+ test_DeletePrinter(tctx, b, &new_handle),
+ "failed to delete new printer");
+
+ return true;
+}
+
+static bool compose_local_driver_directory(struct torture_context *tctx,
+ const char *environment,
+ const char *local_dir,
+ const char **path)
+{
+ char *p;
+
+ p = strrchr(local_dir, '/');
+ if (!p) {
+ return NULL;
+ }
+ p++;
+
+ if (strequal(environment, SPOOLSS_ARCHITECTURE_x64)) {
+ if (!strequal(p, "x64")) {
+ *path = talloc_asprintf(tctx, "%s/x64", local_dir);
+ }
+ } else if (strequal(environment, SPOOLSS_ARCHITECTURE_NT_X86)) {
+ if (!strequal(p, "i386")) {
+ *path = talloc_asprintf(tctx, "%s/i386", local_dir);
+ }
+ } else {
+ torture_assert(tctx, "unknown environment: '%s'\n", environment);
+ }
+
+ return true;
+}
+
+#if 0
+static struct spoolss_DeviceMode *torture_devicemode(TALLOC_CTX *mem_ctx,
+ const char *devicename)
+{
+ struct spoolss_DeviceMode *r;
+
+ r = talloc_zero(mem_ctx, struct spoolss_DeviceMode);
+ if (r == NULL) {
+ return NULL;
+ }
+
+ r->devicename = talloc_strdup(r, devicename);
+ r->specversion = DMSPEC_NT4_AND_ABOVE;
+ r->driverversion = 0x0600;
+ r->size = 0x00dc;
+ r->__driverextra_length = 0;
+ r->fields = DEVMODE_FORMNAME |
+ DEVMODE_TTOPTION |
+ DEVMODE_PRINTQUALITY |
+ DEVMODE_DEFAULTSOURCE |
+ DEVMODE_COPIES |
+ DEVMODE_SCALE |
+ DEVMODE_PAPERSIZE |
+ DEVMODE_ORIENTATION;
+ r->orientation = DMORIENT_PORTRAIT;
+ r->papersize = DMPAPER_LETTER;
+ r->paperlength = 0;
+ r->paperwidth = 0;
+ r->scale = 100;
+ r->copies = 55;
+ r->defaultsource = DMBIN_FORMSOURCE;
+ r->printquality = DMRES_HIGH;
+ r->color = DMRES_MONOCHROME;
+ r->duplex = DMDUP_SIMPLEX;
+ r->yresolution = 0;
+ r->ttoption = DMTT_SUBDEV;
+ r->collate = DMCOLLATE_FALSE;
+ r->formname = talloc_strdup(r, "Letter");
+
+ return r;
+}
+#endif
+
+static bool test_architecture_buffer(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ struct spoolss_OpenPrinterEx r;
+ struct spoolss_UserLevel1 u1;
+ struct policy_handle handle;
+ uint32_t architectures[] = {
+ PROCESSOR_ARCHITECTURE_INTEL,
+ PROCESSOR_ARCHITECTURE_IA64,
+ PROCESSOR_ARCHITECTURE_AMD64
+ };
+ uint32_t needed[3];
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ for (i=0; i < ARRAY_SIZE(architectures); i++) {
+
+ torture_comment(tctx, "Testing OpenPrinterEx with architecture %d\n", architectures[i]);
+
+ u1.size = 0;
+ u1.client = NULL;
+ u1.user = NULL;
+ u1.build = 0;
+ u1.major = 3;
+ u1.minor = 0;
+ u1.processor = architectures[i];
+
+ 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.userlevel_ctr.level = 1;
+ r.in.userlevel_ctr.user_info.level1 = &u1;
+ r.out.handle = &handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r), "");
+ torture_assert_werr_ok(tctx, r.out.result, "");
+
+ {
+ struct spoolss_EnumPrinters e;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+
+ e.in.flags = PRINTER_ENUM_LOCAL;
+ e.in.server = NULL;
+ e.in.level = 2;
+ e.in.buffer = NULL;
+ e.in.offered = 0;
+ e.out.count = &count;
+ e.out.info = &info;
+ e.out.needed = &needed[i];
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_EnumPrinters_r(b, tctx, &e), "");
+#if 0
+ torture_comment(tctx, "needed was %d\n", needed[i]);
+#endif
+ }
+
+ torture_assert(tctx, test_ClosePrinter(tctx, b, &handle), "");
+ }
+
+ for (i=1; i < ARRAY_SIZE(architectures); i++) {
+ if (needed[i-1] != needed[i]) {
+ torture_fail(tctx,
+ talloc_asprintf(tctx, "needed size %d for architecture %d != needed size %d for architecture %d\n",
+ needed[i-1], architectures[i-1], needed[i], architectures[i]));
+ }
+ }
+
+ return true;
+}
+
+static bool test_get_core_printer_drivers_arch_guid(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *architecture,
+ const char *guid_str,
+ const char **package_id)
+{
+ struct spoolss_GetCorePrinterDrivers r;
+ struct spoolss_CorePrinterDriver core_printer_drivers;
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, 2);
+ const char **s;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct GUID guid;
+
+ s = talloc_zero_array(tctx, const char *, 2);
+
+ r.in.servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.architecture = "foobar";
+ r.in.core_driver_size = 0;
+ r.in.core_driver_dependencies = (uint16_t *)blob.data;
+ r.in.core_printer_driver_count = 0;
+ r.out.core_printer_drivers = &core_printer_drivers;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r),
+ "spoolss_GetCorePrinterDrivers failed");
+ torture_assert_hresult_equal(tctx, r.out.result, HRES_E_INVALIDARG,
+ "spoolss_GetCorePrinterDrivers failed");
+
+ guid = GUID_random();
+ s[0] = GUID_string2(tctx, &guid);
+
+ torture_assert(tctx,
+ push_reg_multi_sz(tctx, &blob, s),
+ "push_reg_multi_sz failed");
+
+ r.in.core_driver_size = blob.length/2;
+ r.in.core_driver_dependencies = (uint16_t *)blob.data;
+ r.in.core_printer_driver_count = 1;
+ r.out.core_printer_drivers = talloc_zero_array(tctx, struct spoolss_CorePrinterDriver, r.in.core_printer_driver_count);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r),
+ "spoolss_GetCorePrinterDrivers failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT,
+ "spoolss_GetCorePrinterDrivers failed");
+
+ r.in.architecture = architecture;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r),
+ "spoolss_GetCorePrinterDrivers failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_NOT_FOUND,
+ "spoolss_GetCorePrinterDrivers failed");
+
+ s[0] = talloc_strdup(s, guid_str);
+
+ torture_assert(tctx,
+ push_reg_multi_sz(tctx, &blob, s),
+ "push_reg_multi_sz failed");
+
+ r.in.core_driver_size = blob.length/2;
+ r.in.core_driver_dependencies = (uint16_t *)blob.data;
+ r.in.core_printer_driver_count = 1;
+ r.out.core_printer_drivers = talloc_zero_array(tctx, struct spoolss_CorePrinterDriver, r.in.core_printer_driver_count);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetCorePrinterDrivers_r(b, tctx, &r),
+ "spoolss_GetCorePrinterDrivers failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "spoolss_GetCorePrinterDrivers failed");
+
+ if (package_id) {
+ *package_id = r.out.core_printer_drivers[0].szPackageID;
+ }
+
+ return true;
+}
+
+static bool test_get_core_printer_drivers(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ const char *architectures[] = {
+ SPOOLSS_ARCHITECTURE_NT_X86,
+ SPOOLSS_ARCHITECTURE_x64
+ };
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+
+ for (i=0; i < ARRAY_SIZE(architectures); i++) {
+
+ torture_comment(tctx, "Testing GetCorePrinterDrivers(\"%s\",\"%s\")\n",
+ architectures[i],
+ SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV);
+
+ torture_assert(tctx,
+ test_get_core_printer_drivers_arch_guid(tctx, p,
+ architectures[i],
+ SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV,
+ NULL),
+ "");
+ }
+
+ return true;
+}
+
+static bool test_get_printer_driver_package_path(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ const char *architectures[] = {
+ SPOOLSS_ARCHITECTURE_NT_X86,
+ SPOOLSS_ARCHITECTURE_x64
+ };
+ int i;
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ for (i=0; i < ARRAY_SIZE(architectures); i++) {
+ struct spoolss_GetPrinterDriverPackagePath r;
+ uint32_t required = 0;
+ const char *package_id = NULL;
+
+ test_get_core_printer_drivers_arch_guid(tctx, p,
+ architectures[i],
+ SPOOLSS_CORE_PRINT_PACKAGE_FILES_XPSDRV,
+ &package_id),
+
+ torture_comment(tctx, "Testing GetPrinterDriverPackagePath(\"%s\",\"%s\")\n",
+ architectures[i], package_id);
+
+ r.in.servername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.architecture = "foobar";
+ r.in.language = NULL;
+ r.in.package_id = "";
+ r.in.driver_package_cab_size = 0;
+ r.in.driver_package_cab = NULL;
+
+ r.out.required = &required;
+ r.out.driver_package_cab = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INVALID_ENVIRONMENT,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.architecture = architectures[i];
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_FILE_NOT_FOUND,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.package_id = package_id;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.driver_package_cab_size = required;
+ r.in.driver_package_cab = talloc_zero_array(tctx, char, required);
+ r.out.driver_package_cab = talloc_zero_array(tctx, char, required);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.servername = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_werr_equal(tctx,
+ W_ERROR(WIN32_FROM_HRESULT(r.out.result)), WERR_INSUFFICIENT_BUFFER,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ r.in.driver_package_cab_size = required;
+ r.in.driver_package_cab = talloc_zero_array(tctx, char, required);
+ r.out.driver_package_cab = talloc_zero_array(tctx, char, required);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverPackagePath_r(b, tctx, &r),
+ "spoolss_GetPrinterDriverPackagePath failed");
+ torture_assert_hresult_ok(tctx, r.out.result,
+ "spoolss_GetPrinterDriverPackagePath failed");
+
+ }
+
+ return true;
+}
+
+static bool test_get_printer_printserverhandle(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t levels[] = {0, 1, 2, /* 3,*/ 4, 5, 6, 7, 8};
+ int i;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ torture_assert(tctx,
+ test_GetPrinter_level_exp(tctx, b, &ctx->server_handle,
+ levels[i], WERR_INVALID_LEVEL,
+ NULL),
+ "failed to call GetPrinter");
+ }
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, NULL),
+ "failed to call GetPrinter");
+
+ return true;
+}
+
+#define TEST_SID "S-1-5-21-1234567890-1234567890-1234567890-500"
+
+static bool test_set_printer_printserverhandle(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo3 info3;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct security_descriptor *sd;
+ struct security_ace *ace;
+ struct dom_sid sid;
+ int i;
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, &info),
+ "failed to call GetPrinter");
+
+ secdesc_ctr.sd = info.info3.secdesc;
+ secdesc_ctr.sd->owner_sid = NULL;
+ secdesc_ctr.sd->group_sid = NULL;
+
+ sd = security_descriptor_copy(tctx, secdesc_ctr.sd);
+ if (sd == NULL) {
+ return false;
+ }
+
+ ace = security_ace_create(tctx,
+ TEST_SID,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_STD_REQUIRED,
+ SEC_ACE_FLAG_CONTAINER_INHERIT);
+ torture_assert(tctx, ace, "failed to create ace");
+
+ torture_assert_ntstatus_ok(tctx,
+ security_descriptor_dacl_add(sd, ace),
+ "failed to add ace");
+
+ secdesc_ctr.sd = sd;
+
+ info3.sec_desc_ptr = 0;
+
+ info_ctr.level = 3;
+ info_ctr.info.info3 = &info3;
+
+ ZERO_STRUCT(devmode_ctr);
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, &ctx->server_handle, &info_ctr,
+ &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, &info),
+ "failed to call GetPrinter");
+
+ for (i = 0; i < info.info3.secdesc->dacl->num_aces; i++) {
+ if (security_ace_equal(&info.info3.secdesc->dacl->aces[i], ace)) {
+ break;
+ }
+ }
+
+ if (i == info.info3.secdesc->dacl->num_aces) {
+ torture_fail(tctx, "ace not present");
+ }
+
+ torture_assert(tctx,
+ dom_sid_parse(TEST_SID, &sid),
+ "failed to parse sid");
+
+ torture_assert_ntstatus_ok(tctx,
+ security_descriptor_dacl_del(info.info3.secdesc, &sid),
+ "failed to remove ace from sd");
+
+ secdesc_ctr.sd = info.info3.secdesc;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, &ctx->server_handle, &info_ctr,
+ &devmode_ctr, &secdesc_ctr, 0),
+ "failed to call SetPrinter");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &ctx->server_handle, 3, &info),
+ "failed to call GetPrinter");
+
+ for (i = 0; i < info.info3.secdesc->dacl->num_aces; i++) {
+ if (security_ace_equal(&info.info3.secdesc->dacl->aces[i], ace)) {
+ torture_fail(tctx, "ace still present");
+ }
+ }
+
+ return true;
+}
+
+
+static bool test_PrintServer_Forms_Winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_Forms_winreg(tctx, b, &ctx->server_handle, true, NULL);
+}
+
+static bool test_PrintServer_Forms(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_Forms(tctx, b, &ctx->server_handle, true, NULL, NULL, NULL);
+}
+
+static bool test_PrintServer_EnumForms(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *ctx =
+ talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = ctx->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ return test_EnumForms_all(tctx, b, &ctx->server_handle, true);
+}
+
+static bool torture_rpc_spoolss_setup_common(struct torture_context *tctx, struct test_spoolss_context *t)
+{
+ NTSTATUS status;
+
+ status = torture_rpc_connection(tctx, &t->spoolss_pipe, &ndr_table_spoolss);
+
+ torture_assert_ntstatus_ok(tctx, status, "Error connecting to server");
+
+ torture_assert(tctx,
+ test_OpenPrinter_server(tctx, t->spoolss_pipe, &t->server_handle),
+ "failed to open printserver");
+ torture_assert(tctx,
+ test_get_environment(tctx, t->spoolss_pipe->binding_handle, &t->server_handle, &t->environment),
+ "failed to get environment");
+
+ return true;
+}
+
+static bool torture_rpc_spoolss_setup(struct torture_context *tctx, void **data)
+{
+ struct test_spoolss_context *t;
+
+ *data = t = talloc_zero(tctx, struct test_spoolss_context);
+
+ return torture_rpc_spoolss_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_teardown_common(struct torture_context *tctx, struct test_spoolss_context *t)
+{
+ test_ClosePrinter(tctx, t->spoolss_pipe->binding_handle, &t->server_handle);
+
+ return true;
+}
+
+static bool torture_rpc_spoolss_teardown(struct torture_context *tctx, void *data)
+{
+ struct test_spoolss_context *t = talloc_get_type(data, struct test_spoolss_context);
+ bool ret;
+
+ ret = torture_rpc_spoolss_teardown_common(tctx, t);
+ talloc_free(t);
+
+ return ret;
+}
+
+static bool torture_rpc_spoolss_printer_setup_common(struct torture_context *tctx, struct torture_printer_context *t)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding_handle *b;
+ const char *server_name_slash;
+ const char *driver_name;
+ const char *printer_name;
+ const char *port_name;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &t->spoolss_pipe, &ndr_table_spoolss),
+ "Error connecting to server");
+
+ p = t->spoolss_pipe;
+ b = p->binding_handle;
+ server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ t->driver.info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ t->driver.info8.driver_name = TORTURE_DRIVER;
+ t->driver.info8.driver_path = "pscript5.dll";
+ t->driver.info8.data_file = "cups6.ppd";
+ t->driver.info8.config_file = "ps5ui.dll";
+ t->driver.info8.help_file = "pscript.hlp";
+ t->driver.info8.default_datatype = "RAW";
+ t->driver.info8.dependent_files = talloc_zero(t, struct spoolss_StringArray);
+ t->driver.info8.dependent_files->string = talloc_zero_array(t, const char *, 8 + 1);
+ t->driver.info8.dependent_files->string[0] = "pscript5.dll";
+ t->driver.info8.dependent_files->string[1] = "cups6.ppd";
+ t->driver.info8.dependent_files->string[2] = "ps5ui.dll";
+ t->driver.info8.dependent_files->string[3] = "pscript.hlp";
+ t->driver.info8.dependent_files->string[4] = "pscript.ntf";
+ t->driver.info8.dependent_files->string[5] = "cups6.ini";
+ t->driver.info8.dependent_files->string[6] = "cupsps6.dll";
+ t->driver.info8.dependent_files->string[7] = "cupsui6.dll";
+
+ t->driver.local.driver_directory= "/usr/share/cups/drivers";
+
+ t->info2.portname = "LPT1:";
+
+ printer_name = t->info2.printername;
+ port_name = t->info2.portname;
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, &t->driver),
+ "failed to fillup printserver info");
+
+ t->driver.info8.architecture = talloc_strdup(t, t->driver.remote.environment);
+
+ torture_assert(tctx,
+ compose_local_driver_directory(tctx, t->driver.remote.environment,
+ t->driver.local.driver_directory,
+ &t->driver.local.driver_directory),
+ "failed to compose local driver directory");
+
+ t->info2.drivername = "Microsoft XPS Document Writer";
+
+ if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername, NULL)) {
+ torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) is present on server\n",
+ t->info2.drivername, t->driver.remote.environment);
+ t->have_driver = true;
+ goto try_add;
+ }
+
+ torture_comment(tctx, "driver '%s' (architecture: %s, version: 3) does not exist on the server\n",
+ t->info2.drivername, t->driver.remote.environment);
+
+ t->info2.drivername = "Microsoft XPS Document Writer v4";
+
+ if (test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, t->driver.remote.environment, 3, t->info2.drivername, NULL)) {
+ torture_comment(tctx, "driver '%s' (architecture: %s, version: 4) is present on server\n",
+ t->info2.drivername, t->driver.remote.environment);
+ t->have_driver = true;
+ goto try_add;
+ }
+
+ torture_comment(tctx, "trying to upload own driver\n");
+
+ if (!directory_exist(t->driver.local.driver_directory)) {
+ torture_warning(tctx, "no local driver is available!");
+ t->have_driver = false;
+ goto try_add;
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), &t->driver),
+ "failed to upload printer driver");
+
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &t->driver.info8, 0, false, NULL),
+ "failed to add driver");
+
+ t->added_driver = true;
+ t->have_driver = true;
+
+ try_add:
+ driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
+
+ if (t->wellknown) {
+ torture_assert(tctx,
+ test_AddPrinter_wellknown(tctx, p, printer_name, t->ex),
+ "failed to add wellknown printer");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinter_normal(tctx, p, &t->handle, printer_name, driver_name, port_name, t->devmode, t->ex),
+ "failed to add printer");
+ }
+
+ return true;
+}
+
+static bool torture_rpc_spoolss_printer_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = false;
+ t->wellknown = false;
+ t->info2.printername = TORTURE_PRINTER;
+ t->devmode = NULL;
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_printerex_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = true;
+ t->wellknown = false;
+ t->info2.printername = TORTURE_PRINTER_EX;
+ t->devmode = NULL;
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_printerwkn_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = false;
+ t->wellknown = true;
+ t->info2.printername = TORTURE_WELLKNOWN_PRINTER;
+ t->devmode = NULL;
+
+ /* FIXME */
+ if (t->wellknown) {
+ torture_skip(tctx, "skipping AddPrinter level 1");
+ }
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_printerexwkn_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = true;
+ t->wellknown = true;
+ t->info2.printername = TORTURE_WELLKNOWN_PRINTER_EX;
+ t->devmode = NULL;
+
+ /* FIXME */
+ if (t->wellknown) {
+ torture_skip(tctx, "skipping AddPrinterEx level 1");
+ }
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+
+#if 0
+static bool torture_rpc_spoolss_printerdm_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_printer_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_printer_context);
+
+ t->ex = true;
+ t->wellknown = false;
+ t->info2.printername = TORTURE_PRINTER_EX;
+ t->devmode = torture_devicemode(t, TORTURE_PRINTER_EX);
+
+ return torture_rpc_spoolss_printer_setup_common(tctx, t);
+}
+#endif
+
+static bool test_DeletePrinterDriverEx_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server,
+ const char *driver,
+ const char *environment,
+ uint32_t delete_flags,
+ uint32_t version,
+ WERROR expected_result);
+
+static bool torture_rpc_spoolss_printer_teardown_common(struct torture_context *tctx, struct torture_printer_context *t)
+{
+ bool found = false;
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = NULL;
+ const char *server_name_slash;
+ bool ok = true;
+
+ if (p == NULL) {
+ return true;
+ }
+ b = p->binding_handle;
+
+ server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ if (!t->wellknown) {
+ const char *printer_name = t->info2.printername;
+
+ torture_assert_goto(tctx,
+ test_DeletePrinter(tctx, b, &t->handle),
+ ok,
+ remove_driver,
+ "failed to delete printer");
+
+ torture_assert_goto(tctx,
+ test_EnumPrinters_findname(tctx, b, PRINTER_ENUM_LOCAL, 1,
+ printer_name, &found),
+ ok,
+ remove_driver,
+ "failed to enumerate printers");
+
+ torture_assert_goto(tctx,
+ !found,
+ ok,
+ remove_driver,
+ "deleted printer still there");
+ }
+
+
+remove_driver:
+ if (t->added_driver) {
+ ok = remove_printer_driver(tctx,
+ dcerpc_server_name(p),
+ &t->driver);
+ if (!ok) {
+ torture_warning(tctx,
+ "failed to remove printer driver\n");
+ }
+
+ ok = test_DeletePrinterDriverEx_exp(tctx, b,
+ server_name_slash,
+ t->driver.info8.driver_name,
+ t->driver.info8.architecture,
+ DPD_DELETE_ALL_FILES,
+ t->driver.info8.version,
+ WERR_OK);
+ if (!ok) {
+ torture_warning(tctx,
+ "failed to delete printer driver via "
+ "spoolss\n");
+ }
+ }
+
+ return ok;
+}
+
+static bool torture_rpc_spoolss_printer_teardown(struct torture_context *tctx, void *data)
+{
+ struct torture_printer_context *t = talloc_get_type(data, struct torture_printer_context);
+ bool ret;
+
+ ret = torture_rpc_spoolss_printer_teardown_common(tctx, t);
+ talloc_free(t);
+
+ return ret;
+}
+
+static bool test_print_test(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ torture_assert(tctx,
+ test_DoPrintTest(tctx, b, &t->handle),
+ "failed to do print test");
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return true;
+}
+
+static bool test_print_test_extended(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ bool ret = true;
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ ret = test_DoPrintTest_extended(tctx, b, &t->handle);
+ if (ret == false) {
+ torture_comment(tctx, "WARNING! failed to do extended print test\n");
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_comment(tctx, "non-critical for samba3\n");
+ ret = true;
+ tctx->last_result = TORTURE_SKIP;
+ }
+ }
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return ret;
+}
+
+static bool test_print_test_properties(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skip printer job property tests against samba");
+ }
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ torture_assert(tctx,
+ test_DoPrintTest_properties(tctx, b, &t->handle),
+ "failed to test print job properties");
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return true;
+}
+
+/* use smbd file IO to spool a print job */
+static bool test_print_test_smbd(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ NTSTATUS status;
+ uint32_t count;
+ union spoolss_JobInfo *info = NULL;
+ int i;
+
+ struct smb2_tree *tree;
+ struct smb2_handle job_h;
+ struct smbcli_options options;
+ TALLOC_CTX *mem_ctx = talloc_new(tctx);
+ /*
+ * Do not test against the dynamically added printers, printing via
+ * smbd means that a different spoolss process may handle the
+ * OpenPrinter request to the one that handled the AddPrinter request.
+ * This currently leads to an ugly race condition where one process
+ * sees the new printer and one doesn't.
+ */
+ const char *share = TORTURE_PRINTER_STATIC1;
+
+ torture_comment(tctx, "Testing smbd job spooling\n");
+ lpcfg_smbcli_options(tctx->lp_ctx, &options);
+
+ status = smb2_connect(mem_ctx,
+ torture_setting_string(tctx, "host", NULL),
+ lpcfg_smb_ports(tctx->lp_ctx),
+ share,
+ lpcfg_resolve_context(tctx->lp_ctx),
+ samba_cmdline_get_creds(),
+ &tree,
+ tctx->ev,
+ &options,
+ lpcfg_socket_options(tctx->lp_ctx),
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect to SMB2 printer %s - %s\n",
+ share, nt_errstr(status));
+ return false;
+ }
+
+ status = torture_smb2_testfile(tree, "smbd_spooler_job", &job_h);
+ torture_assert_ntstatus_ok(tctx, status, "smbd spool job create");
+
+ status = smb2_util_write(tree, job_h, "exciting print job data", 0,
+ sizeof("exciting print job data"));
+ torture_assert_ntstatus_ok(tctx, status, "smbd spool job write");
+
+ /* check back end spoolss job was created */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs level 1 failed");
+
+ for (i = 0; i < count; i++) {
+ if (!strcmp(info[i].info1.document_name, "smbd_spooler_job")) {
+ break;
+ }
+ }
+ torture_assert(tctx, (i != count), "smbd_spooler_job not found");
+
+ status = smb2_util_close(tree, job_h);
+ torture_assert_ntstatus_ok(tctx, status, "smbd spool job close");
+
+ /* disconnect from printer share */
+ talloc_free(mem_ctx);
+
+ return true;
+}
+
+static bool test_print_test_purge(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data,
+ struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+ bool ret = true;
+ uint32_t count;
+ union spoolss_JobInfo *info;
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+ for (i=0; i < num_jobs; i++) {
+ ret = test_DoPrintTest_add_one_job(tctx, b, &t->handle,
+ "TorturePrintJob",
+ &job_ids[i]);
+ torture_assert(tctx, ret, "failed to add print job");
+ }
+
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs level 1 failed");
+
+ torture_assert_int_equal(tctx, count, num_jobs,
+ "unexpected number of jobs in queue");
+
+ torture_assert(tctx,
+ test_printer_purge(tctx, b, &t->handle),
+ "failed to purge printer");
+
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs level 1 failed");
+
+ torture_assert_int_equal(tctx, count, 0,
+ "unexpected number of jobs in queue");
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return true;
+}
+
+static bool test_printer_sd(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_PrinterInfo_SD(tctx, b, &t->handle),
+ "failed to test security descriptors");
+
+ return true;
+}
+
+static bool test_printer_dm(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrinterInfo_DevMode(tctx, p, &t->handle, t->info2.printername, t->devmode),
+ "failed to test devicemodes");
+
+ return true;
+}
+
+static bool test_printer_info_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrinterInfo_winreg(tctx, p, &t->handle, t->info2.printername),
+ "failed to test printer info winreg");
+
+ return true;
+}
+
+static bool test_printserver_info_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct test_spoolss_context *t =
+ (struct test_spoolss_context *)talloc_get_type_abort(private_data, struct test_spoolss_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrintserverInfo_winreg(tctx, p, &t->server_handle),
+ "failed to test printserver info winreg");
+
+ return true;
+}
+
+
+static bool test_printer_change_id(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_ChangeID(tctx, p, &t->handle),
+ "failed to test change id");
+
+ return true;
+}
+
+static bool test_printer_keys(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_printer_all_keys(tctx, b, &t->handle),
+ "failed to test printer keys");
+
+ return true;
+}
+
+static bool test_printer_data_consistency(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_EnumPrinterData_consistency(tctx, p, &t->handle),
+ "failed to test printer data consistency");
+
+ return true;
+}
+
+static bool test_printer_data_keys(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx_keys(tctx, p, &t->handle),
+ "failed to test printer data keys");
+
+ return true;
+}
+
+static bool test_printer_data_values(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx_values(tctx, p, &t->handle),
+ "failed to test printer data values");
+
+ return true;
+}
+
+static bool test_printer_data_set(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_SetPrinterDataEx_matrix(tctx, p, &t->handle, t->info2.printername, NULL, NULL),
+ "failed to test printer data set");
+
+ return true;
+}
+
+static bool test_printer_data_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrinterData_winreg(tctx, p, &t->handle, t->info2.printername),
+ "failed to test printer data winreg");
+
+ return true;
+}
+
+static bool test_printer_data_dsspooler(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+
+ torture_assert(tctx,
+ test_PrinterData_DsSpooler(tctx, p, &t->handle, t->info2.printername),
+ "failed to test printer data winreg dsspooler");
+
+ return true;
+}
+
+static bool test_printer_ic(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ talloc_get_type_abort(private_data,
+ struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle gdi_handle;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skip printer information context tests against samba");
+ }
+
+ {
+ struct spoolss_CreatePrinterIC r;
+ struct spoolss_DevmodeContainer devmode_ctr;
+
+ ZERO_STRUCT(devmode_ctr);
+
+ r.in.handle = &t->handle;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.out.gdi_handle = &gdi_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_CreatePrinterIC_r(b, tctx, &r),
+ "CreatePrinterIC failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "CreatePrinterIC failed");
+ }
+
+ {
+ struct spoolss_PlayGDIScriptOnPrinterIC r;
+ DATA_BLOB in,out;
+ int i;
+ uint32_t num_fonts = 0;
+
+ in = data_blob_string_const("");
+
+ r.in.gdi_handle = &gdi_handle;
+ r.in.pIn = in.data;
+ r.in.cIn = in.length;
+ r.in.ul = 0;
+
+ for (i = 0; i < 4; i++) {
+
+ out = data_blob_talloc_zero(tctx, i);
+
+ r.in.cOut = out.length;
+ r.out.pOut = out.data;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r),
+ "PlayGDIScriptOnPrinterIC failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_ENOUGH_MEMORY,
+ "PlayGDIScriptOnPrinterIC failed");
+ }
+
+ out = data_blob_talloc_zero(tctx, 4);
+
+ r.in.cOut = out.length;
+ r.out.pOut = out.data;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r),
+ "PlayGDIScriptOnPrinterIC failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+ "PlayGDIScriptOnPrinterIC failed");
+
+ /* now we should have the required length, so retry with a
+ * buffer which is large enough to carry all font ids */
+
+ num_fonts = IVAL(r.out.pOut, 0);
+
+ torture_comment(tctx, "PlayGDIScriptOnPrinterIC gave font count of %d\n", num_fonts);
+
+ out = data_blob_talloc_zero(tctx,
+ num_fonts * sizeof(struct UNIVERSAL_FONT_ID) + 4);
+
+ r.in.cOut = out.length;
+ r.out.pOut = out.data;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_PlayGDIScriptOnPrinterIC_r(b, tctx, &r),
+ "PlayGDIScriptOnPrinterIC failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+ "PlayGDIScriptOnPrinterIC failed");
+
+ }
+
+ {
+ struct spoolss_DeletePrinterIC r;
+
+ r.in.gdi_handle = &gdi_handle;
+ r.out.gdi_handle = &gdi_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterIC_r(b, tctx, &r),
+ "DeletePrinterIC failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "DeletePrinterIC failed");
+
+ }
+
+ return true;
+}
+
+static bool test_printer_bidi(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ talloc_get_type_abort(private_data,
+ struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_SendRecvBidiData r;
+ struct RPC_BIDI_REQUEST_CONTAINER bidi_req;
+ struct RPC_BIDI_RESPONSE_CONTAINER *bidi_rep = NULL;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skip printer bidirectional tests against samba");
+ }
+
+ ZERO_STRUCT(bidi_req);
+
+ r.in.hPrinter = t->handle;
+ r.in.pAction = "foobar";
+ r.in.pReqData = &bidi_req;
+ r.out.ppRespData = &bidi_rep;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_SendRecvBidiData_r(b, tctx, &r),
+ "SendRecvBidiData failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_NOT_SUPPORTED,
+ "SendRecvBidiData failed");
+
+ if (!(t->info2.attributes & PRINTER_ATTRIBUTE_ENABLE_BIDI)) {
+ torture_skip(tctx, "skipping further tests as printer is not BIDI enabled");
+ }
+
+ r.in.pAction = BIDI_ACTION_ENUM_SCHEMA;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_SendRecvBidiData_r(b, tctx, &r),
+ "SendRecvBidiData failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "SendRecvBidiData failed");
+
+ return true;
+}
+
+static bool test_printer_set_publish(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfo7 info7;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info7.guid = "";
+ info7.action = DSPRINT_PUBLISH;
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ info_ctr.level = 7;
+ info_ctr.info.info7 = &info7;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, handle, &info_ctr,
+ &devmode_ctr, &secdesc_ctr, 0), "");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "");
+ torture_assert(tctx,
+ (info.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED),
+ "info2 publish flag not set");
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 7, &info),
+ "");
+ if (info.info7.action & DSPRINT_PENDING) {
+ torture_comment(tctx, "publish is pending\n");
+ torture_assert_int_equal(tctx,
+ info.info7.action,
+ (DSPRINT_PENDING | DSPRINT_PUBLISH),
+ "info7 publish flag not set");
+ } else {
+ struct GUID guid;
+ char *ref_guid;
+ torture_assert_int_equal(tctx,
+ info.info7.action,
+ DSPRINT_PUBLISH,
+ "info7 publish flag not set");
+
+ /* GUID_from_string is able to parse both plain and
+ * curly-braced guids */
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(info.info7.guid,
+ &guid),
+ "invalid published printer GUID");
+
+ /* Build reference GUID string */
+ ref_guid = GUID_string2(tctx, &guid);
+ torture_assert_not_null(tctx, ref_guid, "ENOMEM");
+ ref_guid = talloc_strdup_upper(tctx, ref_guid);
+ torture_assert_not_null(tctx, ref_guid, "ENOMEM");
+ torture_assert_str_equal(tctx, info.info7.guid, ref_guid,
+ "invalid GUID format");
+ }
+
+ return true;
+}
+
+static bool test_printer_set_unpublish(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfo7 info7;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ info7.action = DSPRINT_UNPUBLISH;
+ info7.guid = "";
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+ info_ctr.level = 7;
+ info_ctr.info.info7 = &info7;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, handle, &info_ctr,
+ &devmode_ctr, &secdesc_ctr, 0), "");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 2, &info),
+ "");
+ torture_assert(tctx,
+ !(info.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED),
+ "info2 publish flag still set");
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, handle, 7, &info),
+ "");
+
+ if (info.info7.action & DSPRINT_PENDING) {
+ struct GUID guid;
+ torture_comment(tctx, "unpublish is pending\n");
+ torture_assert_int_equal(tctx,
+ info.info7.action,
+ (DSPRINT_PENDING | DSPRINT_UNPUBLISH),
+ "info7 unpublish flag not set");
+ torture_assert_ntstatus_ok(tctx,
+ GUID_from_string(info.info7.guid,
+ &guid),
+ "invalid printer GUID");
+ } else {
+ torture_assert_int_equal(tctx,
+ info.info7.action, DSPRINT_UNPUBLISH,
+ "info7 unpublish flag not set");
+ }
+
+ return true;
+}
+
+static bool test_printer_publish_toggle(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ talloc_get_type_abort(private_data,
+ struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle *handle = &t->handle;
+ union spoolss_PrinterInfo info7;
+ union spoolss_PrinterInfo info2;
+
+ /* check publish status via level 7 and level 2 */
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 7, &info7),
+ "");
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info2),
+ "");
+
+ if (info2.info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
+ torture_assert_int_equal(tctx,
+ info7.info7.action, DSPRINT_PUBLISH,
+ "info7 publish flag not set");
+ torture_assert(tctx, test_printer_set_unpublish(tctx, b, handle), "");
+ torture_assert(tctx, test_printer_set_publish(tctx, b, handle), "");
+ } else {
+ torture_assert_int_equal(tctx,
+ info7.info7.action, DSPRINT_UNPUBLISH,
+ "info7 unpublish flag not set");
+ torture_assert(tctx, test_printer_set_publish(tctx, b, handle), "");
+ torture_assert(tctx, test_printer_set_unpublish(tctx, b, handle), "");
+ }
+
+ return true;
+}
+
+static bool test_driver_info_winreg(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ const char *driver_name = t->added_driver ? t->driver.info8.driver_name : t->info2.drivername;
+
+ if (!t->have_driver) {
+ torture_skip(tctx, "skipping driver info winreg test as we don't have a driver");
+ }
+
+ torture_assert(tctx,
+ test_DriverInfo_winreg(tctx, p, &t->handle, t->info2.printername, driver_name, t->driver.remote.environment, 3),
+ "failed to test driver info winreg");
+
+ return true;
+}
+
+static bool test_print_job_enum(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ bool ret = true;
+ uint32_t num_jobs = 8;
+ uint32_t *job_ids;
+ int i;
+ union spoolss_JobInfo *info = NULL;
+ uint32_t count;
+
+ torture_assert(tctx,
+ test_PausePrinter(tctx, b, &t->handle),
+ "failed to pause printer");
+
+ /* purge in case of any jobs from previous tests */
+ torture_assert(tctx,
+ test_printer_purge(tctx, b, &t->handle),
+ "failed to purge printer");
+
+ /* enum before jobs, valid level */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs with valid level");
+ torture_assert_int_equal(tctx, count, 0, "EnumJobs count");
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 2, WERR_OK,
+ &count, &info),
+ "EnumJobs with valid level");
+ torture_assert_int_equal(tctx, count, 0, "EnumJobs count");
+ /* enum before jobs, invalid level - expect failure */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 100,
+ WERR_INVALID_LEVEL,
+ &count, &info),
+ "EnumJobs with invalid level");
+
+ job_ids = talloc_zero_array(tctx, uint32_t, num_jobs);
+
+ for (i = 0; i < num_jobs; i++) {
+ ret = test_DoPrintTest_add_one_job(tctx, b, &t->handle,
+ "TorturePrintJob",
+ &job_ids[i]);
+ torture_assert(tctx, ret, "failed to add print job");
+ }
+
+ /* enum after jobs, valid level */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 1, WERR_OK,
+ &count, &info),
+ "EnumJobs with valid level");
+ torture_assert_int_equal(tctx, count, num_jobs, "EnumJobs count");
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 2, WERR_OK,
+ &count, &info),
+ "EnumJobs with valid level");
+ torture_assert_int_equal(tctx, count, num_jobs, "EnumJobs count");
+ /* enum after jobs, invalid level - expect failure */
+ torture_assert(tctx,
+ test_EnumJobs_args(tctx, b, &t->handle, 100,
+ WERR_INVALID_LEVEL,
+ &count, &info),
+ "EnumJobs with invalid level");
+
+ for (i = 0; i < num_jobs; i++) {
+ test_SetJob(tctx, b, &t->handle, job_ids[i], NULL,
+ SPOOLSS_JOB_CONTROL_DELETE);
+ }
+
+ torture_assert(tctx,
+ test_ResumePrinter(tctx, b, &t->handle),
+ "failed to resume printer");
+
+ return true;
+}
+
+static bool test_printer_log_jobinfo(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct spoolss_BranchOfficeJobDataContainer info;
+ int i;
+
+ struct spoolss_LogJobInfoForBranchOffice r;
+
+ torture_comment(tctx, "Testing LogJobInfoForBranchOffice\n");
+
+ info.cJobDataEntries = 0;
+ info.JobData = NULL;
+
+ r.in.hPrinter = &t->handle;
+ r.in.pBranchOfficeJobDataContainer = &info;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_LogJobInfoForBranchOffice_r(b, tctx, &r),
+ "LogJobInfoForBranchOffice failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "LogJobInfoForBranchOffice failed");
+
+ info.cJobDataEntries = 1;
+ info.JobData = talloc_zero_array(tctx, struct spoolss_BranchOfficeJobData, info.cJobDataEntries);
+
+ info.JobData[0].eEventType = kLogOfflineFileFull;
+ info.JobData[0].JobId = 42;
+ info.JobData[0].JobInfo.LogOfflineFileFull.pMachineName = talloc_strdup(tctx, "mthelena");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_LogJobInfoForBranchOffice_r(b, tctx, &r),
+ "LogJobInfoForBranchOffice failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+ "LogJobInfoForBranchOffice failed");
+
+ info.cJobDataEntries = 42;
+ info.JobData = talloc_zero_array(tctx, struct spoolss_BranchOfficeJobData, info.cJobDataEntries);
+
+ for (i=0; i < info.cJobDataEntries; i++) {
+ info.JobData[i].eEventType = kLogOfflineFileFull;
+ info.JobData[i].JobId = i;
+ info.JobData[i].JobInfo.LogOfflineFileFull.pMachineName = talloc_asprintf(tctx, "torture_%d", i);
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_LogJobInfoForBranchOffice_r(b, tctx, &r),
+ "LogJobInfoForBranchOffice failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_OK,
+ "LogJobInfoForBranchOffice failed");
+
+ return true;
+}
+
+static bool test_printer_os_versions(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_printer_context *t =
+ (struct torture_printer_context *)talloc_get_type_abort(private_data, struct torture_printer_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ union spoolss_PrinterInfo info;
+ DATA_BLOB blob;
+ uint8_t *data;
+ uint32_t length;
+ struct spoolss_OSVersion osversion;
+ uint8_t os_major, os_minor;
+ uint16_t os_build;
+ struct policy_handle server_handle;
+
+ torture_comment(tctx, "Testing OSVersion vs. PRINTER_INFO_STRESS\n");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &t->handle, 0, &info),
+ "failed to get level 0 printer info");
+
+ torture_assert(tctx,
+ test_OpenPrinter_server(tctx, p, &server_handle),
+ "failed to open printserver");
+
+ torture_assert(tctx,
+ test_GetPrinterData_checktype(tctx, b, &server_handle, "OSVersion",
+ NULL, NULL, &data, &length),
+ "failed to fetch OSVersion printer data");
+
+ test_ClosePrinter(tctx, b, &server_handle);
+
+ blob = data_blob_const(data, length);
+
+ torture_assert_ndr_success(tctx,
+ ndr_pull_struct_blob(&blob, tctx, &osversion,
+ (ndr_pull_flags_fn_t)ndr_pull_spoolss_OSVersion),
+ "failed to pull OSVersion");
+
+ os_major = CVAL(&info.info0.version, 0);
+ os_minor = CVAL(&info.info0.version, 1);
+ os_build = SVAL(&info.info0.version, 2);
+
+ torture_assert_int_equal(tctx, os_major, osversion.major, "major");
+ torture_assert_int_equal(tctx, os_minor, osversion.minor, "minor");
+ torture_assert_int_equal(tctx, os_build, osversion.build, "build");
+
+ return true;
+}
+
+
+void torture_tcase_printer(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter_wrap);
+ torture_tcase_add_simple_test(tcase, "csetprinter", test_csetprinter);
+ torture_tcase_add_simple_test(tcase, "print_test", test_print_test);
+ torture_tcase_add_simple_test(tcase, "print_test_extended", test_print_test_extended);
+ torture_tcase_add_simple_test(tcase, "print_test_smbd", test_print_test_smbd);
+ torture_tcase_add_simple_test(tcase, "print_test_properties", test_print_test_properties);
+ torture_tcase_add_simple_test(tcase, "print_test_purge", test_print_test_purge);
+ torture_tcase_add_simple_test(tcase, "printer_info", test_printer_info);
+ torture_tcase_add_simple_test(tcase, "sd", test_printer_sd);
+ torture_tcase_add_simple_test(tcase, "dm", test_printer_dm);
+ torture_tcase_add_simple_test(tcase, "printer_info_winreg", test_printer_info_winreg);
+ torture_tcase_add_simple_test(tcase, "change_id", test_printer_change_id);
+ torture_tcase_add_simple_test(tcase, "keys", test_printer_keys);
+ torture_tcase_add_simple_test(tcase, "printerdata_consistency", test_printer_data_consistency);
+ torture_tcase_add_simple_test(tcase, "printerdata_keys", test_printer_data_keys);
+ torture_tcase_add_simple_test(tcase, "printerdata_values", test_printer_data_values);
+ torture_tcase_add_simple_test(tcase, "printerdata_set", test_printer_data_set);
+ torture_tcase_add_simple_test(tcase, "printerdata_winreg", test_printer_data_winreg);
+ torture_tcase_add_simple_test(tcase, "printerdata_dsspooler", test_printer_data_dsspooler);
+ torture_tcase_add_simple_test(tcase, "driver_info_winreg", test_driver_info_winreg);
+ torture_tcase_add_simple_test(tcase, "printer_rename", test_printer_rename);
+ torture_tcase_add_simple_test(tcase, "printer_ic", test_printer_ic);
+ torture_tcase_add_simple_test(tcase, "bidi", test_printer_bidi);
+ torture_tcase_add_simple_test(tcase, "publish_toggle",
+ test_printer_publish_toggle);
+ torture_tcase_add_simple_test(tcase, "print_job_enum", test_print_job_enum);
+ torture_tcase_add_simple_test(tcase, "log_jobinfo", test_printer_log_jobinfo);
+ torture_tcase_add_simple_test(tcase, "os_versions", test_printer_os_versions);
+}
+
+struct torture_suite *torture_rpc_spoolss_printer(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "printer");
+ struct torture_tcase *tcase;
+
+ tcase = torture_suite_add_tcase(suite, "addprinter");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printer_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+ torture_tcase_printer(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "addprinterex");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printerex_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+ torture_tcase_printer(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "addprinterwkn");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printerwkn_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+ tcase = torture_suite_add_tcase(suite, "addprinterexwkn");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printerexwkn_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+#if 0
+ /* test is not correct */
+ tcase = torture_suite_add_tcase(suite, "addprinterdm");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_printerdm_setup,
+ torture_rpc_spoolss_printer_teardown);
+
+ torture_tcase_printer(tcase);
+#endif
+ return suite;
+}
+
+struct torture_suite *torture_rpc_spoolss(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss");
+ struct torture_tcase *tcase = torture_suite_add_tcase(suite, "printserver");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_setup,
+ torture_rpc_spoolss_teardown);
+
+ torture_tcase_add_simple_test(tcase, "openprinter_badnamelist", test_OpenPrinter_badname_list);
+ torture_tcase_add_simple_test(tcase, "printer_data_list", test_GetPrinterData_list);
+ torture_tcase_add_simple_test(tcase, "enum_forms", test_PrintServer_EnumForms);
+ torture_tcase_add_simple_test(tcase, "forms", test_PrintServer_Forms);
+ torture_tcase_add_simple_test(tcase, "forms_winreg", test_PrintServer_Forms_Winreg);
+ torture_tcase_add_simple_test(tcase, "enum_ports", test_EnumPorts);
+ torture_tcase_add_simple_test(tcase, "add_port", test_AddPort);
+ torture_tcase_add_simple_test(tcase, "get_printer_driver_directory", test_GetPrinterDriverDirectory);
+ torture_tcase_add_simple_test(tcase, "get_print_processor_directory", test_GetPrintProcessorDirectory);
+ torture_tcase_add_simple_test(tcase, "enum_printer_drivers", test_EnumPrinterDrivers);
+ torture_tcase_add_simple_test(tcase, "enum_monitors", test_EnumMonitors);
+ torture_tcase_add_simple_test(tcase, "enum_print_processors", test_EnumPrintProcessors);
+ torture_tcase_add_simple_test(tcase, "print_processors_winreg", test_print_processors_winreg);
+ torture_tcase_add_simple_test(tcase, "add_processor", test_add_print_processor);
+ torture_tcase_add_simple_test(tcase, "enum_printprocdata", test_EnumPrintProcessorDataTypes);
+ torture_tcase_add_simple_test(tcase, "enum_printers", test_EnumPrinters);
+ torture_tcase_add_simple_test(tcase, "enum_ports_old", test_EnumPorts_old);
+ torture_tcase_add_simple_test(tcase, "enum_printers_old", test_EnumPrinters_old);
+ torture_tcase_add_simple_test(tcase, "enum_printers_servername", test_EnumPrinters_servername);
+ torture_tcase_add_simple_test(tcase, "enum_printer_drivers_old", test_EnumPrinterDrivers_old);
+ torture_tcase_add_simple_test(tcase, "architecture_buffer", test_architecture_buffer);
+ torture_tcase_add_simple_test(tcase, "get_core_printer_drivers", test_get_core_printer_drivers);
+ torture_tcase_add_simple_test(tcase, "get_printer_driver_package_path", test_get_printer_driver_package_path);
+ torture_tcase_add_simple_test(tcase, "get_printer", test_get_printer_printserverhandle);
+ torture_tcase_add_simple_test(tcase, "set_printer", test_set_printer_printserverhandle);
+ torture_tcase_add_simple_test(tcase, "printserver_info_winreg", test_printserver_info_winreg);
+ torture_tcase_add_simple_test(tcase, "addpermachineconnection", test_addpermachineconnection);
+
+ torture_suite_add_suite(suite, torture_rpc_spoolss_printer(suite));
+
+ return suite;
+}
+
+static bool test_GetPrinterDriverDirectory_getdir(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server,
+ const char *environment,
+ const char **dir_p)
+{
+ struct spoolss_GetPrinterDriverDirectory r;
+ uint32_t needed;
+
+ r.in.server = server;
+ r.in.environment = environment;
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r),
+ "failed to query driver directory");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_GetPrinterDriverDirectory_r(b, tctx, &r),
+ "failed to query driver directory");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to query driver directory");
+
+ if (dir_p) {
+ *dir_p = r.out.info->info1.directory_name;
+ }
+
+ return true;
+}
+
+static const char *get_driver_from_info(struct spoolss_AddDriverInfoCtr *info_ctr)
+{
+ if (info_ctr == NULL) {
+ return NULL;
+ }
+
+ switch (info_ctr->level) {
+ case 1:
+ return info_ctr->info.info1->driver_name;
+ case 2:
+ return info_ctr->info.info2->driver_name;
+ case 3:
+ return info_ctr->info.info3->driver_name;
+ case 4:
+ return info_ctr->info.info4->driver_name;
+ case 6:
+ return info_ctr->info.info6->driver_name;
+ case 8:
+ return info_ctr->info.info8->driver_name;
+ default:
+ return NULL;
+ }
+}
+
+static const char *get_environment_from_info(struct spoolss_AddDriverInfoCtr *info_ctr)
+{
+ if (info_ctr == NULL) {
+ return NULL;
+ }
+
+ switch (info_ctr->level) {
+ case 2:
+ return info_ctr->info.info2->architecture;
+ case 3:
+ return info_ctr->info.info3->architecture;
+ case 4:
+ return info_ctr->info.info4->architecture;
+ case 6:
+ return info_ctr->info.info6->architecture;
+ case 8:
+ return info_ctr->info.info8->architecture;
+ default:
+ return NULL;
+ }
+}
+
+
+static bool test_AddPrinterDriver_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ struct spoolss_AddDriverInfoCtr *info_ctr,
+ WERROR expected_result)
+{
+ struct spoolss_AddPrinterDriver r;
+ const char *drivername = get_driver_from_info(info_ctr);
+ const char *environment = get_environment_from_info(info_ctr);
+
+ r.in.servername = servername;
+ r.in.info_ctr = info_ctr;
+
+ torture_comment(tctx, "Testing AddPrinterDriver(%s) level: %d, environment: '%s'\n",
+ drivername, info_ctr->level, environment);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_AddPrinterDriver_r(b, tctx, &r),
+ "spoolss_AddPrinterDriver failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "spoolss_AddPrinterDriver failed with unexpected result");
+
+ return true;
+
+}
+
+static bool test_AddPrinterDriverEx_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *servername,
+ struct spoolss_AddDriverInfoCtr *info_ctr,
+ uint32_t flags,
+ WERROR expected_result)
+{
+ struct spoolss_AddPrinterDriverEx r;
+ const char *drivername = get_driver_from_info(info_ctr);
+ const char *environment = get_environment_from_info(info_ctr);
+
+ r.in.servername = servername;
+ r.in.info_ctr = info_ctr;
+ r.in.flags = flags;
+
+ torture_comment(tctx, "Testing AddPrinterDriverEx(%s) level: %d, environment: '%s'\n",
+ drivername, info_ctr->level, environment);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_AddPrinterDriverEx_r(b, tctx, &r),
+ "AddPrinterDriverEx failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "AddPrinterDriverEx failed with unexpected result");
+
+ return true;
+}
+
+#define ASSERT_DRIVER_PATH(tctx, path, driver_dir, cmt) \
+ if (path && strlen(path)) {\
+ torture_assert_strn_equal(tctx, path, driver_dir, strlen(driver_dir), cmt); \
+ }
+
+static bool test_AddPrinterDriver_args_level_1(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo1 info1;
+
+ ZERO_STRUCT(info1);
+
+ info_ctr.level = 1;
+ info_ctr.info.info1 = &info1;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriverEx level 1");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriver level 1");
+ }
+
+ info1.driver_name = r->driver_name;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriverEx level 1");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriver level 1");
+ }
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_2(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo2 info2;
+ union spoolss_DriverInfo info;
+
+ ZERO_STRUCT(info2);
+
+ info_ctr.level = 2;
+ info_ctr.info.info2 = &info2;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.driver_name = r->driver_name;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.version = r->version;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.architecture = r->architecture;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.driver_path = r->driver_path;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.data_file = r->data_file;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ info2.config_file = r->config_file;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, 0, WERR_INVALID_PARAMETER),
+ "failed to test AddPrinterDriverEx");
+ }
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 2");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK),
+ "failed to test AddPrinterDriver level 2");
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 2, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ ASSERT_DRIVER_PATH(tctx, info.info2.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info2.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info2.config_file, remote_driver_dir, "unexpected path");
+ }
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_3(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo3 info3;
+ union spoolss_DriverInfo info;
+
+ info3.driver_name = r->driver_name;
+ info3.version = r->version;
+ info3.architecture = r->architecture;
+ info3.driver_path = r->driver_path;
+ info3.data_file = r->data_file;
+ info3.config_file = r->config_file;
+ info3.help_file = r->help_file;
+ info3.monitor_name = r->monitor_name;
+ info3.default_datatype = r->default_datatype;
+ info3._ndr_size_dependent_files = r->_ndr_size_dependent_files;
+ info3.dependent_files = r->dependent_files;
+
+ info_ctr.level = 3;
+ info_ctr.info.info3 = &info3;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 3");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK),
+ "failed to test AddPrinterDriver level 3");
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 3, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ int i;
+ ASSERT_DRIVER_PATH(tctx, info.info3.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info3.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info3.config_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info3.help_file, remote_driver_dir, "unexpected path");
+ for (i=0; info.info3.dependent_files && info.info3.dependent_files[i] != NULL; i++) {
+ ASSERT_DRIVER_PATH(tctx, info.info3.dependent_files[i], remote_driver_dir, "unexpected path");
+ }
+ }
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_4(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo4 info4;
+ union spoolss_DriverInfo info;
+
+ info4.version = r->version;
+ info4.driver_name = r->driver_name;
+ info4.architecture = r->architecture;
+ info4.driver_path = r->driver_path;
+ info4.data_file = r->data_file;
+ info4.config_file = r->config_file;
+ info4.help_file = r->help_file;
+ info4.monitor_name = r->monitor_name;
+ info4.default_datatype = r->default_datatype;
+ info4._ndr_size_dependent_files = r->_ndr_size_dependent_files;
+ info4.dependent_files = r->dependent_files;
+ info4._ndr_size_previous_names = r->_ndr_size_previous_names;
+ info4.previous_names = r->previous_names;
+
+ info_ctr.level = 4;
+ info_ctr.info.info4 = &info4;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 4");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_OK),
+ "failed to test AddPrinterDriver level 4");
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 4, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ int i;
+ ASSERT_DRIVER_PATH(tctx, info.info4.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info4.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info4.config_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info4.help_file, remote_driver_dir, "unexpected path");
+ for (i=0; info.info4.dependent_files && info.info4.dependent_files[i] != NULL; i++) {
+ ASSERT_DRIVER_PATH(tctx, info.info4.dependent_files[i], remote_driver_dir, "unexpected path");
+ }
+ }
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_6(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ struct spoolss_AddDriverInfo6 info6;
+ union spoolss_DriverInfo info;
+
+ info6.version = r->version;
+ info6.driver_name = r->driver_name;
+ info6.architecture = r->architecture;
+ info6.driver_path = r->driver_path;
+ info6.data_file = r->data_file;
+ info6.config_file = r->config_file;
+ info6.help_file = r->help_file;
+ info6.monitor_name = r->monitor_name;
+ info6.default_datatype = r->default_datatype;
+ info6._ndr_size_dependent_files = r->_ndr_size_dependent_files;
+ info6.dependent_files = r->dependent_files;
+ info6._ndr_size_previous_names = r->_ndr_size_previous_names;
+ info6.previous_names = r->previous_names;
+ info6.driver_date = r->driver_date;
+ info6.driver_version = r->driver_version;
+ info6.manufacturer_name = r->manufacturer_name;
+ info6.manufacturer_url = r->manufacturer_url;
+ info6.hardware_id = r->hardware_id;
+ info6.provider = r->provider;
+
+ info_ctr.level = 6;
+ info_ctr.info.info6 = &info6;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 6");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriver level 6");
+ }
+
+ /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
+
+ if (!ex) {
+ return true;
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 6, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ int i;
+ ASSERT_DRIVER_PATH(tctx, info.info6.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info6.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info6.config_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info6.help_file, remote_driver_dir, "unexpected path");
+ for (i=0; info.info6.dependent_files && info.info6.dependent_files[i] != NULL; i++) {
+ ASSERT_DRIVER_PATH(tctx, info.info6.dependent_files[i], remote_driver_dir, "unexpected path");
+ }
+ }
+
+ torture_assert_nttime_equal(tctx, info.info6.driver_date, info6.driver_date, "driverdate mismatch");
+ torture_assert_u64_equal(tctx, info.info6.driver_version, info6.driver_version, "driverversion mismatch");
+
+ return true;
+}
+
+static bool test_AddPrinterDriver_args_level_8(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t flags,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ struct spoolss_AddDriverInfoCtr info_ctr;
+ union spoolss_DriverInfo info;
+
+ info_ctr.level = 8;
+ info_ctr.info.info8 = r;
+
+ if (ex) {
+ torture_assert(tctx,
+ test_AddPrinterDriverEx_exp(tctx, b, server_name, &info_ctr, flags, WERR_OK),
+ "failed to test AddPrinterDriverEx level 8");
+ } else {
+ torture_assert(tctx,
+ test_AddPrinterDriver_exp(tctx, b, server_name, &info_ctr, WERR_INVALID_LEVEL),
+ "failed to test AddPrinterDriver level 8");
+ }
+
+ /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
+
+ if (!ex) {
+ return true;
+ }
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name, r->architecture, 8, r->driver_name, &info),
+ "failed to find added printer driver");
+
+ if (remote_driver_dir) {
+ int i;
+ ASSERT_DRIVER_PATH(tctx, info.info8.driver_path, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info8.data_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info8.config_file, remote_driver_dir, "unexpected path");
+ ASSERT_DRIVER_PATH(tctx, info.info8.help_file, remote_driver_dir, "unexpected path");
+ for (i=0; info.info8.dependent_files && info.info8.dependent_files[i] != NULL; i++) {
+ ASSERT_DRIVER_PATH(tctx, info.info8.dependent_files[i], remote_driver_dir, "unexpected path");
+ }
+ }
+
+ torture_assert_nttime_equal(tctx, info.info8.driver_date, r->driver_date, "driverdate mismatch");
+ torture_assert_u64_equal(tctx, info.info8.driver_version, r->driver_version, "driverversion mismatch");
+
+ return true;
+}
+
+#undef ASSERT_DRIVER_PATH
+
+static bool test_DeletePrinterDriver_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server,
+ const char *driver,
+ const char *environment,
+ WERROR expected_result)
+{
+ struct spoolss_DeletePrinterDriver r;
+
+ r.in.server = server;
+ r.in.architecture = environment;
+ r.in.driver = driver;
+
+ torture_comment(tctx, "Testing DeletePrinterDriver(%s)\n", driver);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterDriver_r(b, tctx, &r),
+ "DeletePrinterDriver failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "DeletePrinterDriver failed with unexpected result");
+
+ return true;
+}
+
+static bool test_DeletePrinterDriverEx_exp(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server,
+ const char *driver,
+ const char *environment,
+ uint32_t delete_flags,
+ uint32_t version,
+ WERROR expected_result)
+{
+ struct spoolss_DeletePrinterDriverEx r;
+
+ r.in.server = server;
+ r.in.architecture = environment;
+ r.in.driver = driver;
+ r.in.delete_flags = delete_flags;
+ r.in.version = version;
+
+ torture_comment(tctx, "Testing DeletePrinterDriverEx(%s)\n", driver);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_DeletePrinterDriverEx_r(b, tctx, &r),
+ "DeletePrinterDriverEx failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "DeletePrinterDriverEx failed with unexpected result");
+
+ return true;
+}
+
+static bool test_DeletePrinterDriver(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *driver,
+ const char *environment)
+{
+ torture_assert(tctx,
+ test_DeletePrinterDriver_exp(tctx, b, server_name, driver, "FOOBAR", WERR_INVALID_ENVIRONMENT),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_OK),
+ "failed to delete driver");
+
+ if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver, NULL)) {
+ torture_fail(tctx, "deleted driver still enumerated");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDriver_exp(tctx, b, server_name, driver, environment, WERR_UNKNOWN_PRINTER_DRIVER),
+ "2nd delete failed");
+
+ return true;
+}
+
+static bool test_DeletePrinterDriverEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ const char *driver,
+ const char *environment,
+ uint32_t delete_flags,
+ uint32_t version)
+{
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, "FOOBAR", delete_flags, version, WERR_INVALID_ENVIRONMENT),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, environment, delete_flags, version, WERR_OK),
+ "failed to delete driver");
+
+ if (test_EnumPrinterDrivers_findone(tctx, b, server_name, environment, 1, driver, NULL)) {
+ torture_fail(tctx, "deleted driver still enumerated");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name, driver, environment, delete_flags, version, WERR_UNKNOWN_PRINTER_DRIVER),
+ "2nd delete failed");
+
+ return true;
+}
+
+static bool test_PrinterDriver_args(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ const char *server_name,
+ uint32_t level,
+ struct spoolss_AddDriverInfo8 *r,
+ uint32_t add_flags,
+ uint32_t delete_flags,
+ uint32_t delete_version,
+ bool ex,
+ const char *remote_driver_dir)
+{
+ bool ret = true;
+
+ switch (level) {
+ case 1:
+ ret = test_AddPrinterDriver_args_level_1(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 2:
+ ret = test_AddPrinterDriver_args_level_2(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 3:
+ ret = test_AddPrinterDriver_args_level_3(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 4:
+ ret = test_AddPrinterDriver_args_level_4(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 6:
+ ret = test_AddPrinterDriver_args_level_6(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ case 8:
+ ret = test_AddPrinterDriver_args_level_8(tctx, b, server_name, r, add_flags, ex, remote_driver_dir);
+ break;
+ default:
+ return false;
+ }
+
+ if (ret == false) {
+ return ret;
+ }
+
+ if (level == 1) {
+ return ret;
+ }
+
+ /* spoolss_AddPrinterDriver does not deal with level 6 or 8 - gd */
+
+ if (!ex && (level == 6 || level == 8)) {
+ return ret;
+ }
+
+ {
+ struct dcerpc_pipe *p2;
+ struct policy_handle hive_handle;
+ struct dcerpc_binding_handle *b2;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &p2, &ndr_table_winreg),
+ "could not open winreg pipe");
+ b2 = p2->binding_handle;
+
+ torture_assert(tctx, test_winreg_OpenHKLM(tctx, b2, &hive_handle), "");
+
+ ret = test_GetDriverInfo_winreg(tctx, b, NULL, NULL, r->driver_name, r->architecture, r->version, b2, &hive_handle, server_name);
+
+ test_winreg_CloseKey(tctx, b2, &hive_handle);
+
+ talloc_free(p2);
+ }
+
+ if (ex) {
+ return test_DeletePrinterDriverEx(tctx, b, server_name, r->driver_name, r->architecture, delete_flags, r->version);
+ } else {
+ return test_DeletePrinterDriver(tctx, b, server_name, r->driver_name, r->architecture);
+ }
+}
+
+static bool fillup_printserver_info(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct torture_driver_context *d)
+{
+ struct policy_handle server_handle;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ torture_assert(tctx,
+ test_OpenPrinter_server(tctx, p, &server_handle),
+ "failed to open printserver");
+ torture_assert(tctx,
+ test_get_environment(tctx, b, &server_handle, &d->remote.environment),
+ "failed to get environment");
+ torture_assert(tctx,
+ test_ClosePrinter(tctx, b, &server_handle),
+ "failed to close printserver");
+
+ torture_assert(tctx,
+ test_GetPrinterDriverDirectory_getdir(tctx, b, server_name_slash,
+ d->local.environment ? d->local.environment : d->remote.environment,
+ &d->remote.driver_directory),
+ "failed to get driver directory");
+
+ return true;
+}
+
+static const char *driver_directory_dir(const char *driver_directory)
+{
+ char *p;
+
+ p = strrchr(driver_directory, '\\');
+ if (p) {
+ return p+1;
+ }
+
+ return NULL;
+}
+
+static const char *driver_directory_share(struct torture_context *tctx,
+ const char *driver_directory)
+{
+ const char *p;
+ char *tok;
+
+ if (driver_directory[0] == '\\' && driver_directory[1] == '\\') {
+ driver_directory += 2;
+ }
+
+ p = talloc_strdup(tctx, driver_directory);
+
+ torture_assert(tctx,
+ next_token_talloc(tctx, &p, &tok, "\\"),
+ "cannot explode uri");
+ torture_assert(tctx,
+ next_token_talloc(tctx, &p, &tok, "\\"),
+ "cannot explode uri");
+
+ return tok;
+}
+
+#define CREATE_PRINTER_DRIVER_PATH(_d, _file) \
+ talloc_asprintf((_d), "%s\\%s\\%s", (_d)->remote.driver_directory, (_d)->remote.driver_upload_directory, (_file))
+
+
+static bool create_printer_driver_directory(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct torture_driver_context *d)
+{
+ char *driver_dir;
+
+ if (d->remote.driver_upload_directory == NULL) {
+ return true;
+ }
+
+ driver_dir = talloc_asprintf(tctx,
+ "%s\\%s",
+ driver_directory_dir(d->remote.driver_directory),
+ d->remote.driver_upload_directory);
+ torture_assert_not_null(tctx, driver_dir, "ENOMEM");
+
+ torture_comment(tctx,
+ "Create remote driver directory: %s\n",
+ driver_dir);
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_mkdir(cli->tree,
+ driver_dir),
+ "Failed to create driver directory");
+
+ return true;
+}
+
+static bool upload_printer_driver_file(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct torture_driver_context *d,
+ const char *file_name)
+{
+ FILE *f;
+ int fnum;
+ uint8_t *buf;
+ int maxwrite = 64512;
+ off_t nread = 0;
+ size_t start = 0;
+ const char *remote_dir = driver_directory_dir(d->remote.driver_directory);
+ const char *remote_name;
+ const char *local_name;
+ const char *p;
+
+ if (!file_name || strlen(file_name) == 0) {
+ return true;
+ }
+
+ p = strrchr(file_name, '\\');
+ if (p == NULL) {
+ p = file_name;
+ } else {
+ p++;
+ }
+
+ local_name = talloc_asprintf(tctx, "%s/%s", d->local.driver_directory, p);
+ torture_assert_not_null(tctx, local_name, "ENOMEM");
+ if (d->remote.driver_upload_directory != NULL) {
+ remote_name = talloc_asprintf(tctx,
+ "%s\\%s\\%s",
+ remote_dir,
+ d->remote.driver_upload_directory,
+ p);
+ } else {
+ remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, p);
+ }
+ torture_assert_not_null(tctx, remote_name, "ENOMEM");
+
+ torture_comment(tctx, "Uploading %s to %s\n", local_name, remote_name);
+
+ fnum = smbcli_open(cli->tree, remote_name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+ if (fnum == -1) {
+ torture_fail(tctx, talloc_asprintf(tctx, "failed to open remote file: %s\n", remote_name));
+ }
+
+ f = fopen(local_name, "r");
+ if (f == NULL) {
+ torture_fail(tctx, talloc_asprintf(tctx, "failed to open local file: %s\n", local_name));
+ }
+
+ buf = talloc_array(tctx, uint8_t, maxwrite);
+ if (!buf) {
+ fclose(f);
+ return false;
+ }
+
+ while (!feof(f)) {
+ int n = maxwrite;
+ int ret;
+
+ if ((n = fread(buf, 1, n, f)) < 1) {
+ if((n == 0) && feof(f))
+ break; /* Empty local file. */
+
+ torture_warning(tctx,
+ "failed to read file: %s\n", strerror(errno));
+ break;
+ }
+
+ ret = smbcli_write(cli->tree, fnum, 0, buf, nread + start, n);
+
+ if (n != ret) {
+ torture_warning(tctx,
+ "failed to write file: %s\n", smbcli_errstr(cli->tree));
+ break;
+ }
+
+ nread += n;
+ }
+
+ fclose(f);
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_close(cli->tree, fnum),
+ "failed to close file");
+
+ return true;
+}
+
+static bool connect_printer_driver_share(struct torture_context *tctx,
+ const char *server_name,
+ const char *share_name,
+ struct smbcli_state **cli)
+{
+ struct smbcli_options smb_options;
+ struct smbcli_session_options smb_session_options;
+
+ torture_comment(tctx, "Connecting printer driver share '%s' on '%s'\n",
+ share_name, server_name);
+
+ lpcfg_smbcli_options(tctx->lp_ctx, &smb_options);
+ lpcfg_smbcli_session_options(tctx->lp_ctx, &smb_session_options);
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_full_connection(tctx, cli, server_name,
+ lpcfg_smb_ports(tctx->lp_ctx),
+ share_name, NULL,
+ lpcfg_socket_options(tctx->lp_ctx),
+ samba_cmdline_get_creds(),
+ lpcfg_resolve_context(tctx->lp_ctx),
+ tctx->ev,
+ &smb_options,
+ &smb_session_options,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx)),
+ "failed to open driver share");
+
+ return true;
+}
+
+static bool upload_printer_driver(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d)
+{
+ struct smbcli_state *cli;
+ const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+ int i;
+
+ torture_assert(tctx,
+ connect_printer_driver_share(tctx, server_name, share_name, &cli),
+ "failed to connect to driver share");
+
+ torture_comment(tctx, "Uploading printer driver files to \\\\%s\\%s\n",
+ server_name, share_name);
+
+ torture_assert(tctx,
+ create_printer_driver_directory(tctx, cli, d),
+ "failed to create driver directory");
+
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.driver_path),
+ "failed to upload driver_path");
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.data_file),
+ "failed to upload data_file");
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.config_file),
+ "failed to upload config_file");
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.help_file),
+ "failed to upload help_file");
+ if (d->info8.dependent_files) {
+ for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+ torture_assert(tctx,
+ upload_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]),
+ "failed to upload dependent_files");
+ }
+ }
+
+ talloc_free(cli);
+
+ return true;
+}
+
+static bool check_printer_driver_file(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct torture_driver_context *d,
+ const char *file_name)
+{
+ const char *remote_arch_dir = driver_directory_dir(d->remote.driver_directory);
+ const char *remote_name = talloc_asprintf(tctx, "%s\\%d\\%s",
+ remote_arch_dir,
+ d->info8.version,
+ file_name);
+ int fnum;
+
+ torture_assert(tctx, (file_name && strlen(file_name) != 0), "invalid filename");
+
+ torture_comment(tctx, "checking for driver file at %s\n", remote_name);
+
+ fnum = smbcli_open(cli->tree, remote_name, O_RDONLY, DENY_NONE);
+ if (fnum == -1) {
+ return false;
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_close(cli->tree, fnum),
+ "failed to close driver file");
+
+ return true;
+}
+
+static bool check_printer_driver_files(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d,
+ bool expect_exist)
+{
+ struct smbcli_state *cli;
+ const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+ int i;
+
+ torture_assert(tctx,
+ connect_printer_driver_share(tctx, server_name, share_name, &cli),
+ "failed to connect to driver share");
+
+ torture_comment(tctx, "checking %sexistent driver files at \\\\%s\\%s\n",
+ (expect_exist ? "": "non-"),
+ server_name, share_name);
+
+ if (d->info8.driver_path && d->info8.driver_path[0]) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.driver_path) == expect_exist,
+ "failed driver_path check");
+ }
+ if (d->info8.data_file && d->info8.data_file[0]) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.data_file) == expect_exist,
+ "failed data_file check");
+ }
+ if (d->info8.config_file && d->info8.config_file[0]) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.config_file) == expect_exist,
+ "failed config_file check");
+ }
+ if (d->info8.help_file && d->info8.help_file[0]) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.help_file) == expect_exist,
+ "failed help_file check");
+ }
+ if (d->info8.dependent_files) {
+ for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+ torture_assert(tctx,
+ check_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]) == expect_exist,
+ "failed dependent_files check");
+ }
+ }
+
+ talloc_free(cli);
+
+ return true;
+}
+
+static bool remove_printer_driver_file(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct torture_driver_context *d,
+ const char *file_name)
+{
+ const char *remote_name;
+ const char *remote_dir = driver_directory_dir(d->remote.driver_directory);
+
+ if (!file_name || strlen(file_name) == 0) {
+ return true;
+ }
+
+ remote_name = talloc_asprintf(tctx, "%s\\%s", remote_dir, file_name);
+
+ torture_comment(tctx, "Removing %s\n", remote_name);
+
+ torture_assert_ntstatus_ok(tctx,
+ smbcli_unlink(cli->tree, remote_name),
+ "failed to unlink");
+
+ return true;
+}
+
+static bool remove_printer_driver(struct torture_context *tctx,
+ const char *server_name,
+ struct torture_driver_context *d)
+{
+ struct smbcli_state *cli;
+ const char *share_name = driver_directory_share(tctx, d->remote.driver_directory);
+ int i;
+
+ torture_assert(tctx,
+ connect_printer_driver_share(tctx, server_name, share_name, &cli),
+ "failed to connect to driver share");
+
+ torture_comment(tctx, "Removing printer driver files from \\\\%s\\%s\n",
+ server_name, share_name);
+
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.driver_path),
+ "failed to remove driver_path");
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.data_file),
+ "failed to remove data_file");
+ if (!strequal(d->info8.config_file, d->info8.driver_path)) {
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.config_file),
+ "failed to remove config_file");
+ }
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.help_file),
+ "failed to remove help_file");
+ if (d->info8.dependent_files) {
+ for (i=0; d->info8.dependent_files->string && d->info8.dependent_files->string[i] != NULL; i++) {
+ if (strequal(d->info8.dependent_files->string[i], d->info8.driver_path) ||
+ strequal(d->info8.dependent_files->string[i], d->info8.data_file) ||
+ strequal(d->info8.dependent_files->string[i], d->info8.config_file) ||
+ strequal(d->info8.dependent_files->string[i], d->info8.help_file)) {
+ continue;
+ }
+ torture_assert(tctx,
+ remove_printer_driver_file(tctx, cli, d, d->info8.dependent_files->string[i]),
+ "failed to remove dependent_files");
+ }
+ }
+
+ talloc_free(cli);
+
+ return true;
+
+}
+
+static bool test_add_driver_arg(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct torture_driver_context *d)
+{
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ uint32_t levels[] = { 1, 2, 3, 4, 6, 8 };
+ int i;
+ struct spoolss_AddDriverInfo8 info8;
+ uint32_t add_flags = APD_COPY_NEW_FILES;
+ uint32_t delete_flags = 0;
+
+ ZERO_STRUCT(info8);
+
+ torture_comment(tctx, "Testing PrinterDriver%s '%s' for environment '%s'\n",
+ d->ex ? "Ex" : "", d->info8.driver_name, d->local.environment);
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d),
+ "failed to fillup printserver info");
+
+ if (!directory_exist(d->local.driver_directory)) {
+ torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to upload printer driver");
+
+ info8 = d->info8;
+ if (d->info8.dependent_files) {
+ info8.dependent_files = talloc_zero(tctx, struct spoolss_StringArray);
+ if (d->info8.dependent_files->string) {
+ for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+ }
+ info8.dependent_files->string = talloc_zero_array(info8.dependent_files, const char *, i+1);
+ for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+ info8.dependent_files->string[i] = talloc_strdup(info8.dependent_files->string, d->info8.dependent_files->string[i]);
+ }
+ }
+ }
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ switch (levels[i]) {
+ case 2:
+ case 4:
+ torture_comment(tctx, "skipping level %d against samba\n", levels[i]);
+ continue;
+ default:
+ break;
+ }
+ }
+ if (torture_setting_bool(tctx, "w2k3", false)) {
+ switch (levels[i]) {
+ case 8:
+ torture_comment(tctx, "skipping level %d against w2k3\n", levels[i]);
+ continue;
+ default:
+ break;
+ }
+ }
+
+ torture_comment(tctx,
+ "Testing PrinterDriver%s '%s' add & delete level %d\n",
+ d->ex ? "Ex" : "", info8.driver_name, levels[i]);
+
+ ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex, d->remote.driver_directory);
+ }
+
+ info8.driver_path = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.driver_path);
+ info8.data_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.data_file);
+ if (d->info8.config_file) {
+ info8.config_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.config_file);
+ }
+ if (d->info8.help_file) {
+ info8.help_file = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.help_file);
+ }
+ if (d->info8.dependent_files && d->info8.dependent_files->string) {
+ for (i=0; d->info8.dependent_files->string[i] != NULL; i++) {
+ info8.dependent_files->string[i] = talloc_asprintf(tctx, "%s\\%s", d->remote.driver_directory, d->info8.dependent_files->string[i]);
+ }
+ }
+
+ for (i=0; i < ARRAY_SIZE(levels); i++) {
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ switch (levels[i]) {
+ case 2:
+ case 4:
+ continue;
+ default:
+ break;
+ }
+ }
+ if (torture_setting_bool(tctx, "w2k3", false)) {
+ switch (levels[i]) {
+ case 8:
+ torture_comment(tctx, "skipping level %d against w2k3\n", levels[i]);
+ continue;
+ default:
+ break;
+ }
+ }
+
+ torture_comment(tctx,
+ "Testing PrinterDriver%s '%s' add & delete level %d (full unc paths)\n",
+ d->ex ? "Ex" : "", info8.driver_name, levels[i]);
+
+ ret &= test_PrinterDriver_args(tctx, b, server_name_slash, levels[i], &info8, add_flags, delete_flags, d->info8.version, d->ex, d->remote.driver_directory);
+ }
+
+ torture_assert(tctx,
+ remove_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to remove printer driver");
+
+ torture_comment(tctx, "\n");
+
+ return ret;
+}
+
+static bool test_add_driver_ex_64(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_EX;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->ex = true;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_ex_32(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_EX;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->ex = true;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_64(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_ADD;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->ex = false;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_32(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_ADD;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->ex = false;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_adobe(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping adobe test which only works against samba3");
+ }
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, "Windows 4.0");
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/adobe/");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_9X;
+ d->info8.driver_name = TORTURE_DRIVER_ADOBE;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "ADOBEPS4.DRV");
+ d->info8.data_file = talloc_strdup(d, "DEFPRTR2.PPD");
+ d->info8.config_file = talloc_strdup(d, "ADOBEPS4.DRV");
+#if 0
+ d->info8.help_file = talloc_strdup(d, "ADOBEPS4.HLP");
+ d->info8.monitor_name = talloc_strdup(d, "PSMON.DLL");
+#endif
+ d->ex = false;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_adobe_cupsaddsmb(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+ struct spoolss_StringArray *a;
+
+ if (!torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping cupsaddsmb test which only works against samba3");
+ }
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, "Windows 4.0");
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/adobe/");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_9X;
+ d->info8.driver_name = TORTURE_DRIVER_ADOBE_CUPSADDSMB;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "ADOBEPS4.DRV");
+ d->info8.data_file = talloc_strdup(d, "DEFPRTR2.PPD");
+ d->info8.config_file = NULL;
+ d->info8.help_file = talloc_strdup(d, "ADOBEPS4.HLP");
+ d->info8.monitor_name = talloc_strdup(d, "PSMON.DLL");
+ d->info8.default_datatype = talloc_strdup(d, "RAW");
+
+ a = talloc_zero(d, struct spoolss_StringArray);
+ a->string = talloc_zero_array(a, const char *, 7);
+ a->string[0] = talloc_strdup(a->string, "ADOBEPS4.DRV");
+ a->string[1] = talloc_strdup(a->string, "DEFPRTR2.PPD");
+ a->string[2] = talloc_strdup(a->string, "ADOBEPS4.HLP");
+ a->string[3] = talloc_strdup(a->string, "PSMON.DLL");
+ a->string[4] = talloc_strdup(a->string, "ADFONTS.MFM");
+ a->string[5] = talloc_strdup(a->string, "ICONLIB.DLL");
+
+ d->info8.dependent_files = a;
+ d->ex = false;
+
+ return test_add_driver_arg(tctx, p, d);
+}
+
+static bool test_add_driver_timestamps(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+ struct timeval t = timeval_current();
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_TIMESTAMPS;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->info8.driver_date = timeval_to_nttime(&t);
+ d->ex = true;
+
+ torture_assert(tctx,
+ test_add_driver_arg(tctx, p, d),
+ "");
+
+ unix_to_nt_time(&d->info8.driver_date, 1);
+
+ torture_assert(tctx,
+ test_add_driver_arg(tctx, p, d),
+ "");
+
+ return true;
+}
+
+static bool test_multiple_drivers(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ int i;
+ struct spoolss_AddDriverInfo8 info8;
+ uint32_t add_flags = APD_COPY_NEW_FILES;
+ uint32_t delete_flags = 0;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_NT_X86);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/i386");
+
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->info8.architecture = d->local.environment;
+ d->ex = true;
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d),
+ "failed to fillup printserver info");
+
+ if (!directory_exist(d->local.driver_directory)) {
+ torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to upload printer driver");
+
+ info8 = d->info8;
+
+ for (i=0; i < 3; i++) {
+ info8.driver_name = talloc_asprintf(d, "torture_test_driver_%d", i);
+
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &info8, add_flags, true, NULL),
+ "failed to add driver");
+ }
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_0", info8.architecture, delete_flags, info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_1", NULL),
+ "torture_test_driver_1 no longer on the server");
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL),
+ "torture_test_driver_2 no longer on the server");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_1", info8.architecture, delete_flags, info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ test_EnumPrinterDrivers_findone(tctx, b, server_name_slash, info8.architecture, 3, "torture_test_driver_2", NULL),
+ "torture_test_driver_2 no longer on the server");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx, b, server_name_slash, "torture_test_driver_2", info8.architecture, delete_flags, info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ remove_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to remove printer driver");
+
+ return true;
+}
+
+static bool test_driver_copy_from_directory(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *architecture)
+{
+ struct torture_driver_context *d;
+ struct spoolss_StringArray *a;
+ uint32_t add_flags = APD_COPY_NEW_FILES|APD_COPY_FROM_DIRECTORY|APD_RETURN_BLOCKING_STATUS_CODE;
+ uint32_t delete_flags = DPD_DELETE_ALL_FILES;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx,
+ "\\\\%s",
+ dcerpc_server_name(p));
+ struct GUID guid = GUID_random();
+ bool ok = false;
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+ torture_assert_not_null(tctx, d, "ENOMEM");
+
+ d->local.environment = talloc_strdup(d, architecture);
+ torture_assert_not_null_goto(tctx, d->local.environment, ok, done, "ENOMEM");
+
+ if (strequal(architecture, SPOOLSS_ARCHITECTURE_x64)) {
+ d->local.driver_directory =
+ talloc_strdup(d, "/usr/share/cups/drivers/x64");
+ } else {
+ d->local.driver_directory =
+ talloc_strdup(d, "/usr/share/cups/drivers/i386");
+ }
+ torture_assert_not_null_goto(tctx, d->local.driver_directory, ok, done, "ENOMEM");
+
+ d->remote.driver_upload_directory = GUID_string2(d, &guid);
+ torture_assert_not_null_goto(tctx, d->remote.driver_upload_directory, ok, done, "ENOMEM");
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d),
+ "failed to fillup printserver info");
+
+ d->ex = true;
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_COPY_DIR;
+ d->info8.architecture = d->local.environment;
+
+ d->info8.driver_path = CREATE_PRINTER_DRIVER_PATH(d, "pscript5.dll");
+ torture_assert_not_null_goto(tctx, d->info8.driver_path, ok, done, "ENOMEM");
+ d->info8.data_file = CREATE_PRINTER_DRIVER_PATH(d, "cups6.ppd");
+ torture_assert_not_null_goto(tctx, d->info8.data_file, ok, done, "ENOMEM");
+ d->info8.config_file = CREATE_PRINTER_DRIVER_PATH(d, "cupsui6.dll");
+ torture_assert_not_null_goto(tctx, d->info8.config_file, ok, done, "ENOMEM");
+ d->info8.help_file = CREATE_PRINTER_DRIVER_PATH(d, "pscript.hlp");
+ torture_assert_not_null_goto(tctx, d->info8.help_file, ok, done, "ENOMEM");
+
+ a = talloc_zero(d, struct spoolss_StringArray);
+ torture_assert_not_null_goto(tctx, a, ok, done, "ENOMEM");
+ a->string = talloc_zero_array(a, const char *, 3);
+ torture_assert_not_null_goto(tctx, a->string, ok, done, "ENOMEM");
+ a->string[0] = CREATE_PRINTER_DRIVER_PATH(d, "cups6.inf");
+ torture_assert_not_null_goto(tctx, a->string[0], ok, done, "ENOMEM");
+ a->string[1] = CREATE_PRINTER_DRIVER_PATH(d, "cups6.ini");
+ torture_assert_not_null_goto(tctx, a->string[1], ok, done, "ENOMEM");
+
+ d->info8.dependent_files = a;
+
+ if (!directory_exist(d->local.driver_directory)) {
+ torture_skip(tctx,
+ "Skipping Printer Driver test as no local drivers "
+ "are available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to upload printer driver");
+
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx,
+ b,
+ server_name_slash,
+ &d->info8,
+ add_flags,
+ true,
+ NULL),
+ "failed to add driver");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx,
+ b,
+ server_name_slash,
+ d->info8.driver_name,
+ d->local.environment,
+ delete_flags,
+ d->info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ check_printer_driver_files(tctx,
+ dcerpc_server_name(p),
+ d,
+ false),
+ "printer driver file check failed");
+
+ ok = true;
+done:
+ talloc_free(d);
+ return ok;
+}
+
+static bool test_driver_copy_from_directory_64(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_driver_copy_from_directory(tctx, p, SPOOLSS_ARCHITECTURE_x64);
+}
+
+static bool test_driver_copy_from_directory_32(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_driver_copy_from_directory(tctx, p, SPOOLSS_ARCHITECTURE_NT_X86);
+}
+
+static bool test_del_driver_all_files(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d;
+ struct spoolss_StringArray *a;
+ uint32_t add_flags = APD_COPY_NEW_FILES;
+ uint32_t delete_flags = DPD_DELETE_ALL_FILES;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ d = talloc_zero(tctx, struct torture_driver_context);
+
+ d->local.environment = talloc_strdup(d, SPOOLSS_ARCHITECTURE_x64);
+ d->local.driver_directory = talloc_strdup(d, "/usr/share/cups/drivers/x64");
+
+ d->ex = true;
+ d->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d->info8.driver_name = TORTURE_DRIVER_DELETER;
+ d->info8.architecture = d->local.environment;
+ d->info8.driver_path = talloc_strdup(d, "pscript5.dll");
+ d->info8.data_file = talloc_strdup(d, "cups6.ppd");
+ d->info8.config_file = talloc_strdup(d, "cupsui6.dll");
+ d->info8.help_file = talloc_strdup(d, "pscript.hlp");
+
+ a = talloc_zero(d, struct spoolss_StringArray);
+ a->string = talloc_zero_array(a, const char *, 3);
+ a->string[0] = talloc_strdup(a->string, "cups6.inf");
+ a->string[1] = talloc_strdup(a->string, "cups6.ini");
+
+ d->info8.dependent_files = a;
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d),
+ "failed to fillup printserver info");
+
+ if (!directory_exist(d->local.driver_directory)) {
+ torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d),
+ "failed to upload printer driver");
+
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d->info8, add_flags, true, NULL),
+ "failed to add driver");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx(tctx, b, server_name_slash,
+ d->info8.driver_name,
+ d->info8.architecture,
+ delete_flags,
+ d->info8.version),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ check_printer_driver_files(tctx, dcerpc_server_name(p), d, false),
+ "printer driver file check failed");
+
+ talloc_free(d);
+ return true;
+}
+
+static bool test_del_driver_unused_files(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_driver_context *d1;
+ struct torture_driver_context *d2;
+ uint32_t add_flags = APD_COPY_NEW_FILES;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *server_name_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ d1 = talloc_zero(tctx, struct torture_driver_context);
+ d1->ex = true;
+
+ d1->local.environment = talloc_strdup(d1, SPOOLSS_ARCHITECTURE_x64);
+ d1->local.driver_directory = talloc_strdup(d1, "/usr/share/cups/drivers/x64");
+
+ d1->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d1->info8.driver_name = TORTURE_DRIVER_DELETER;
+ d1->info8.architecture = NULL;
+ d1->info8.driver_path = talloc_strdup(d1, "pscript5.dll");
+ d1->info8.data_file = talloc_strdup(d1, "cups6.ppd");
+ d1->info8.config_file = talloc_strdup(d1, "cupsui6.dll");
+ d1->info8.help_file = talloc_strdup(d1, "pscript.hlp");
+ d1->info8.architecture = d1->local.environment;
+
+ d2 = talloc_zero(tctx, struct torture_driver_context);
+ d2->ex = true;
+
+ d2->local.environment = talloc_strdup(d2, SPOOLSS_ARCHITECTURE_x64);
+ d2->local.driver_directory = talloc_strdup(d2, "/usr/share/cups/drivers/x64");
+
+ d2->info8.version = SPOOLSS_DRIVER_VERSION_200X;
+ d2->info8.driver_name = TORTURE_DRIVER_DELETERIN;
+ d2->info8.architecture = NULL;
+ d2->info8.driver_path = talloc_strdup(d2, "pscript5.dll"); /* overlapping */
+ d2->info8.data_file = talloc_strdup(d2, "cupsps6.dll");
+ d2->info8.config_file = talloc_strdup(d2, "cups6.ini");
+ d2->info8.help_file = talloc_strdup(d2, "pscript.hlp"); /* overlapping */
+ d2->info8.architecture = d2->local.environment;
+
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d1),
+ "failed to fillup printserver info");
+ torture_assert(tctx,
+ fillup_printserver_info(tctx, p, d2),
+ "failed to fillup printserver info");
+
+ if (!directory_exist(d1->local.driver_directory)) {
+ torture_skip(tctx, "Skipping Printer Driver test as no local driver is available");
+ }
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d1),
+ "failed to upload printer driver");
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d1->info8, add_flags, true, NULL),
+ "failed to add driver");
+
+ torture_assert(tctx,
+ upload_printer_driver(tctx, dcerpc_server_name(p), d2),
+ "failed to upload printer driver");
+ torture_assert(tctx,
+ test_AddPrinterDriver_args_level_3(tctx, b, server_name_slash, &d2->info8, add_flags, true, NULL),
+ "failed to add driver");
+
+ /* some files are in use by a separate driver, should fail */
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+ d1->info8.driver_name,
+ d1->info8.architecture,
+ DPD_DELETE_ALL_FILES,
+ d1->info8.version,
+ WERR_PRINTER_DRIVER_IN_USE),
+ "invalid delete driver response");
+
+ /* should only delete files not in use by other driver */
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+ d1->info8.driver_name,
+ d1->info8.architecture,
+ DPD_DELETE_UNUSED_FILES,
+ d1->info8.version,
+ WERR_OK),
+ "failed to delete driver (unused files)");
+
+ /* check non-overlapping were deleted */
+ d1->info8.driver_path = NULL;
+ d1->info8.help_file = NULL;
+ torture_assert(tctx,
+ check_printer_driver_files(tctx, dcerpc_server_name(p), d1, false),
+ "printer driver file check failed");
+ /* d2 files should be uneffected */
+ torture_assert(tctx,
+ check_printer_driver_files(tctx, dcerpc_server_name(p), d2, true),
+ "printer driver file check failed");
+
+ torture_assert(tctx,
+ test_DeletePrinterDriverEx_exp(tctx, b, server_name_slash,
+ d2->info8.driver_name,
+ d2->info8.architecture,
+ DPD_DELETE_ALL_FILES,
+ d2->info8.version,
+ WERR_OK),
+ "failed to delete driver");
+
+ torture_assert(tctx,
+ check_printer_driver_files(tctx, dcerpc_server_name(p), d2, false),
+ "printer driver file check failed");
+
+ talloc_free(d1);
+ talloc_free(d2);
+ return true;
+}
+
+struct torture_suite *torture_rpc_spoolss_driver(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss.driver");
+
+ struct torture_rpc_tcase *tcase = torture_suite_add_rpc_iface_tcase(suite,
+ "driver", &ndr_table_spoolss);
+ torture_rpc_tcase_add_test(tcase, "add_driver_64", test_add_driver_64);
+ torture_rpc_tcase_add_test(tcase, "add_driver_ex_64", test_add_driver_ex_64);
+
+ torture_rpc_tcase_add_test(tcase, "add_driver_32", test_add_driver_32);
+ torture_rpc_tcase_add_test(tcase, "add_driver_ex_32", test_add_driver_ex_32);
+
+ torture_rpc_tcase_add_test(tcase, "add_driver_adobe", test_add_driver_adobe);
+
+ torture_rpc_tcase_add_test(tcase, "add_driver_adobe_cupsaddsmb", test_add_driver_adobe_cupsaddsmb);
+
+ torture_rpc_tcase_add_test(tcase, "add_driver_timestamps", test_add_driver_timestamps);
+
+ torture_rpc_tcase_add_test(tcase, "multiple_drivers", test_multiple_drivers);
+
+ torture_rpc_tcase_add_test(tcase,
+ "test_driver_copy_from_directory_64",
+ test_driver_copy_from_directory_64);
+
+ torture_rpc_tcase_add_test(tcase,
+ "test_driver_copy_from_directory_32",
+ test_driver_copy_from_directory_32);
+
+ torture_rpc_tcase_add_test(tcase, "del_driver_all_files", test_del_driver_all_files);
+
+ torture_rpc_tcase_add_test(tcase, "del_driver_unused_files", test_del_driver_unused_files);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/spoolss_access.c b/source4/torture/rpc/spoolss_access.c
new file mode 100644
index 0000000..b0d5265
--- /dev/null
+++ b/source4/torture/rpc/spoolss_access.c
@@ -0,0 +1,905 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for spoolss rpc operations
+
+ Copyright (C) Guenther Deschner 2010
+
+ 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_misc.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+#include "lib/cmdline/cmdline.h"
+
+#define TORTURE_USER "torture_user"
+#define TORTURE_USER_ADMINGROUP "torture_user_544"
+#define TORTURE_USER_PRINTOPGROUP "torture_user_550"
+#define TORTURE_USER_PRINTOPPRIV "torture_user_priv"
+#define TORTURE_USER_SD "torture_user_sd"
+#define TORTURE_WORKSTATION "torture_workstation"
+
+struct torture_user {
+ const char *username;
+ void *testuser;
+ uint32_t *builtin_memberships;
+ uint32_t num_builtin_memberships;
+ const char **privs;
+ uint32_t num_privs;
+ bool privs_present;
+ bool sd;
+ bool admin_rights;
+ bool system_security;
+};
+
+struct torture_access_context {
+ struct dcerpc_pipe *spoolss_pipe;
+ const char *printername;
+ struct security_descriptor *sd_orig;
+ struct torture_user user;
+};
+
+static bool test_openprinter_handle(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ const char *printername,
+ const char *username,
+ uint32_t access_mask,
+ WERROR expected_result,
+ struct policy_handle *handle)
+{
+ struct spoolss_OpenPrinterEx r;
+ struct spoolss_UserLevel1 level1;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ level1.size = 28;
+ level1.client = talloc_asprintf(tctx, "\\\\%s", "smbtorture");
+ level1.user = username;
+ /* Windows 7 and Windows Server 2008 R2 */
+ level1.build = 7007;
+ level1.major = 6;
+ level1.minor = 1;
+ level1.processor= 0;
+
+ r.in.printername = printername;
+ r.in.datatype = NULL;
+ r.in.devmode_ctr.devmode= NULL;
+ r.in.access_mask = access_mask;
+ r.in.userlevel_ctr.level = 1;
+ r.in.userlevel_ctr.user_info.level1 = &level1;
+ r.out.handle = handle;
+
+ torture_comment(tctx, "Testing OpenPrinterEx(%s) with access_mask 0x%08x (%s)\n",
+ r.in.printername, r.in.access_mask, name);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_OpenPrinterEx_r(b, tctx, &r),
+ "OpenPrinterEx failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ talloc_asprintf(tctx, "OpenPrinterEx(%s) as '%s' with access_mask: 0x%08x (%s) failed",
+ r.in.printername, username, r.in.access_mask, name));
+
+ return true;
+}
+
+static bool test_openprinter_access(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *name,
+ const char *printername,
+ const char *username,
+ uint32_t access_mask,
+ WERROR expected_result)
+{
+ struct policy_handle handle;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ bool ret = true;
+
+ ZERO_STRUCT(handle);
+
+ if (printername == NULL) {
+ torture_comment(tctx, "skipping test %s as there is no printer\n", name);
+ return true;
+ }
+
+ ret = test_openprinter_handle(tctx, p, name, printername, username, access_mask, expected_result, &handle);
+ if (is_valid_policy_hnd(&handle)) {
+ test_ClosePrinter(tctx, b, &handle);
+ }
+
+ return ret;
+}
+
+static bool spoolss_access_setup_membership(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ uint32_t num_members,
+ uint32_t *members,
+ struct dom_sid *user_sid)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle connect_handle, domain_handle;
+ int i;
+
+ torture_comment(tctx,
+ "Setting up BUILTIN membership for %s\n",
+ dom_sid_string(tctx, user_sid));
+ for (i=0; i < num_members; i++) {
+ torture_comment(tctx, "adding user to S-1-5-32-%d\n", members[i]);
+ }
+
+ {
+ struct samr_Connect2 r;
+ r.in.system_name = "";
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.connect_handle = &connect_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_Connect2_r(b, tctx, &r),
+ "samr_Connect2 failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_Connect2 failed");
+ }
+
+ {
+ struct samr_OpenDomain r;
+ r.in.connect_handle = &connect_handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.sid = dom_sid_parse_talloc(tctx, "S-1-5-32");
+ r.out.domain_handle = &domain_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_OpenDomain_r(b, tctx, &r),
+ "samr_OpenDomain failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_OpenDomain failed");
+ }
+
+ for (i=0; i < num_members; i++) {
+
+ struct policy_handle alias_handle;
+
+ {
+ struct samr_OpenAlias r;
+ r.in.domain_handle = &domain_handle;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.in.rid = members[i];
+ r.out.alias_handle = &alias_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_OpenAlias_r(b, tctx, &r),
+ "samr_OpenAlias failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_OpenAlias failed");
+ }
+
+ {
+ struct samr_AddAliasMember r;
+ r.in.alias_handle = &alias_handle;
+ r.in.sid = user_sid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_samr_AddAliasMember_r(b, tctx, &r),
+ "samr_AddAliasMember failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "samr_AddAliasMember failed");
+ }
+
+ test_samr_handle_Close(b, tctx, &alias_handle);
+ }
+
+ test_samr_handle_Close(b, tctx, &domain_handle);
+ test_samr_handle_Close(b, tctx, &connect_handle);
+
+ return true;
+}
+
+static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s)
+{
+ name->string = s;
+}
+static void init_lsa_String(struct lsa_String *name, const char *s)
+{
+ name->string = s;
+}
+
+static bool spoolss_access_setup_privs(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ uint32_t num_privs,
+ const char **privs,
+ struct dom_sid *user_sid,
+ bool *privs_present)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle *handle;
+ int i;
+
+ torture_assert(tctx,
+ test_lsa_OpenPolicy2(b, tctx, &handle),
+ "failed to open policy");
+
+ for (i=0; i < num_privs; i++) {
+ struct lsa_LookupPrivValue r;
+ struct lsa_LUID luid;
+ struct lsa_String name;
+
+ init_lsa_String(&name, privs[i]);
+
+ r.in.handle = handle;
+ r.in.name = &name;
+ r.out.luid = &luid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_LookupPrivValue_r(b, tctx, &r),
+ "lsa_LookupPrivValue failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "lsa_LookupPrivValue failed for '%s' with %s\n",
+ privs[i], nt_errstr(r.out.result));
+ *privs_present = false;
+ return true;
+ }
+ }
+
+ *privs_present = true;
+
+ {
+ struct lsa_AddAccountRights r;
+ struct lsa_RightSet rights;
+
+ rights.count = num_privs;
+ rights.names = talloc_zero_array(tctx, struct lsa_StringLarge, rights.count);
+
+ for (i=0; i < rights.count; i++) {
+ init_lsa_StringLarge(&rights.names[i], privs[i]);
+ }
+
+ r.in.handle = handle;
+ r.in.sid = user_sid;
+ r.in.rights = &rights;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_AddAccountRights_r(b, tctx, &r),
+ "lsa_AddAccountRights failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "lsa_AddAccountRights failed");
+ }
+
+ test_lsa_Close(b, tctx, handle);
+
+ return true;
+}
+
+static bool test_SetPrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ struct spoolss_SetPrinterInfoCtr *info_ctr,
+ struct spoolss_DevmodeContainer *devmode_ctr,
+ struct sec_desc_buf *secdesc_ctr,
+ enum spoolss_PrinterControl command)
+{
+ struct spoolss_SetPrinter r;
+
+ r.in.handle = handle;
+ r.in.info_ctr = info_ctr;
+ r.in.devmode_ctr = devmode_ctr;
+ r.in.secdesc_ctr = secdesc_ctr;
+ r.in.command = command;
+
+ torture_comment(tctx, "Testing SetPrinter level %d\n", r.in.info_ctr->level);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r),
+ "failed to call SetPrinter");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to call SetPrinter");
+
+ return true;
+}
+
+static bool spoolss_access_setup_sd(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *printername,
+ const struct dom_sid *user_sid,
+ struct security_descriptor **sd_orig)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle handle;
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo3 info3;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct security_ace *ace;
+ struct security_descriptor *sd;
+
+ torture_assert(tctx,
+ test_openprinter_handle(tctx, p, "", printername, "", SEC_FLAG_MAXIMUM_ALLOWED, WERR_OK, &handle),
+ "failed to open printer");
+
+ torture_assert(tctx,
+ test_GetPrinter_level(tctx, b, &handle, 3, &info),
+ "failed to get sd");
+
+ sd = security_descriptor_copy(tctx, info.info3.secdesc);
+ *sd_orig = security_descriptor_copy(tctx, info.info3.secdesc);
+
+ ace = talloc_zero(tctx, struct security_ace);
+
+ ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace->flags = 0;
+ ace->access_mask = PRINTER_ALL_ACCESS;
+ ace->trustee = *user_sid;
+
+ torture_assert_ntstatus_ok(tctx,
+ security_descriptor_dacl_add(sd, ace),
+ "failed to add new ace");
+
+ ace = talloc_zero(tctx, struct security_ace);
+
+ ace->type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace->flags = SEC_ACE_FLAG_OBJECT_INHERIT |
+ SEC_ACE_FLAG_CONTAINER_INHERIT |
+ SEC_ACE_FLAG_INHERIT_ONLY;
+ ace->access_mask = SEC_GENERIC_ALL;
+ ace->trustee = *user_sid;
+
+ torture_assert_ntstatus_ok(tctx,
+ security_descriptor_dacl_add(sd, ace),
+ "failed to add new ace");
+
+ ZERO_STRUCT(info3);
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ info_ctr.level = 3;
+ info_ctr.info.info3 = &info3;
+ secdesc_ctr.sd = sd;
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, &handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+ "failed to set sd");
+
+ return true;
+}
+
+static bool test_EnumPrinters_findone(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char **printername)
+{
+ struct spoolss_EnumPrinters r;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+ uint32_t needed;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ *printername = NULL;
+
+ r.in.flags = PRINTER_ENUM_LOCAL;
+ r.in.server = NULL;
+ r.in.level = 1;
+ r.in.buffer = NULL;
+ r.in.offered = 0;
+ r.out.count = &count;
+ r.out.info = &info;
+ r.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+ "failed to enum printers");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, needed);
+ r.in.buffer = &blob;
+ r.in.offered = needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_EnumPrinters_r(b, tctx, &r),
+ "failed to enum printers");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to enum printers");
+
+ for (i=0; i < count; i++) {
+
+ if (count > 1 && strequal(info[i].info1.name, "Microsoft XPS Document Writer")) {
+ continue;
+ }
+
+ torture_comment(tctx, "testing printer: %s\n",
+ info[i].info1.name);
+
+ *printername = talloc_strdup(tctx, info[i].info1.name);
+
+ break;
+ }
+
+ return true;
+}
+
+static bool torture_rpc_spoolss_access_setup_common(struct torture_context *tctx, struct torture_access_context *t)
+{
+ void *testuser;
+ const char *testuser_passwd;
+ struct cli_credentials *test_credentials;
+ struct dom_sid *test_sid;
+ struct dcerpc_pipe *p;
+ const char *printername;
+ const char *binding = torture_setting_string(tctx, "binding", NULL);
+ struct dcerpc_pipe *spoolss_pipe;
+
+ testuser = torture_create_testuser_max_pwlen(tctx, t->user.username,
+ torture_setting_string(tctx, "workgroup", NULL),
+ ACB_NORMAL,
+ &testuser_passwd,
+ 32);
+ if (!testuser) {
+ torture_fail(tctx, "Failed to create test user");
+ }
+
+ test_credentials = cli_credentials_init(tctx);
+ cli_credentials_set_workstation(test_credentials, "localhost", CRED_SPECIFIED);
+ cli_credentials_set_domain(test_credentials, lpcfg_workgroup(tctx->lp_ctx),
+ CRED_SPECIFIED);
+ cli_credentials_set_username(test_credentials, t->user.username, CRED_SPECIFIED);
+ cli_credentials_set_password(test_credentials, testuser_passwd, CRED_SPECIFIED);
+ test_sid = discard_const_p(struct dom_sid,
+ torture_join_user_sid(testuser));
+
+ if (t->user.num_builtin_memberships) {
+ struct dcerpc_pipe *samr_pipe = torture_join_samr_pipe(testuser);
+
+ torture_assert(tctx,
+ spoolss_access_setup_membership(tctx, samr_pipe,
+ t->user.num_builtin_memberships,
+ t->user.builtin_memberships,
+ test_sid),
+ "failed to setup membership");
+ }
+
+ if (t->user.num_privs) {
+ struct dcerpc_pipe *lsa_pipe;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &lsa_pipe, &ndr_table_lsarpc),
+ "Error connecting to server");
+
+ torture_assert(tctx,
+ spoolss_access_setup_privs(tctx, lsa_pipe,
+ t->user.num_privs,
+ t->user.privs,
+ test_sid,
+ &t->user.privs_present),
+ "failed to setup privs");
+ talloc_free(lsa_pipe);
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &spoolss_pipe, &ndr_table_spoolss),
+ "Error connecting to server");
+
+ torture_assert(tctx,
+ test_EnumPrinters_findone(tctx, spoolss_pipe, &printername),
+ "failed to enumerate printers");
+
+ if (t->user.sd && printername) {
+ torture_assert(tctx,
+ spoolss_access_setup_sd(tctx, spoolss_pipe,
+ printername,
+ test_sid,
+ &t->sd_orig),
+ "failed to setup sd");
+ }
+
+ talloc_free(spoolss_pipe);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect(tctx, &p, binding, &ndr_table_spoolss,
+ test_credentials, tctx->ev, tctx->lp_ctx),
+ "Error connecting to server");
+
+ t->spoolss_pipe = p;
+ t->printername = printername;
+ t->user.testuser = testuser;
+
+ return true;
+}
+
+static bool torture_rpc_spoolss_access_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_access_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_access_context);
+
+ t->user.username = talloc_strdup(t, TORTURE_USER);
+
+ return torture_rpc_spoolss_access_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_access_admin_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_access_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_access_context);
+
+ t->user.num_builtin_memberships = 1;
+ t->user.builtin_memberships = talloc_zero_array(t, uint32_t, t->user.num_builtin_memberships);
+ t->user.builtin_memberships[0] = BUILTIN_RID_ADMINISTRATORS;
+ t->user.username = talloc_strdup(t, TORTURE_USER_ADMINGROUP);
+ t->user.admin_rights = true;
+ t->user.system_security = true;
+
+ return torture_rpc_spoolss_access_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_access_printop_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_access_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_access_context);
+
+ t->user.num_builtin_memberships = 1;
+ t->user.builtin_memberships = talloc_zero_array(t, uint32_t, t->user.num_builtin_memberships);
+ t->user.builtin_memberships[0] = BUILTIN_RID_PRINT_OPERATORS;
+ t->user.username = talloc_strdup(t, TORTURE_USER_PRINTOPGROUP);
+ t->user.admin_rights = true;
+
+ return torture_rpc_spoolss_access_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_access_priv_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_access_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_access_context);
+
+ t->user.username = talloc_strdup(t, TORTURE_USER_PRINTOPPRIV);
+ t->user.num_privs = 1;
+ t->user.privs = talloc_zero_array(t, const char *, t->user.num_privs);
+ t->user.privs[0] = talloc_strdup(t, "SePrintOperatorPrivilege");
+ t->user.admin_rights = true;
+
+ return torture_rpc_spoolss_access_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_access_sd_setup(struct torture_context *tctx, void **data)
+{
+ struct torture_access_context *t;
+
+ *data = t = talloc_zero(tctx, struct torture_access_context);
+
+ t->user.username = talloc_strdup(t, TORTURE_USER_SD);
+ t->user.sd = true;
+ t->user.admin_rights = true;
+
+ return torture_rpc_spoolss_access_setup_common(tctx, t);
+}
+
+static bool torture_rpc_spoolss_access_teardown_common(struct torture_context *tctx, struct torture_access_context *t)
+{
+ if (t->user.testuser) {
+ torture_leave_domain(tctx, t->user.testuser);
+ }
+
+ /* remove membership ? */
+ if (t->user.num_builtin_memberships) {
+ }
+
+ /* remove privs ? */
+ if (t->user.num_privs) {
+ }
+
+ /* restore sd */
+ if (t->user.sd && t->printername) {
+ struct policy_handle handle;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo3 info3;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct dcerpc_pipe *spoolss_pipe;
+ struct dcerpc_binding_handle *b;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection(tctx, &spoolss_pipe, &ndr_table_spoolss),
+ "Error connecting to server");
+
+ b = spoolss_pipe->binding_handle;
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(info3);
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ info_ctr.level = 3;
+ info_ctr.info.info3 = &info3;
+ secdesc_ctr.sd = t->sd_orig;
+
+ torture_assert(tctx,
+ test_openprinter_handle(tctx, spoolss_pipe, "", t->printername, "", SEC_FLAG_MAXIMUM_ALLOWED, WERR_OK, &handle),
+ "failed to open printer");
+
+ torture_assert(tctx,
+ test_SetPrinter(tctx, b, &handle, &info_ctr, &devmode_ctr, &secdesc_ctr, 0),
+ "failed to set sd");
+
+ talloc_free(spoolss_pipe);
+ }
+
+ return true;
+}
+
+static bool torture_rpc_spoolss_access_teardown(struct torture_context *tctx, void *data)
+{
+ struct torture_access_context *t = talloc_get_type(data, struct torture_access_context);
+ bool ret;
+
+ ret = torture_rpc_spoolss_access_teardown_common(tctx, t);
+ talloc_free(t);
+
+ return ret;
+}
+
+static bool test_openprinter(struct torture_context *tctx,
+ void *private_data)
+{
+ struct torture_access_context *t =
+ (struct torture_access_context *)talloc_get_type_abort(private_data, struct torture_access_context);
+ struct dcerpc_pipe *p = t->spoolss_pipe;
+ bool ret = true;
+ const char *username = t->user.username;
+ const char *servername_slash = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ int i;
+
+ struct {
+ const char *name;
+ uint32_t access_mask;
+ const char *printername;
+ WERROR expected_result;
+ } checks[] = {
+
+ /* printserver handle tests */
+
+ {
+ .name = "0",
+ .access_mask = 0,
+ .printername = servername_slash,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "SEC_FLAG_MAXIMUM_ALLOWED",
+ .access_mask = SEC_FLAG_MAXIMUM_ALLOWED,
+ .printername = servername_slash,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "SERVER_ACCESS_ENUMERATE",
+ .access_mask = SERVER_ACCESS_ENUMERATE,
+ .printername = servername_slash,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "SERVER_ACCESS_ADMINISTER",
+ .access_mask = SERVER_ACCESS_ADMINISTER,
+ .printername = servername_slash,
+ .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED
+ },
+ {
+ .name = "SERVER_READ",
+ .access_mask = SERVER_READ,
+ .printername = servername_slash,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "SERVER_WRITE",
+ .access_mask = SERVER_WRITE,
+ .printername = servername_slash,
+ .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED
+ },
+ {
+ .name = "SERVER_EXECUTE",
+ .access_mask = SERVER_EXECUTE,
+ .printername = servername_slash,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "SERVER_ALL_ACCESS",
+ .access_mask = SERVER_ALL_ACCESS,
+ .printername = servername_slash,
+ .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED
+ },
+
+ /* printer handle tests */
+
+ {
+ .name = "0",
+ .access_mask = 0,
+ .printername = t->printername,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "SEC_FLAG_MAXIMUM_ALLOWED",
+ .access_mask = SEC_FLAG_MAXIMUM_ALLOWED,
+ .printername = t->printername,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "SEC_FLAG_SYSTEM_SECURITY",
+ .access_mask = SEC_FLAG_SYSTEM_SECURITY,
+ .printername = t->printername,
+ .expected_result = t->user.system_security ? WERR_OK : WERR_ACCESS_DENIED
+ },
+ {
+ .name = "0x010e0000",
+ .access_mask = 0x010e0000,
+ .printername = t->printername,
+ .expected_result = t->user.system_security ? WERR_OK : WERR_ACCESS_DENIED
+ },
+ {
+ .name = "PRINTER_ACCESS_USE",
+ .access_mask = PRINTER_ACCESS_USE,
+ .printername = t->printername,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "SEC_STD_READ_CONTROL",
+ .access_mask = SEC_STD_READ_CONTROL,
+ .printername = t->printername,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "PRINTER_ACCESS_ADMINISTER",
+ .access_mask = PRINTER_ACCESS_ADMINISTER,
+ .printername = t->printername,
+ .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED
+ },
+ {
+ .name = "SEC_STD_WRITE_DAC",
+ .access_mask = SEC_STD_WRITE_DAC,
+ .printername = t->printername,
+ .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED
+ },
+ {
+ .name = "SEC_STD_WRITE_OWNER",
+ .access_mask = SEC_STD_WRITE_OWNER,
+ .printername = t->printername,
+ .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED
+ },
+ {
+ .name = "PRINTER_READ",
+ .access_mask = PRINTER_READ,
+ .printername = t->printername,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "PRINTER_WRITE",
+ .access_mask = PRINTER_WRITE,
+ .printername = t->printername,
+ .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED
+ },
+ {
+ .name = "PRINTER_EXECUTE",
+ .access_mask = PRINTER_EXECUTE,
+ .printername = t->printername,
+ .expected_result = WERR_OK
+ },
+ {
+ .name = "PRINTER_ALL_ACCESS",
+ .access_mask = PRINTER_ALL_ACCESS,
+ .printername = t->printername,
+ .expected_result = t->user.admin_rights ? WERR_OK : WERR_ACCESS_DENIED
+ }
+ };
+
+ if (t->user.num_privs && !t->user.privs_present) {
+ torture_skip(tctx, "skipping test as not all required privileges are present on the server\n");
+ }
+
+ for (i=0; i < ARRAY_SIZE(checks); i++) {
+ ret &= test_openprinter_access(tctx, p,
+ checks[i].name,
+ checks[i].printername,
+ username,
+ checks[i].access_mask,
+ checks[i].expected_result);
+ }
+
+ return ret;
+}
+
+static bool test_openprinter_wrap(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct torture_access_context *t;
+ const char *printername;
+ bool ret = true;
+
+ t = talloc_zero(tctx, struct torture_access_context);
+
+ t->user.username = talloc_strdup(tctx, "dummy");
+ t->spoolss_pipe = p;
+
+ torture_assert(tctx,
+ test_EnumPrinters_findone(tctx, p, &printername),
+ "failed to enumerate printers");
+
+ t->printername = printername;
+
+ ret = test_openprinter(tctx, (void *)t);
+
+ talloc_free(t);
+
+ return ret;
+}
+
+struct torture_suite *torture_rpc_spoolss_access(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "spoolss.access");
+ struct torture_tcase *tcase;
+ struct torture_rpc_tcase *rpc_tcase;
+
+ tcase = torture_suite_add_tcase(suite, "normaluser");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_access_setup,
+ torture_rpc_spoolss_access_teardown);
+
+ torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter);
+
+ tcase = torture_suite_add_tcase(suite, "adminuser");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_access_admin_setup,
+ torture_rpc_spoolss_access_teardown);
+
+ torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter);
+
+ tcase = torture_suite_add_tcase(suite, "printopuser");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_access_printop_setup,
+ torture_rpc_spoolss_access_teardown);
+
+ torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter);
+
+ tcase = torture_suite_add_tcase(suite, "printopuserpriv");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_access_priv_setup,
+ torture_rpc_spoolss_access_teardown);
+
+ torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter);
+
+ tcase = torture_suite_add_tcase(suite, "normaluser_sd");
+
+ torture_tcase_set_fixture(tcase,
+ torture_rpc_spoolss_access_sd_setup,
+ torture_rpc_spoolss_access_teardown);
+
+ torture_tcase_add_simple_test(tcase, "openprinter", test_openprinter);
+
+ rpc_tcase = torture_suite_add_machine_workstation_rpc_iface_tcase(suite, "workstation",
+ &ndr_table_spoolss,
+ TORTURE_WORKSTATION);
+
+ torture_rpc_tcase_add_test(rpc_tcase, "openprinter", test_openprinter_wrap);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/spoolss_notify.c b/source4/torture/rpc/spoolss_notify.c
new file mode 100644
index 0000000..bd9dac4
--- /dev/null
+++ b/source4/torture/rpc/spoolss_notify.c
@@ -0,0 +1,636 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for spoolss rpc notify operations
+
+ Copyright (C) Jelmer Vernooij 2007
+ Copyright (C) Guenther Deschner 2010
+
+ 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 "system/filesys.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "torture/rpc/torture_rpc.h"
+#include "rpc_server/dcerpc_server.h"
+#include "rpc_server/dcerpc_server_proto.h"
+#include "rpc_server/service_rpc.h"
+#include "samba/process_model.h"
+#include "smb_server/smb_server.h"
+#include "lib/socket/netif.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+
+static struct dcesrv_context_callbacks srv_cb = {
+ .log.successful_authz = log_successful_dcesrv_authz_event,
+ .auth.gensec_prepare = dcesrv_gensec_prepare,
+ .assoc_group.find = dcesrv_assoc_group_find_s4,
+};
+
+static NTSTATUS spoolss__op_bind(struct dcesrv_connection_context *context,
+ 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)) {
+ dce_call->fault_code = DCERPC_FAULT_NDR;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* Note that received_packets are allocated on the NULL 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 void free_received_packets(void)
+{
+ struct received_packet *rp;
+ struct received_packet *rp_next;
+
+ for (rp = received_packets; rp; rp = rp_next) {
+ rp_next = rp->next;
+ DLIST_REMOVE(received_packets, rp);
+ talloc_unlink(rp, rp->r);
+ talloc_free(rp);
+ }
+ received_packets = NULL;
+}
+
+static WERROR _spoolss_ReplyOpenPrinter(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct spoolss_ReplyOpenPrinter *r)
+{
+ DEBUG(1,("_spoolss_ReplyOpenPrinter\n"));
+
+ NDR_PRINT_IN_DEBUG(spoolss_ReplyOpenPrinter, r);
+
+ r->out.handle = talloc(r, struct policy_handle);
+ r->out.handle->handle_type = 42;
+ r->out.handle->uuid = GUID_random();
+ r->out.result = WERR_OK;
+
+ NDR_PRINT_OUT_DEBUG(spoolss_ReplyOpenPrinter, r);
+
+ return WERR_OK;
+}
+
+static WERROR _spoolss_ReplyClosePrinter(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct spoolss_ReplyClosePrinter *r)
+{
+ DEBUG(1,("_spoolss_ReplyClosePrinter\n"));
+
+ NDR_PRINT_IN_DEBUG(spoolss_ReplyClosePrinter, r);
+
+ ZERO_STRUCTP(r->out.handle);
+ r->out.result = WERR_OK;
+
+ NDR_PRINT_OUT_DEBUG(spoolss_ReplyClosePrinter, r);
+
+ return WERR_OK;
+}
+
+static WERROR _spoolss_RouterReplyPrinterEx(struct dcesrv_call_state *dce_call,
+ TALLOC_CTX *mem_ctx,
+ struct spoolss_RouterReplyPrinterEx *r)
+{
+ DEBUG(1,("_spoolss_RouterReplyPrinterEx\n"));
+
+ NDR_PRINT_IN_DEBUG(spoolss_RouterReplyPrinterEx, r);
+
+ r->out.reply_result = talloc(r, uint32_t);
+ *r->out.reply_result = 0;
+ r->out.result = WERR_OK;
+
+ NDR_PRINT_OUT_DEBUG(spoolss_RouterReplyPrinterEx, r);
+
+ return WERR_OK;
+}
+
+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(NULL, struct received_packet);
+ rp->opnum = opnum;
+ rp->r = talloc_reference(rp, r);
+
+ DLIST_ADD_END(received_packets, rp);
+
+ switch (opnum) {
+ case 58: {
+ struct spoolss_ReplyOpenPrinter *r2 = (struct spoolss_ReplyOpenPrinter *)r;
+ r2->out.result = _spoolss_ReplyOpenPrinter(dce_call, mem_ctx, r2);
+ break;
+ }
+ case 60: {
+ struct spoolss_ReplyClosePrinter *r2 = (struct spoolss_ReplyClosePrinter *)r;
+ r2->out.result = _spoolss_ReplyClosePrinter(dce_call, mem_ctx, r2);
+ break;
+ }
+ case 66: {
+ struct spoolss_RouterReplyPrinterEx *r2 = (struct spoolss_RouterReplyPrinterEx *)r;
+ r2->out.result = _spoolss_RouterReplyPrinterEx(dce_call, mem_ctx, r2);
+ break;
+ }
+
+ default:
+ dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR;
+ break;
+ }
+
+ if (dce_call->fault_code != 0) {
+ 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;
+}
+
+static const 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)
+{
+ uint32_t 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,
+ NULL,
+ &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 NTSTATUS spoolss__op_shutdown_server(struct dcesrv_context *dce_ctx,
+ const struct dcesrv_endpoint_server *ep_server)
+{
+ return NT_STATUS_OK;
+}
+
+static bool test_OpenPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *printername)
+{
+ struct spoolss_OpenPrinter r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(r);
+
+ r.in.printername = printername;
+ 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);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_OpenPrinter_r(b, tctx, &r),
+ "OpenPrinter failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "OpenPrinter failed");
+
+ return true;
+}
+
+static struct spoolss_NotifyOption *setup_printserver_NotifyOption(struct torture_context *tctx)
+{
+ struct spoolss_NotifyOption *o;
+
+ o = talloc_zero(tctx, struct spoolss_NotifyOption);
+
+ o->version = 2;
+ o->flags = PRINTER_NOTIFY_OPTIONS_REFRESH;
+
+ o->count = 2;
+ o->types = talloc_zero_array(o, struct spoolss_NotifyOptionType, o->count);
+
+ o->types[0].type = PRINTER_NOTIFY_TYPE;
+ o->types[0].count = 1;
+ o->types[0].fields = talloc_array(o->types, union spoolss_Field, o->types[0].count);
+ o->types[0].fields[0].field = PRINTER_NOTIFY_FIELD_SERVER_NAME;
+
+ o->types[1].type = JOB_NOTIFY_TYPE;
+ o->types[1].count = 1;
+ o->types[1].fields = talloc_array(o->types, union spoolss_Field, o->types[1].count);
+ o->types[1].fields[0].field = JOB_NOTIFY_FIELD_MACHINE_NAME;
+
+ return o;
+}
+
+#if 0
+static struct spoolss_NotifyOption *setup_printer_NotifyOption(struct torture_context *tctx)
+{
+ struct spoolss_NotifyOption *o;
+
+ o = talloc_zero(tctx, struct spoolss_NotifyOption);
+
+ o->version = 2;
+ o->flags = PRINTER_NOTIFY_OPTIONS_REFRESH;
+
+ o->count = 1;
+ o->types = talloc_zero_array(o, struct spoolss_NotifyOptionType, o->count);
+
+ o->types[0].type = PRINTER_NOTIFY_TYPE;
+ o->types[0].count = 1;
+ o->types[0].fields = talloc_array(o->types, union spoolss_Field, o->types[0].count);
+ o->types[0].fields[0].field = PRINTER_NOTIFY_FIELD_COMMENT;
+
+ return o;
+}
+#endif
+
+static bool test_RemoteFindFirstPrinterChangeNotifyEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *address,
+ struct spoolss_NotifyOption *option)
+{
+ struct spoolss_RemoteFindFirstPrinterChangeNotifyEx r;
+ const char *local_machine = talloc_asprintf(tctx, "\\\\%s", address);
+
+ torture_comment(tctx, "Testing RemoteFindFirstPrinterChangeNotifyEx(%s)\n", local_machine);
+
+ r.in.flags = 0;
+ r.in.local_machine = local_machine;
+ r.in.options = 0;
+ r.in.printer_local = 0;
+ r.in.notify_options = option;
+ r.in.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_RemoteFindFirstPrinterChangeNotifyEx_r(b, tctx, &r),
+ "RemoteFindFirstPrinterChangeNotifyEx failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "error return code for RemoteFindFirstPrinterChangeNotifyEx");
+
+ return true;
+}
+
+static bool test_RouterRefreshPrinterChangeNotify(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ struct spoolss_NotifyOption *options,
+ struct spoolss_NotifyInfo **info)
+{
+ struct spoolss_RouterRefreshPrinterChangeNotify r;
+
+ torture_comment(tctx, "Testing RouterRefreshPrinterChangeNotify\n");
+
+ r.in.handle = handle;
+ r.in.change_low = 0;
+ r.in.options = options;
+ r.out.info = info;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_RouterRefreshPrinterChangeNotify_r(b, tctx, &r),
+ "RouterRefreshPrinterChangeNotify failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "error return code for RouterRefreshPrinterChangeNotify");
+
+ return true;
+}
+
+#if 0
+static bool test_SetPrinter(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle)
+{
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinter r;
+ struct spoolss_SetPrinterInfo2 info2;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx, test_GetPrinter_level(tctx, b, handle, 2, &info), "");
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ info2.servername = info.info2.servername;
+ info2.printername = info.info2.printername;
+ info2.sharename = info.info2.sharename;
+ info2.portname = info.info2.portname;
+ info2.drivername = info.info2.drivername;
+ info2.comment = talloc_asprintf(tctx, "torture_comment %d\n", (int)time(NULL));
+ info2.location = info.info2.location;
+ info2.devmode_ptr = 0;
+ info2.sepfile = info.info2.sepfile;
+ info2.printprocessor = info.info2.printprocessor;
+ info2.datatype = info.info2.datatype;
+ info2.parameters = info.info2.parameters;
+ info2.secdesc_ptr = 0;
+ info2.attributes = info.info2.attributes;
+ info2.priority = info.info2.priority;
+ info2.defaultpriority = info.info2.defaultpriority;
+ info2.starttime = info.info2.starttime;
+ info2.untiltime = info.info2.untiltime;
+ info2.status = info.info2.status;
+ info2.cjobs = info.info2.cjobs;
+ info2.averageppm = info.info2.averageppm;
+
+ info_ctr.level = 2;
+ info_ctr.info.info2 = &info2;
+
+ r.in.handle = handle;
+ r.in.info_ctr = &info_ctr;
+ r.in.devmode_ctr = &devmode_ctr;
+ r.in.secdesc_ctr = &secdesc_ctr;
+ r.in.command = 0;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_spoolss_SetPrinter_r(b, tctx, &r), "SetPrinter failed");
+ torture_assert_werr_ok(tctx, r.out.result, "SetPrinter failed");
+
+ return true;
+}
+#endif
+
+static bool test_start_dcerpc_server(struct torture_context *tctx,
+ struct tevent_context *event_ctx,
+ struct dcesrv_context **dce_ctx_p,
+ const char **address_p)
+{
+ struct dcesrv_endpoint_server ep_server;
+ NTSTATUS status;
+ struct dcesrv_context *dce_ctx;
+ const char *endpoints[] = { "spoolss", NULL };
+ struct dcesrv_endpoint *e;
+ const char *address;
+ struct interface *ifaces;
+
+ ntvfs_init(tctx->lp_ctx);
+
+ /* fill in our name */
+ ep_server.name = "spoolss";
+
+ ep_server.initialized = false;
+
+ /* fill in all the operations */
+ ep_server.init_server = spoolss__op_init_server;
+ ep_server.shutdown_server = spoolss__op_shutdown_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");
+
+ lpcfg_set_cmdline(tctx->lp_ctx, "dcerpc endpoint servers", "spoolss");
+
+ load_interface_list(tctx, tctx->lp_ctx, &ifaces);
+ address = iface_list_first_v4(ifaces);
+
+ torture_comment(tctx, "Listening for callbacks on %s\n", address);
+
+ status = process_model_init(tctx->lp_ctx);
+ torture_assert_ntstatus_ok(tctx, status,
+ "unable to initialize process models");
+
+ status = smbsrv_add_socket(tctx, event_ctx, tctx->lp_ctx,
+ process_model_startup("single"),
+ address, NULL);
+ torture_assert_ntstatus_ok(tctx, status, "starting smb server");
+
+ status = dcesrv_init_context(tctx, tctx->lp_ctx, &srv_cb, &dce_ctx);
+ torture_assert_ntstatus_ok(tctx, status,
+ "unable to initialize DCE/RPC server");
+
+ status = dcesrv_init_ep_servers(dce_ctx, endpoints);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "unable to initialize DCE/RPC ep servers");
+
+ for (e=dce_ctx->endpoint_list;e;e=e->next) {
+ status = dcesrv_add_ep(dce_ctx, tctx->lp_ctx,
+ e, tctx->ev,
+ process_model_startup("single"), NULL);
+ torture_assert_ntstatus_ok(tctx, status,
+ "unable listen on dcerpc endpoint server");
+ }
+
+ *dce_ctx_p = dce_ctx;
+ *address_p = address;
+
+ return true;
+}
+
+static struct received_packet *last_packet(struct received_packet *p)
+{
+ struct received_packet *tmp;
+ for (tmp = p; tmp->next; tmp = tmp->next) {
+ }
+ return tmp;
+}
+
+static bool test_RFFPCNEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct dcesrv_context *dce_ctx;
+ struct policy_handle handle;
+ const char *address;
+ struct received_packet *tmp;
+ struct spoolss_NotifyOption *server_option = setup_printserver_NotifyOption(tctx);
+#if 0
+ struct spoolss_NotifyOption *printer_option = setup_printer_NotifyOption(tctx);
+#endif
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *printername = NULL;
+ struct spoolss_NotifyInfo *info = NULL;
+
+ free_received_packets();
+
+ /* Start DCE/RPC server */
+ torture_assert(tctx, test_start_dcerpc_server(tctx, tctx->ev, &dce_ctx, &address), "");
+
+ printername = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+
+ torture_assert(tctx, test_OpenPrinter(tctx, p, &handle, printername), "");
+ torture_assert(tctx, test_RemoteFindFirstPrinterChangeNotifyEx(tctx, b, &handle, address, server_option), "");
+ torture_assert(tctx, received_packets, "no packets received");
+ torture_assert_int_equal(tctx, received_packets->opnum, NDR_SPOOLSS_REPLYOPENPRINTER,
+ "no ReplyOpenPrinter packet after RemoteFindFirstPrinterChangeNotifyEx");
+ torture_assert(tctx, test_RouterRefreshPrinterChangeNotify(tctx, b, &handle, NULL, &info), "");
+ torture_assert(tctx, test_RouterRefreshPrinterChangeNotify(tctx, b, &handle, server_option, &info), "");
+ torture_assert(tctx, test_ClosePrinter(tctx, b, &handle), "");
+ tmp = last_packet(received_packets);
+ torture_assert_int_equal(tctx, tmp->opnum, NDR_SPOOLSS_REPLYCLOSEPRINTER,
+ "no ReplyClosePrinter packet after ClosePrinter");
+#if 0
+ printername = talloc_asprintf(tctx, "\\\\%s\\%s", dcerpc_server_name(p), name);
+
+ torture_assert(tctx, test_OpenPrinter(tctx, p, &handle, "Epson AL-2600"), "");
+ torture_assert(tctx, test_RemoteFindFirstPrinterChangeNotifyEx(tctx, p, &handle, address, printer_option), "");
+ tmp = last_packet(received_packets);
+ torture_assert_int_equal(tctx, tmp->opnum, NDR_SPOOLSS_REPLYOPENPRINTER,
+ "no ReplyOpenPrinter packet after RemoteFindFirstPrinterChangeNotifyEx");
+ torture_assert(tctx, test_RouterRefreshPrinterChangeNotify(tctx, p, &handle, NULL, &info), "");
+ torture_assert(tctx, test_RouterRefreshPrinterChangeNotify(tctx, p, &handle, printer_option, &info), "");
+ torture_assert(tctx, test_SetPrinter(tctx, p, &handle), "");
+ tmp = last_packet(received_packets);
+ torture_assert_int_equal(tctx, tmp->opnum, NDR_SPOOLSS_ROUTERREPLYPRINTEREX,
+ "no RouterReplyPrinterEx packet after ClosePrinter");
+ torture_assert(tctx, test_ClosePrinter(tctx, p, &handle), "");
+ tmp = last_packet(received_packets);
+ torture_assert_int_equal(tctx, tmp->opnum, NDR_SPOOLSS_REPLYCLOSEPRINTER,
+ "no ReplyClosePrinter packet after ClosePrinter");
+#endif
+ /* Shut down DCE/RPC server */
+ talloc_free(dce_ctx);
+ free_received_packets();
+
+ 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 *p)
+{
+ struct spoolss_ReplyOpenPrinter r;
+ struct spoolss_ReplyClosePrinter s;
+ struct policy_handle h;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "skipping ReplyOpenPrinter server implementation test against s3\n");
+ }
+
+ r.in.server_name = "earth";
+ r.in.printer_local = 2;
+ r.in.type = REG_DWORD;
+ r.in.bufsize = 0;
+ r.in.buffer = NULL;
+ r.out.handle = &h;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_spoolss_ReplyOpenPrinter_r(b, 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_r(b, tctx, &s),
+ "spoolss_ReplyClosePrinter call failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "error return code");
+
+ 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);
+ torture_rpc_tcase_add_test(tcase, "testReplyOpenPrinter", test_ReplyOpenPrinter);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/spoolss_win.c b/source4/torture/rpc/spoolss_win.c
new file mode 100644
index 0000000..9162acd
--- /dev/null
+++ b/source4/torture/rpc/spoolss_win.c
@@ -0,0 +1,612 @@
+/*
+ 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/rpc/torture_rpc.h"
+#include "librpc/gen_ndr/ndr_spoolss_c.h"
+#include "librpc/gen_ndr/ndr_misc.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 */
+ const char **printer_keys;
+
+ bool printer_has_driver;
+};
+
+/* This is a convenience function for all OpenPrinterEx calls */
+static bool test_OpenPrinterEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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.userlevel_ctr.level = 1;
+ op.in.userlevel_ctr.user_info.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_r(b, 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_binding_handle *b,
+ 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.userlevel_ctr.level = 1;
+ op.in.userlevel_ctr.user_info.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_r(b, tctx, &op);
+
+ if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(op.out.result)) {
+ status = dcerpc_spoolss_ClosePrinter_r(b, tctx, &cp);
+ torture_assert_ntstatus_ok(tctx, status, "ClosePrinter failed");
+ }
+
+ return true;
+}
+
+
+
+/* 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));
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ /* First, see if we can open the printer read_only */
+ ret = test_OpenPrinterEx(tctx, b, handle, printername, 0);
+ torture_assert(tctx, ret == true, "OpenPrinterEx failed.");
+
+ ret = test_ClosePrinter(tctx, b, handle);
+ torture_assert(tctx, ret, "ClosePrinter failed");
+
+ /* Now let's see if we have admin rights to it. */
+ ret = test_OpenPrinterAsAdmin(tctx, b, printername);
+ torture_assert(tctx, ret == true,
+ "OpenPrinterEx as admin failed unexpectedly.");
+
+ ret = test_OpenPrinterEx(tctx, b, handle, printername, SERVER_EXECUTE);
+ torture_assert(tctx, ret == true, "OpenPrinterEx failed.");
+
+ return true;
+}
+
+static bool test_GetPrinterData(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *value_name,
+ WERROR expected_werr,
+ uint32_t expected_value)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterData gpd;
+ uint32_t needed;
+ enum winreg_Type type;
+ uint8_t *data = talloc_zero_array(tctx, uint8_t, 4);
+
+ torture_comment(tctx, "Testing GetPrinterData(%s).\n", value_name);
+ gpd.in.handle = handle;
+ gpd.in.value_name = value_name;
+ gpd.in.offered = 4;
+ gpd.out.needed = &needed;
+ gpd.out.type = &type;
+ gpd.out.data = data;
+
+ status = dcerpc_spoolss_GetPrinterData_r(b, 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)) {
+ uint32_t value = IVAL(data, 0);
+ torture_assert_int_equal(tctx, value,
+ expected_value,
+ talloc_asprintf(tctx, "GetPrinterData for %s did not return expected value.", value_name));
+ }
+ 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);
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_PrinterInfo *info;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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;
+ ep.out.needed = &needed;
+ ep.out.count = &count;
+ ep.out.info = &info;
+
+ status = dcerpc_spoolss_EnumPrinters_r(b, 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, needed);
+ ep.in.buffer = &blob;
+ ep.in.offered = needed;
+ status = dcerpc_spoolss_EnumPrinters_r(b, ctx, &ep);
+ torture_assert_ntstatus_ok(tctx, status,"EnumPrinters failed.");
+ }
+
+ torture_assert_werr_ok(tctx, ep.out.result, "EnumPrinters failed.");
+
+ ctx->printer_count = count;
+ ctx->printer_info = info;
+
+ torture_comment(tctx, "Found %d printer(s).\n", ctx->printer_count);
+
+ return true;
+}
+
+static bool test_GetPrinter(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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);
+ uint32_t needed;
+
+ 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;
+ gp.out.needed = &needed;
+
+ status = dcerpc_spoolss_GetPrinter_r(b, 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, needed);
+ gp.in.buffer = &blob;
+ gp.in.offered = needed;
+ status = dcerpc_spoolss_GetPrinter_r(b, 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;
+
+ if (level == 2 && gp.out.info) {
+ ctx->printer_has_driver = gp.out.info->info2.drivername &&
+ strlen(gp.out.info->info2.drivername);
+ }
+
+ return true;
+}
+
+static bool test_EnumJobs(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_EnumJobs ej;
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, 1024);
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_JobInfo *info;
+
+ torture_comment(tctx, "Test EnumJobs\n");
+
+ ZERO_STRUCT(ej);
+ ej.in.handle = handle;
+ ej.in.firstjob = 0;
+ ej.in.numjobs = 0;
+ ej.in.level = 2;
+ ej.in.buffer = &blob;
+ ej.in.offered = 1024;
+ ej.out.needed = &needed;
+ ej.out.count = &count;
+ ej.out.info = &info;
+
+ status = dcerpc_spoolss_EnumJobs_r(b, tctx, &ej);
+ torture_assert_ntstatus_ok(tctx, status, "EnumJobs failed");
+ if (W_ERROR_EQUAL(ej.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ blob = data_blob_talloc_zero(tctx, needed);
+ ej.in.offered = needed;
+ ej.in.buffer = &blob;
+ status = dcerpc_spoolss_EnumJobs_r(b, 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_binding_handle *b,
+ struct test_spoolss_win_context *ctx,
+ struct policy_handle *handle)
+{
+ NTSTATUS status;
+ struct spoolss_GetPrinterDriver2 gpd2;
+ DATA_BLOB blob = data_blob_talloc_zero(tctx, 87424);
+ uint32_t needed;
+ uint32_t server_major_version;
+ uint32_t server_minor_version;
+
+ 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;
+ gpd2.out.needed = &needed;
+ gpd2.out.server_major_version = &server_major_version;
+ gpd2.out.server_minor_version = &server_minor_version;
+
+ status = dcerpc_spoolss_GetPrinterDriver2_r(b, tctx, &gpd2);
+ torture_assert_ntstatus_ok(tctx, status, "GetPrinterDriver2 failed");
+
+ if (ctx->printer_has_driver) {
+ torture_assert_werr_ok(tctx, gpd2.out.result,
+ "GetPrinterDriver2 failed.");
+ }
+
+ return true;
+}
+
+static bool test_EnumForms(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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);
+ uint32_t needed;
+ uint32_t count;
+ union spoolss_FormInfo *info;
+
+ 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;
+ ef.out.needed = &needed;
+ ef.out.count = &count;
+ ef.out.info = &info;
+
+ status = dcerpc_spoolss_EnumForms_r(b, 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, needed);
+ ef.in.buffer = &blob;
+ ef.in.offered = needed;
+ status = dcerpc_spoolss_EnumForms_r(b, 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_binding_handle *b,
+ struct policy_handle *handle,
+ const char* key,
+ struct test_spoolss_win_context *ctx)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterKey epk;
+ uint32_t needed = 0;
+ union spoolss_KeyNames key_buffer;
+ uint32_t _ndr_size;
+
+ torture_comment(tctx, "Testing EnumPrinterKey(%s)\n", key);
+
+ epk.in.handle = handle;
+ epk.in.key_name = talloc_strdup(tctx, key);
+ epk.in.offered = 0;
+ epk.out.needed = &needed;
+ epk.out.key_buffer = &key_buffer;
+ epk.out._ndr_size = &_ndr_size;
+
+ status = dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &epk);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterKey failed");
+
+ if (W_ERROR_EQUAL(epk.out.result, WERR_MORE_DATA)) {
+ epk.in.offered = needed;
+ status = dcerpc_spoolss_EnumPrinterKey_r(b, tctx, &epk);
+ torture_assert_ntstatus_ok(tctx, status,
+ "EnumPrinterKey failed");
+ }
+
+ torture_assert_werr_ok(tctx, epk.out.result, "EnumPrinterKey failed");
+
+ ctx->printer_keys = key_buffer.string_array;
+
+ return true;
+}
+
+static bool test_EnumPrinterDataEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key,
+ uint32_t initial_blob_size,
+ WERROR expected_error)
+{
+ NTSTATUS status;
+ struct spoolss_EnumPrinterDataEx epde;
+ struct spoolss_PrinterEnumValues *info;
+ uint32_t needed;
+ uint32_t count;
+
+ torture_comment(tctx, "Testing EnumPrinterDataEx(%s)\n", key);
+
+ epde.in.handle = handle;
+ epde.in.key_name = talloc_strdup(tctx, key);
+ epde.in.offered = 0;
+ epde.out.needed = &needed;
+ epde.out.count = &count;
+ epde.out.info = &info;
+
+ status = dcerpc_spoolss_EnumPrinterDataEx_r(b, tctx, &epde);
+ torture_assert_ntstatus_ok(tctx, status, "EnumPrinterDataEx failed.");
+ if (W_ERROR_EQUAL(epde.out.result, WERR_MORE_DATA)) {
+ epde.in.offered = needed;
+ status = dcerpc_spoolss_EnumPrinterDataEx_r(b, 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_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;
+ uint32_t i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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, b, &handle01,"UISingleJobStatusString",
+ WERR_INVALID_PARAMETER, 0);
+ torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n");
+
+ server_name = talloc_asprintf(ctx, "\\\\%s", dcerpc_server_name(p));
+ ret &= test_OpenPrinterEx(tctx, b, &unused_handle1, server_name, 0);
+
+ ret &= test_EnumPrinters(tctx, p, ctx, 1024);
+
+ ret &= test_OpenPrinterEx(tctx, b, &handle02, server_name, 0);
+ ret &= test_GetPrinterData(tctx, b, &handle02, "MajorVersion", WERR_OK,
+ 3);
+ ret &= test_ClosePrinter(tctx, b, &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, b, &handle02,
+ ctx->printer_info[0].info2.printername,
+ PRINTER_ACCESS_USE);
+ ret &= test_GetPrinter(tctx, b, &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, b, &handle03,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_GetPrinter(tctx, b, &handle03, ctx, 0, 1164);
+ ret &= test_GetPrinter(tctx, b, &handle03, ctx, 2, 0);
+
+ ret &= test_OpenPrinterEx(tctx, b, &handle04,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_GetPrinter(tctx, b, &handle04, ctx, 2, 0);
+ ret &= test_ClosePrinter(tctx, b, &handle04);
+
+ ret &= test_OpenPrinterEx(tctx, b, &handle04,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_GetPrinter(tctx, b, &handle04, ctx, 2, 4096);
+ ret &= test_ClosePrinter(tctx, b, &handle04);
+
+ ret &= test_OpenPrinterAsAdmin(tctx, b,
+ ctx->printer_info[0].info2.printername);
+
+ ret &= test_OpenPrinterEx(tctx, b, &handle04,
+ ctx->printer_info[0].info2.printername, PRINTER_READ);
+ ret &= test_GetPrinterData(tctx, b, &handle04,"UISingleJobStatusString",
+ WERR_FILE_NOT_FOUND, 0);
+ torture_comment(tctx, "Skip RemoteFindNextPrinterChangeNotifyEx test\n");
+
+ ret &= test_OpenPrinterEx(tctx, b, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+
+ ret &= test_EnumJobs(tctx, b, &handle04);
+ ret &= test_GetPrinter(tctx, b, &handle04, ctx, 2, 4096);
+
+ ret &= test_ClosePrinter(tctx, b, &unused_handle2);
+ ret &= test_ClosePrinter(tctx, b, &handle04);
+
+ ret &= test_EnumPrinters(tctx, p, ctx, 1556);
+ ret &= test_GetPrinterDriver2(tctx, b, ctx, &handle03);
+ ret &= test_EnumForms(tctx, b, &handle03, 0);
+
+ ret &= test_EnumPrinterKey(tctx, b, &handle03, "", ctx);
+
+ for (i=0; ctx->printer_keys && ctx->printer_keys[i] != NULL; i++) {
+
+ ret &= test_EnumPrinterKey(tctx, b, &handle03,
+ ctx->printer_keys[i],
+ tmp_ctx);
+ ret &= test_EnumPrinterDataEx(tctx, b, &handle03,
+ ctx->printer_keys[i], 0,
+ WERR_OK);
+ }
+
+ ret &= test_EnumPrinterDataEx(tctx, b, &handle03, "", 0,
+ WERR_INVALID_PARAMETER);
+
+ ret &= test_GetPrinter(tctx, b, &handle03, tmp_ctx, 2, 0);
+
+ ret &= test_OpenPrinterEx(tctx, b, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, b, &unused_handle2);
+
+ ret &= test_GetPrinter(tctx, b, &handle03, tmp_ctx, 2, 2556);
+
+ ret &= test_OpenPrinterEx(tctx, b, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, b, &unused_handle2);
+
+ ret &= test_OpenPrinterEx(tctx, b, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, b, &unused_handle2);
+
+ ret &= test_GetPrinter(tctx, b, &handle03, tmp_ctx, 7, 0);
+
+ ret &= test_OpenPrinterEx(tctx, b, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, b, &unused_handle2);
+
+ ret &= test_ClosePrinter(tctx, b, &handle03);
+
+ ret &= test_OpenPrinterEx(tctx, b, &unused_handle2,
+ ctx->printer_info[0].info2.printername, 0);
+ ret &= test_ClosePrinter(tctx, b, &unused_handle2);
+
+ ret &= test_OpenPrinterEx(tctx, b, &handle03, server_name, 0);
+ ret &= test_GetPrinterData(tctx, b, &handle03, "W3SvcInstalled",
+ WERR_OK, 0);
+ ret &= test_ClosePrinter(tctx, b, &handle03);
+
+ ret &= test_ClosePrinter(tctx, b, &unused_handle1);
+ ret &= test_ClosePrinter(tctx, b, &handle02);
+
+ ret &= test_OpenPrinterEx(tctx, b, &handle02,
+ ctx->printer_info[0].info2.sharename, 0);
+ ret &= test_GetPrinter(tctx, b, &handle02, tmp_ctx, 2, 0);
+ ret &= test_ClosePrinter(tctx, b, &handle02);
+
+end_testWinXP:
+ ret &= test_ClosePrinter(tctx, b, &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 0000000..c777f36
--- /dev/null
+++ b/source4/torture/rpc/srvsvc.c
@@ -0,0 +1,1206 @@
+/*
+ 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 "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "torture/rpc/torture_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;
+ union srvsvc_NetCharDevInfo info;
+ uint32_t levels[] = {0, 1};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.device_name = devname;
+ r.out.info = &info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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_NetCharDevInfoCtr info_ctr;
+ struct srvsvc_NetCharDevCtr0 c0;
+ struct srvsvc_NetCharDevCtr0 c1;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0, 1};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(info_ctr);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+ r.out.info_ctr = &info_ctr;
+ r.out.totalentries = &totalentries;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int j;
+
+ info_ctr.level = levels[i];
+
+ switch(info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr0 = &c1;
+ break;
+ }
+
+ torture_comment(tctx, "Testing NetCharDevEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetCharDevEnum_r(b, 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 (info_ctr.level == 1) {
+ for (j=0;j<r.out.info_ctr->ctr.ctr1->count;j++) {
+ const char *device;
+ device = r.out.info_ctr->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;
+ union srvsvc_NetCharDevQInfo info;
+ uint32_t levels[] = {0, 1};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.queue_name = devicequeue;
+ r.in.user = talloc_asprintf(tctx,"Administrator");
+ r.out.info = &info;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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_NetCharDevQInfoCtr info_ctr;
+ struct srvsvc_NetCharDevQCtr0 c0;
+ struct srvsvc_NetCharDevQCtr1 c1;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0, 1};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(info_ctr);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.user = talloc_asprintf(tctx,"%s","Administrator");
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ int j;
+
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ }
+ torture_comment(tctx, "Testing NetCharDevQEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetCharDevQEnum_r(b, 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 (info_ctr.level == 1) {
+ for (j=0;j<r.out.info_ctr->ctr.ctr1->count;j++) {
+ const char *device;
+ device = r.out.info_ctr->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_NetConnInfoCtr info_ctr;
+ struct srvsvc_NetConnCtr0 c0;
+ struct srvsvc_NetConnCtr1 c1;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0, 1};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(info_ctr);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.path = talloc_asprintf(tctx,"%s","IPC$");
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ }
+
+ torture_comment(tctx, "Testing NetConnEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetConnEnum_r(b, 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_NetFileInfoCtr info_ctr;
+ struct srvsvc_NetFileCtr2 c2;
+ struct srvsvc_NetFileCtr3 c3;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {2, 3};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(info_ctr);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.path = NULL;
+ r.in.user = NULL;
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)4096;
+ r.in.resume_handle = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 3:
+ ZERO_STRUCT(c3);
+ info_ctr.ctr.ctr3 = &c3;
+ break;
+ }
+ torture_comment(tctx, "Testing NetFileEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetFileEnum_r(b, 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_NetSessInfoCtr info_ctr;
+ struct srvsvc_NetSessCtr0 c0;
+ struct srvsvc_NetSessCtr1 c1;
+ struct srvsvc_NetSessCtr2 c2;
+ struct srvsvc_NetSessCtr10 c10;
+ struct srvsvc_NetSessCtr502 c502;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0, 1, 2, 10, 502};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(info_ctr);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.client = NULL;
+ r.in.user = NULL;
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ info_ctr.level = levels[i];
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 10:
+ ZERO_STRUCT(c10);
+ info_ctr.ctr.ctr10 = &c10;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ }
+
+ torture_comment(tctx, "Testing NetSessEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetSessEnum_r(b, 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;
+ enum srvsvc_ShareType type;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.device_name = device_name;
+ r.out.type = &type;
+
+ torture_comment(tctx,
+ "Testing NetShareCheck on device '%s'\n", r.in.device_name);
+
+ status = dcerpc_srvsvc_NetShareCheck_r(b, 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;
+ union srvsvc_NetShareInfo info;
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.share_name = sharename;
+ r.out.info = &info;
+
+ 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;
+
+ torture_comment(tctx, "Testing NetShareGetInfo level %u on share '%s'\n",
+ r.in.level, r.in.share_name);
+
+ status = dcerpc_srvsvc_NetShareGetInfo_r(b, 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, "IPC$", true);
+}
+
+static bool test_NetShareGetInfoAdminAnon(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ return test_NetShareGetInfo(tctx, p, "IPC$", 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 sec_desc_buf sd_buf;
+ union srvsvc_NetShareInfo info;
+ struct {
+ uint32_t level;
+ WERROR expected;
+ } levels[] = {
+ { 0, WERR_INVALID_LEVEL },
+ { 1, WERR_OK },
+ { 2, WERR_OK },
+ { 501, WERR_INVALID_LEVEL },
+ { 502, WERR_OK },
+ { 1004, WERR_OK },
+ { 1005, WERR_OK },
+ { 1006, WERR_OK },
+/* { 1007, WERR_OK }, */
+ { 1501, WERR_OK },
+ };
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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");
+
+ info.info2 = talloc(tctx, struct srvsvc_NetShareInfo2);
+ info.info2->name = r.in.share_name;
+ info.info2->type = STYPE_DISKTREE;
+ info.info2->comment = talloc_strdup(tctx, "test comment");
+ info.info2->permissions = 123434566;
+ info.info2->max_users = -1;
+ info.info2->current_users = 0;
+ info.info2->path = talloc_strdup(tctx, "C:\\");
+ info.info2->password = NULL;
+
+ a.in.info = &info;
+ a.in.level = 2;
+ a.in.parm_error = NULL;
+
+ status = dcerpc_srvsvc_NetShareAdd_r(b, 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:
+ info.info0 = talloc(tctx, struct srvsvc_NetShareInfo0);
+ info.info0->name = r.in.share_name;
+ break;
+ case 1:
+ info.info1 = talloc(tctx, struct srvsvc_NetShareInfo1);
+ info.info1->name = r.in.share_name;
+ info.info1->type = STYPE_DISKTREE;
+ info.info1->comment = talloc_strdup(tctx, "test comment 1");
+ break;
+ case 2:
+ info.info2 = talloc(tctx, struct srvsvc_NetShareInfo2);
+ info.info2->name = r.in.share_name;
+ info.info2->type = STYPE_DISKTREE;
+ info.info2->comment = talloc_strdup(tctx, "test comment 2");
+ info.info2->permissions = 0;
+ info.info2->max_users = 2;
+ info.info2->current_users = 1;
+ info.info2->path = talloc_strdup(tctx, "::BLaH::"); /* "C:\\"); */
+ info.info2->password = NULL;
+ break;
+ case 501:
+ info.info501 = talloc(tctx, struct srvsvc_NetShareInfo501);
+ info.info501->name = r.in.share_name;
+ info.info501->type = STYPE_DISKTREE;
+ info.info501->comment = talloc_strdup(tctx, "test comment 501");
+ info.info501->csc_policy = 0;
+ break;
+ case 502:
+ ZERO_STRUCT(sd_buf);
+ info.info502 = talloc(tctx, struct srvsvc_NetShareInfo502);
+ info.info502->name = r.in.share_name;
+ info.info502->type = STYPE_DISKTREE;
+ info.info502->comment = talloc_strdup(tctx, "test comment 502");
+ info.info502->permissions = 0;
+ info.info502->max_users = 502;
+ info.info502->current_users = 1;
+ info.info502->path = talloc_strdup(tctx, "C:\\");
+ info.info502->password = NULL;
+ info.info502->sd_buf = sd_buf;
+ break;
+ case 1004:
+ info.info1004 = talloc(tctx, struct srvsvc_NetShareInfo1004);
+ info.info1004->comment = talloc_strdup(tctx, "test comment 1004");
+ break;
+ case 1005:
+ info.info1005 = talloc(tctx, struct srvsvc_NetShareInfo1005);
+ info.info1005->dfs_flags = 0;
+ break;
+ case 1006:
+ info.info1006 = talloc(tctx, struct srvsvc_NetShareInfo1006);
+ info.info1006->max_users = 1006;
+ break;
+/* case 1007:
+ info.info1007 = talloc(tctx, struct srvsvc_NetShareInfo1007);
+ info.info1007->flags = 0;
+ info.info1007->alternate_directory_name = talloc_strdup(tctx, "test");
+ break;
+*/
+ case 1501:
+ info.info1501 = talloc_zero(tctx, struct sec_desc_buf);
+ break;
+ }
+
+ r.in.info = &info;
+
+ status = dcerpc_srvsvc_NetShareSetInfo_r(b, 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;
+ q.out.info = &info;
+
+ status = dcerpc_srvsvc_NetShareGetInfo_r(b, 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->info2->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->info502->max_users, 502, "max users");
+ torture_assert_str_equal(tctx, q.out.info->info502->path, "C:\\", "path");
+ break;
+ case 1004:
+ torture_assert_str_equal(tctx, q.out.info->info1004->comment, "test comment 1004",
+ "comment");
+ break;
+ case 1005:
+ break;
+ case 1006:
+ torture_assert_int_equal(tctx, q.out.info->info1006->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_r(b, 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_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr0 c0;
+ struct srvsvc_NetShareCtr1 c1;
+ struct srvsvc_NetShareCtr2 c2;
+ struct srvsvc_NetShareCtr501 c501;
+ struct srvsvc_NetShareCtr502 c502;
+ uint32_t totalentries = 0;
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(info_ctr);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = &resume_handle;
+ r.out.resume_handle = &resume_handle;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+
+ int j;
+ WERROR expected;
+
+ info_ctr.level = levels[i].level;
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 501:
+ ZERO_STRUCT(c501);
+ info_ctr.ctr.ctr501 = &c501;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ }
+
+ expected = levels[i].anon_status;
+ if (admin) expected = levels[i].admin_status;
+
+ resume_handle = 0;
+
+ torture_comment(tctx, "Testing NetShareEnumAll level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetShareEnumAll_r(b, 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 (info_ctr.level == 2 && r.out.info_ctr->ctr.ctr2) {
+ for (j=0;j<r.out.info_ctr->ctr.ctr2->count;j++) {
+ const char *name;
+ name = r.out.info_ctr->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_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr0 c0;
+ struct srvsvc_NetShareCtr1 c1;
+ struct srvsvc_NetShareCtr2 c2;
+ struct srvsvc_NetShareCtr501 c501;
+ struct srvsvc_NetShareCtr502 c502;
+ uint32_t totalentries = 0;
+ 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_INVALID_LEVEL, WERR_INVALID_LEVEL },
+ { 502, WERR_ACCESS_DENIED, WERR_OK },
+ };
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.in.info_ctr = &info_ctr;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ WERROR expected;
+
+ info_ctr.level = levels[i].level;
+
+ switch (info_ctr.level) {
+ case 0:
+ ZERO_STRUCT(c0);
+ info_ctr.ctr.ctr0 = &c0;
+ break;
+ case 1:
+ ZERO_STRUCT(c1);
+ info_ctr.ctr.ctr1 = &c1;
+ break;
+ case 2:
+ ZERO_STRUCT(c2);
+ info_ctr.ctr.ctr2 = &c2;
+ break;
+ case 501:
+ ZERO_STRUCT(c501);
+ info_ctr.ctr.ctr501 = &c501;
+ break;
+ case 502:
+ ZERO_STRUCT(c502);
+ info_ctr.ctr.ctr502 = &c502;
+ break;
+ }
+
+ expected = levels[i].anon_status;
+ if (admin) expected = levels[i].admin_status;
+
+ torture_comment(tctx, "Testing NetShareEnum level %u\n", info_ctr.level);
+ status = dcerpc_srvsvc_NetShareEnum_r(b, 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;
+ union srvsvc_NetSrvInfo info;
+ uint32_t levels[] = {100, 101, 102, 502, 503};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ r.in.level = levels[i];
+ r.out.info = &info;
+ torture_comment(tctx, "Testing NetSrvGetInfo level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetSrvGetInfo_r(b, 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;
+ struct srvsvc_NetDiskInfo info;
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0};
+ int i;
+ uint32_t resume_handle=0;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(info);
+ ZERO_STRUCT(r);
+
+ r.in.server_unc = NULL;
+ r.in.resume_handle = &resume_handle;
+ r.in.info = &info;
+ r.out.info = &info;
+ r.out.totalentries = &totalentries;
+ r.out.resume_handle = &resume_handle;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ ZERO_STRUCTP(r.out.info);
+ r.in.level = levels[i];
+ torture_comment(tctx, "Testing NetDiskEnum level %u\n", r.in.level);
+ status = dcerpc_srvsvc_NetDiskEnum_r(b, 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_NetTransportInfoCtr transports;
+ struct srvsvc_NetTransportCtr0 ctr0;
+ struct srvsvc_NetTransportCtr1 ctr1;
+
+ uint32_t totalentries = 0;
+ uint32_t levels[] = {0, 1};
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(transports);
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s", dcerpc_server_name(p));
+ r.in.transports = &transports;
+ r.in.max_buffer = (uint32_t)-1;
+ r.in.resume_handle = NULL;
+ r.out.totalentries = &totalentries;
+ r.out.transports = &transports;
+
+ for (i=0;i<ARRAY_SIZE(levels);i++) {
+ transports.level = levels[i];
+ switch (transports.level) {
+ case 0:
+ ZERO_STRUCT(ctr0);
+ transports.ctr.ctr0 = &ctr0;
+ break;
+ case 1:
+ ZERO_STRUCT(ctr1);
+ transports.ctr.ctr1 = &ctr1;
+ break;
+ }
+ torture_comment(tctx, "Testing NetTransportEnum level %u\n", transports.level);
+ status = dcerpc_srvsvc_NetTransportEnum_r(b, 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;
+ struct srvsvc_NetRemoteTODInfo *info = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_unc = talloc_asprintf(tctx,"\\\\%s",dcerpc_server_name(p));
+ r.out.info = &info;
+
+ torture_comment(tctx, "Testing NetRemoteTOD\n");
+ status = dcerpc_srvsvc_NetRemoteTOD_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ if (n == min) {
+ /*
+ * We did not move, so
+ * do not loop forever
+ */
+ break;
+ }
+ 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_r(b, 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 0000000..746b399
--- /dev/null
+++ b/source4/torture/rpc/svcctl.c
@@ -0,0 +1,736 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for svcctl rpc operations
+
+ Copyright (C) Jelmer Vernooij 2004
+ Copyright (C) Guenther Deschner 2008,2009,2020
+
+ 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_svcctl_c.h"
+#include "librpc/gen_ndr/ndr_svcctl.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+
+#define TORTURE_DEFAULT_SERVICE "Spooler"
+
+static bool test_OpenSCManager(struct dcerpc_binding_handle *b,
+ 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_r(b, tctx, &r),
+ "OpenSCManager failed!");
+
+ return true;
+}
+
+static bool test_CloseServiceHandle(struct dcerpc_binding_handle *b,
+ 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_r(b, tctx, &r),
+ "CloseServiceHandle failed");
+
+ return true;
+}
+
+static bool test_OpenService(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *h,
+ const char *name,
+ struct policy_handle *s)
+{
+ struct svcctl_OpenServiceW r;
+
+ r.in.scmanager_handle = h;
+ r.in.ServiceName = name;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = s;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_OpenServiceW_r(b, tctx, &r),
+ "OpenServiceW failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "OpenServiceW failed!");
+
+ return true;
+
+}
+
+static bool test_QueryServiceStatus(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct svcctl_QueryServiceStatus r;
+ struct policy_handle h, s;
+ struct SERVICE_STATUS service_status;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ r.in.handle = &s;
+ r.out.service_status = &service_status;
+
+ status = dcerpc_svcctl_QueryServiceStatus_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatus failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceStatus failed!");
+
+ if (!test_CloseServiceHandle(b, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_QueryServiceStatusEx(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct svcctl_QueryServiceStatusEx r;
+ struct policy_handle h, s;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ uint32_t info_level = SVC_STATUS_PROCESS_INFO;
+ uint8_t *buffer;
+ uint32_t offered = 0;
+ uint32_t needed = 0;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ buffer = talloc(tctx, uint8_t);
+
+ r.in.handle = &s;
+ r.in.info_level = info_level;
+ r.in.offered = offered;
+ r.out.buffer = buffer;
+ r.out.needed = &needed;
+
+ status = dcerpc_svcctl_QueryServiceStatusEx_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatusEx failed!");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ r.in.offered = needed;
+ buffer = talloc_array(tctx, uint8_t, needed);
+ r.out.buffer = buffer;
+
+ status = dcerpc_svcctl_QueryServiceStatusEx_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatusEx failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceStatusEx failed!");
+ }
+
+ if (!test_CloseServiceHandle(b, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_QueryServiceConfigW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct svcctl_QueryServiceConfigW r;
+ struct QUERY_SERVICE_CONFIG query;
+ struct policy_handle h, s;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ uint32_t offered = 0;
+ uint32_t needed = 0;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ r.in.handle = &s;
+ r.in.offered = offered;
+ r.out.query = &query;
+ r.out.needed = &needed;
+
+ status = dcerpc_svcctl_QueryServiceConfigW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigW failed!");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ r.in.offered = needed;
+ status = dcerpc_svcctl_QueryServiceConfigW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigW failed!");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfigW failed!");
+
+ if (!test_CloseServiceHandle(b, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_QueryServiceConfig2W(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct svcctl_QueryServiceConfig2W r;
+ struct policy_handle h, s;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ uint32_t info_level = SERVICE_CONFIG_DESCRIPTION;
+ uint8_t *buffer;
+ uint32_t offered = 0;
+ uint32_t needed = 0;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ buffer = talloc(tctx, uint8_t);
+
+ r.in.handle = &s;
+ r.in.info_level = info_level;
+ r.in.offered = offered;
+ r.out.buffer = buffer;
+ r.out.needed = &needed;
+
+ status = dcerpc_svcctl_QueryServiceConfig2W_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ r.in.offered = needed;
+ buffer = talloc_array(tctx, uint8_t, needed);
+ r.out.buffer = buffer;
+
+ status = dcerpc_svcctl_QueryServiceConfig2W_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfig2W failed!");
+ }
+
+ r.in.info_level = SERVICE_CONFIG_FAILURE_ACTIONS;
+ r.in.offered = offered;
+ r.out.buffer = buffer;
+ r.out.needed = &needed;
+
+ status = dcerpc_svcctl_QueryServiceConfig2W_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ r.in.offered = needed;
+ buffer = talloc_array(tctx, uint8_t, needed);
+ r.out.buffer = buffer;
+
+ status = dcerpc_svcctl_QueryServiceConfig2W_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfig2W failed!");
+ }
+
+ if (!test_CloseServiceHandle(b, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_QueryServiceObjectSecurity(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct svcctl_QueryServiceObjectSecurity r;
+ struct policy_handle h, s;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ uint8_t *buffer = NULL;
+ uint32_t needed;
+
+ enum ndr_err_code ndr_err;
+ struct security_descriptor sd;
+ DATA_BLOB blob;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ r.in.handle = &s;
+ r.in.security_flags = 0;
+ r.in.offered = 0;
+ r.out.buffer = NULL;
+ r.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &r),
+ "QueryServiceObjectSecurity failed!");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "QueryServiceObjectSecurity failed!");
+
+ r.in.security_flags = SECINFO_DACL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &r),
+ "QueryServiceObjectSecurity failed!");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ r.in.offered = needed;
+ buffer = talloc_array(tctx, uint8_t, needed);
+ r.out.buffer = buffer;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &r),
+ "QueryServiceObjectSecurity failed!");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result, "QueryServiceObjectSecurity failed!");
+
+ blob = data_blob_const(buffer, needed);
+
+ ndr_err = ndr_pull_struct_blob(&blob, tctx, &sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ if (DEBUGLEVEL >= 1) {
+ NDR_PRINT_DEBUG(security_descriptor, &sd);
+ }
+
+ if (!test_CloseServiceHandle(b, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_SetServiceObjectSecurity(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct svcctl_QueryServiceObjectSecurity q;
+ struct svcctl_SetServiceObjectSecurity r;
+ struct policy_handle h, s;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ uint8_t *buffer;
+ uint32_t needed;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ q.in.handle = &s;
+ q.in.security_flags = SECINFO_DACL;
+ q.in.offered = 0;
+ q.out.buffer = NULL;
+ q.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &q),
+ "QueryServiceObjectSecurity failed!");
+
+ if (W_ERROR_EQUAL(q.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ q.in.offered = needed;
+ buffer = talloc_array(tctx, uint8_t, needed);
+ q.out.buffer = buffer;
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_QueryServiceObjectSecurity_r(b, tctx, &q),
+ "QueryServiceObjectSecurity failed!");
+ }
+
+ torture_assert_werr_ok(tctx, q.out.result,
+ "QueryServiceObjectSecurity failed!");
+
+ r.in.handle = &s;
+ r.in.security_flags = SECINFO_DACL;
+ r.in.buffer = q.out.buffer;
+ r.in.offered = *q.out.needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_SetServiceObjectSecurity_r(b, tctx, &r),
+ "SetServiceObjectSecurity failed!");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "SetServiceObjectSecurity failed!");
+
+ if (!test_CloseServiceHandle(b, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_StartServiceW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct svcctl_StartServiceW r;
+ struct policy_handle h, s;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ r.in.handle = &s;
+ r.in.NumArgs = 0;
+ r.in.Arguments = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_StartServiceW_r(b, tctx, &r),
+ "StartServiceW failed!");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_SERVICE_ALREADY_RUNNING,
+ "StartServiceW failed!");
+
+ if (!test_CloseServiceHandle(b, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_ControlService(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct svcctl_ControlService r;
+ struct policy_handle h, s;
+ struct SERVICE_STATUS service_status;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ r.in.handle = &s;
+ r.in.control = 0;
+ r.out.service_status = &service_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_ControlService_r(b, tctx, &r),
+ "ControlService failed!");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "ControlService failed!");
+
+ if (!test_CloseServiceHandle(b, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ 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_STATUSW *service = NULL;
+ uint32_t needed = 0;
+ uint32_t services_returned = 0;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ r.in.handle = &h;
+ r.in.type = SERVICE_TYPE_WIN32;
+ r.in.state = SERVICE_STATE_ALL;
+ r.in.offered = 0;
+ r.in.resume_handle = &resume_handle;
+ r.out.service = NULL;
+ r.out.resume_handle = &resume_handle;
+ r.out.services_returned = &services_returned;
+ r.out.needed = &needed;
+
+ status = dcerpc_svcctl_EnumServicesStatusW_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.in.offered = needed;
+ r.out.service = talloc_array(tctx, uint8_t, needed);
+
+ status = dcerpc_svcctl_EnumServicesStatusW_r(b, tctx, &r);
+
+ torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "EnumServicesStatus failed");
+ }
+
+ if (services_returned > 0) {
+
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ struct ndr_pull *ndr;
+
+ blob.length = r.in.offered;
+ blob.data = talloc_steal(tctx, r.out.service);
+
+ ndr = ndr_pull_init_blob(&blob, tctx);
+
+ service = talloc_array(tctx, struct ENUM_SERVICE_STATUSW, services_returned);
+ if (!service) {
+ return false;
+ }
+
+ ndr_err = ndr_pull_ENUM_SERVICE_STATUSW_array(
+ ndr, services_returned, service);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+ }
+
+ for(i = 0; i < services_returned; i++) {
+
+ torture_assert(tctx, service[i].service_name,
+ "Service without name returned!");
+
+ printf("%-20s \"%s\", Type: %d, State: %d\n",
+ service[i].service_name, service[i].display_name,
+ service[i].status.type, service[i].status.state);
+ }
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_EnumDependentServicesW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct svcctl_EnumDependentServicesW r;
+ struct policy_handle h, s;
+ uint32_t needed;
+ uint32_t services_returned;
+ uint32_t i;
+ uint32_t states[] = { SERVICE_STATE_ACTIVE,
+ SERVICE_STATE_INACTIVE,
+ SERVICE_STATE_ALL };
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s))
+ return false;
+
+ r.in.service = &s;
+ r.in.offered = 0;
+ r.in.state = 0;
+ r.out.service_status = NULL;
+ r.out.services_returned = &services_returned;
+ r.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_EnumDependentServicesW_r(b, tctx, &r),
+ "EnumDependentServicesW failed!");
+
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "EnumDependentServicesW failed!");
+
+ for (i=0; i<ARRAY_SIZE(states); i++) {
+
+ r.in.state = states[i];
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_EnumDependentServicesW_r(b, tctx, &r),
+ "EnumDependentServicesW failed!");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ r.in.offered = needed;
+ r.out.service_status = talloc_array(tctx, uint8_t, needed);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_svcctl_EnumDependentServicesW_r(b, tctx, &r),
+ "EnumDependentServicesW failed!");
+
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "EnumDependentServicesW failed");
+ }
+
+ if (!test_CloseServiceHandle(b, tctx, &s))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_SCManager(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct policy_handle h;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenSCManager(b, tctx, &h))
+ return false;
+
+ if (!test_CloseServiceHandle(b, tctx, &h))
+ return false;
+
+ return true;
+}
+
+static bool test_ChangeServiceConfigW(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct svcctl_ChangeServiceConfigW r;
+ struct svcctl_QueryServiceConfigW q;
+ struct policy_handle h, s;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct QUERY_SERVICE_CONFIG query;
+ bool ok;
+
+ uint32_t offered = 0;
+ uint32_t needed = 0;
+
+ ok = test_OpenSCManager(b, tctx, &h);
+ if (!ok) {
+ return false;
+ }
+
+ ok = test_OpenService(b, tctx, &h, TORTURE_DEFAULT_SERVICE, &s);
+ if (!ok) {
+ return false;
+ }
+
+ q.in.handle = &s;
+ q.in.offered = offered;
+ q.out.query = &query;
+ q.out.needed = &needed;
+
+ status = dcerpc_svcctl_QueryServiceConfigW_r(b, tctx, &q);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigW failed!");
+
+ if (W_ERROR_EQUAL(q.out.result, WERR_INSUFFICIENT_BUFFER)) {
+ q.in.offered = needed;
+ status = dcerpc_svcctl_QueryServiceConfigW_r(b, tctx, &q);
+ torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfigW failed!");
+ }
+ torture_assert_werr_ok(tctx, q.out.result, "QueryServiceConfigW failed!");
+
+ r.in.handle = &s;
+ r.in.type = query.service_type;
+ r.in.start_type = query.start_type;
+ r.in.error_control = query.error_control;
+
+ /*
+ * according to MS-SCMR 3.1.4.11 NULL params are supposed to leave the
+ * existing values intact.
+ */
+
+ r.in.binary_path = NULL;
+ r.in.load_order_group = NULL;
+ r.in.dependencies = NULL;
+ r.in.dwDependSize = 0;
+ r.in.service_start_name = NULL;
+ r.in.password = NULL;
+ r.in.dwPwSize = 0;
+ r.in.display_name = NULL;
+ r.in.tag_id = NULL;
+ r.out.tag_id = NULL;
+
+ status = dcerpc_svcctl_ChangeServiceConfigW_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "ChangeServiceConfigW failed!");
+ torture_assert_werr_ok(tctx, r.out.result, "ChangeServiceConfigW failed!");
+
+ ok = test_CloseServiceHandle(b, tctx, &s);
+ if (!ok) {
+ return false;
+ }
+
+ ok = test_CloseServiceHandle(b, tctx, &h);
+ if (!ok) {
+ 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);
+ torture_rpc_tcase_add_test(tcase, "EnumDependentServicesW",
+ test_EnumDependentServicesW);
+ torture_rpc_tcase_add_test(tcase, "QueryServiceStatus",
+ test_QueryServiceStatus);
+ torture_rpc_tcase_add_test(tcase, "QueryServiceStatusEx",
+ test_QueryServiceStatusEx);
+ torture_rpc_tcase_add_test(tcase, "QueryServiceConfigW",
+ test_QueryServiceConfigW);
+ torture_rpc_tcase_add_test(tcase, "QueryServiceConfig2W",
+ test_QueryServiceConfig2W);
+ torture_rpc_tcase_add_test(tcase, "QueryServiceObjectSecurity",
+ test_QueryServiceObjectSecurity);
+ torture_rpc_tcase_add_test(tcase, "SetServiceObjectSecurity",
+ test_SetServiceObjectSecurity);
+ torture_rpc_tcase_add_test(tcase, "StartServiceW",
+ test_StartServiceW);
+ torture_rpc_tcase_add_test(tcase, "ControlService",
+ test_ControlService);
+ torture_rpc_tcase_add_test(tcase, "ChangeServiceConfigW",
+ test_ChangeServiceConfigW);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/testjoin.c b/source4/torture/rpc/testjoin.c
new file mode 100644
index 0000000..0a3c96f
--- /dev/null
+++ b/source4/torture/rpc/testjoin.c
@@ -0,0 +1,915 @@
+/*
+ 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 "system/time.h"
+#include "libnet/libnet.h"
+#include "lib/cmdline/cmdline.h"
+#include "librpc/gen_ndr/ndr_lsa_c.h"
+#include "librpc/gen_ndr/ndr_samr_c.h"
+
+#include "libcli/auth/libcli_auth.h"
+#include "torture/rpc/torture_rpc.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "source3/rpc_client/init_samr.h"
+
+struct test_join {
+ struct dcerpc_pipe *p;
+ struct policy_handle user_handle;
+ struct policy_handle domain_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 torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ 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 samr_Ids rids, types;
+ 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;
+ n.out.rids = &rids;
+ n.out.types = &types;
+
+ status = dcerpc_samr_LookupNames_r(b, mem_ctx, &n);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (NT_STATUS_IS_OK(n.out.result)) {
+ rid = n.out.rids->ids[0];
+ } else {
+ return n.out.result;
+ }
+
+ 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_r(b, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "OpenUser(%s) failed - %s\n", name, nt_errstr(status));
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "OpenUser(%s) failed - %s\n", name, nt_errstr(r.out.result));
+ return r.out.result;
+ }
+
+ d.in.user_handle = &user_handle;
+ d.out.user_handle = &user_handle;
+ status = dcerpc_samr_DeleteUser_r(b, mem_ctx, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(d.out.result)) {
+ return d.out.result;
+ }
+
+ 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_max_pwlen(struct torture_context *tctx,
+ const char *username,
+ const char *domain,
+ uint16_t acct_type,
+ const char **random_password,
+ int max_pw_len)
+{
+ NTSTATUS status;
+ struct samr_Connect c;
+ struct samr_CreateUser2 r;
+ struct samr_OpenDomain o;
+ struct samr_LookupDomain l;
+ struct dom_sid2 *sid = NULL;
+ struct samr_GetUserPwInfo pwp;
+ struct samr_PwInfo info;
+ struct samr_SetUserInfo s;
+ union samr_UserInfo u;
+ struct policy_handle 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(tctx, "dc_binding", NULL);
+ struct dcerpc_binding_handle *b = NULL;
+ join = talloc(NULL, struct test_join);
+ if (join == NULL) {
+ return NULL;
+ }
+
+ ZERO_STRUCTP(join);
+
+ torture_comment(tctx, "Connecting to SAMR\n");
+
+ if (dc_binding) {
+ status = dcerpc_pipe_connect(join,
+ &join->p,
+ dc_binding,
+ &ndr_table_samr,
+ samba_cmdline_get_creds(),
+ NULL, tctx->lp_ctx);
+
+ } else {
+ status = torture_rpc_connection(tctx,
+ &join->p,
+ &ndr_table_samr);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+ b = join->p->binding_handle;
+
+ c.in.system_name = NULL;
+ c.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ c.out.connect_handle = &handle;
+
+ status = dcerpc_samr_Connect_r(b, join, &c);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *errstr = nt_errstr(status);
+ torture_comment(tctx, "samr_Connect failed - %s\n", errstr);
+ return NULL;
+ }
+ if (!NT_STATUS_IS_OK(c.out.result)) {
+ const char *errstr = nt_errstr(c.out.result);
+ torture_comment(tctx, "samr_Connect failed - %s\n", errstr);
+ return NULL;
+ }
+
+ if (domain) {
+ torture_comment(tctx, "Opening domain %s\n", domain);
+
+ name.string = domain;
+ l.in.connect_handle = &handle;
+ l.in.domain_name = &name;
+ l.out.sid = &sid;
+
+ status = dcerpc_samr_LookupDomain_r(b, join, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+ if (!NT_STATUS_IS_OK(l.out.result)) {
+ torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(l.out.result));
+ goto failed;
+ }
+ } else {
+ struct samr_EnumDomains e;
+ uint32_t resume_handle = 0, num_entries;
+ struct samr_SamArray *sam;
+ int i;
+
+ e.in.connect_handle = &handle;
+ e.in.buf_size = (uint32_t)-1;
+ e.in.resume_handle = &resume_handle;
+ e.out.sam = &sam;
+ e.out.num_entries = &num_entries;
+ e.out.resume_handle = &resume_handle;
+
+ status = dcerpc_samr_EnumDomains_r(b, join, &e);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "EnumDomains failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+ if (!NT_STATUS_IS_OK(e.out.result)) {
+ torture_comment(tctx, "EnumDomains failed - %s\n", nt_errstr(e.out.result));
+ goto failed;
+ }
+ if ((num_entries != 2) || (sam && sam->count != 2)) {
+ torture_comment(tctx, "unexpected number of domains\n");
+ goto failed;
+ }
+ for (i=0; i < 2; i++) {
+ if (!strequal(sam->entries[i].name.string, "builtin")) {
+ domain = sam->entries[i].name.string;
+ break;
+ }
+ }
+ if (domain) {
+ torture_comment(tctx, "Opening domain %s\n", domain);
+
+ name.string = domain;
+ l.in.connect_handle = &handle;
+ l.in.domain_name = &name;
+ l.out.sid = &sid;
+
+ status = dcerpc_samr_LookupDomain_r(b, join, &l);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+ if (!NT_STATUS_IS_OK(l.out.result)) {
+ torture_comment(tctx, "LookupDomain failed - %s\n", nt_errstr(l.out.result));
+ goto failed;
+ }
+ } else {
+ torture_comment(tctx, "cannot proceed without domain name\n");
+ 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 = &join->domain_handle;
+
+ status = dcerpc_samr_OpenDomain_r(b, join, &o);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+ if (!NT_STATUS_IS_OK(o.out.result)) {
+ torture_comment(tctx, "OpenDomain failed - %s\n", nt_errstr(o.out.result));
+ goto failed;
+ }
+
+ torture_comment(tctx, "Creating account %s\n", username);
+
+again:
+ name.string = username;
+ r.in.domain_handle = &join->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_r(b, join, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "CreateUser2 failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+
+ if (NT_STATUS_EQUAL(r.out.result, NT_STATUS_USER_EXISTS)) {
+ status = DeleteUser_byname(tctx, b, join, &join->domain_handle, name.string);
+ if (NT_STATUS_IS_OK(status)) {
+ goto again;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "CreateUser2 failed - %s\n", nt_errstr(r.out.result));
+ goto failed;
+ }
+
+ join->user_sid = dom_sid_add_rid(join, join->dom_sid, rid);
+
+ pwp.in.user_handle = &join->user_handle;
+ pwp.out.info = &info;
+
+ status = dcerpc_samr_GetUserPwInfo_r(b, join, &pwp);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(pwp.out.result)) {
+ policy_min_pw_len = pwp.out.info->min_password_length;
+ }
+
+ random_pw = generate_random_password(join, MAX(8, policy_min_pw_len), max_pw_len);
+
+ torture_comment(tctx, "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;
+
+ u.info24.password_expired = 0;
+
+ status = dcerpc_fetch_session_key(join->p, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "SetUserInfo level %u - no session key - %s\n",
+ s.in.level, nt_errstr(status));
+ torture_leave_domain(tctx, join);
+ goto failed;
+ }
+
+ status = init_samr_CryptPassword(random_pw,
+ &session_key,
+ &u.info24.password);
+ torture_assert_ntstatus_ok(tctx,
+ status,
+ "init_samr_CryptPassword failed");
+
+ status = dcerpc_samr_SetUserInfo_r(b, join, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "SetUserInfo failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_comment(tctx, "SetUserInfo failed - %s\n", nt_errstr(s.out.result));
+ 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",
+ lpcfg_netbios_name(tctx->lp_ctx),
+ timestring(join, time(NULL)));
+
+ torture_comment(tctx, "Resetting ACB flags, force pw change time\n");
+
+ status = dcerpc_samr_SetUserInfo_r(b, join, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "SetUserInfo failed - %s\n", nt_errstr(status));
+ goto failed;
+ }
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_comment(tctx, "SetUserInfo failed - %s\n", nt_errstr(s.out.result));
+ goto failed;
+ }
+
+ if (random_password) {
+ *random_password = random_pw;
+ }
+
+ return join;
+
+failed:
+ torture_leave_domain(tctx, join);
+ return NULL;
+}
+
+/*
+ * Set privileges on an account.
+ */
+
+static void init_lsa_StringLarge(struct lsa_StringLarge *name, const char *s)
+{
+ name->string = s;
+}
+static void init_lsa_String(struct lsa_String *name, const char *s)
+{
+ name->string = s;
+}
+
+bool torture_setup_privs(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ uint32_t num_privs,
+ const char **privs,
+ const struct dom_sid *user_sid)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct policy_handle *handle;
+ int i;
+
+ torture_assert(tctx,
+ test_lsa_OpenPolicy2(b, tctx, &handle),
+ "failed to open policy");
+
+ for (i=0; i < num_privs; i++) {
+ struct lsa_LookupPrivValue r;
+ struct lsa_LUID luid;
+ struct lsa_String name;
+
+ init_lsa_String(&name, privs[i]);
+
+ r.in.handle = handle;
+ r.in.name = &name;
+ r.out.luid = &luid;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_LookupPrivValue_r(b, tctx, &r),
+ "lsa_LookupPrivValue failed");
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ torture_comment(tctx, "lsa_LookupPrivValue failed for '%s' with %s\n",
+ privs[i], nt_errstr(r.out.result));
+ return false;
+ }
+ }
+
+ {
+ struct lsa_AddAccountRights r;
+ struct lsa_RightSet rights;
+
+ rights.count = num_privs;
+ rights.names = talloc_zero_array(tctx, struct lsa_StringLarge, rights.count);
+ for (i=0; i < rights.count; i++) {
+ init_lsa_StringLarge(&rights.names[i], privs[i]);
+ }
+
+ r.in.handle = handle;
+ r.in.sid = discard_const_p(struct dom_sid, user_sid);
+ r.in.rights = &rights;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_lsa_AddAccountRights_r(b, tctx, &r),
+ "lsa_AddAccountRights failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result,
+ "lsa_AddAccountRights failed");
+ }
+
+ test_lsa_Close(b, tctx, handle);
+
+ return true;
+}
+
+struct test_join *torture_create_testuser(struct torture_context *torture,
+ const char *username,
+ const char *domain,
+ uint16_t acct_type,
+ const char **random_password)
+{
+ return torture_create_testuser_max_pwlen(torture, username, domain, acct_type, random_password, 255);
+}
+
+NTSTATUS torture_delete_testuser(struct torture_context *torture,
+ struct test_join *join,
+ const char *username)
+{
+ NTSTATUS status;
+
+ status = DeleteUser_byname(torture,
+ join->p->binding_handle,
+ torture,
+ &join->domain_handle,
+ username);
+
+ return status;
+}
+
+_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;
+ const char *binding_str = NULL;
+ struct dcerpc_binding *binding = NULL;
+ enum dcerpc_transport_t transport;
+
+ tj = talloc_zero(tctx, struct test_join);
+ if (!tj) return NULL;
+
+ binding_str = torture_setting_string(tctx, "binding", NULL);
+ if (binding_str == NULL) {
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ binding_str = talloc_asprintf(tj, "ncacn_np:%s", host);
+ }
+ status = dcerpc_parse_binding(tj, binding_str, &binding);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_parse_binding(%s) failed - %s\n",
+ binding_str, nt_errstr(status)));
+ talloc_free(tj);
+ return NULL;
+ }
+ transport = dcerpc_binding_get_transport(binding);
+ switch (transport) {
+ case NCALRPC:
+ case NCACN_UNIX_STREAM:
+ break;
+ default:
+ dcerpc_binding_set_transport(binding, NCACN_NP);
+ dcerpc_binding_set_flags(binding, 0, DCERPC_AUTH_OPTIONS);
+ break;
+ }
+
+ libnet_r = talloc_zero(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 = samba_cmdline_get_creds();
+ libnet_r->in.binding = dcerpc_binding_string(libnet_r, binding);
+ if (libnet_r->in.binding == NULL) {
+ talloc_free(tj);
+ return 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",
+ lpcfg_netbios_name(tctx->lp_ctx), timestring(tj, time(NULL)));
+
+ status = dcerpc_samr_SetUserInfo_r(tj->p->binding_handle, tj, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "SetUserInfo (non-critical) failed - %s\n", nt_errstr(status));
+ }
+ if (!NT_STATUS_IS_OK(s.out.result)) {
+ torture_comment(tctx, "SetUserInfo (non-critical) failed - %s\n", nt_errstr(s.out.result));
+ }
+
+ *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",
+ dcerpc_binding_get_string_option(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", samba_cmdline_get_creds());
+ ldb_set_opaque(ldb_ctx, "loadparm", samba_cmdline_get_lp_ctx());
+
+ rtn = ldb_connect(ldb_ctx, remote_ldb_url, 0, NULL);
+ if (rtn != LDB_SUCCESS) {
+ libnet_r->out.error_string = NULL;
+ talloc_free(tmp_ctx);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ rtn = ldb_delete(ldb_ctx, server_dn);
+ if (rtn != LDB_SUCCESS) {
+ 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 *tctx, 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_r(join->p->binding_handle, join, &d);
+ if (!NT_STATUS_IS_OK(status)) {
+ torture_comment(tctx, "DeleteUser failed\n");
+ } else if (!NT_STATUS_IS_OK(d.out.result)) {
+ torture_comment(tctx, "Delete of machine account %s failed\n",
+ join->netbios_name);
+ } else {
+ torture_comment(tctx, "Delete of machine account %s was successful.\n",
+ join->netbios_name);
+ }
+
+ if (join->libnet_r) {
+ status = torture_leave_ads_domain(tctx, 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;
+}
+
+#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/torture_rpc.h b/source4/torture/rpc/torture_rpc.h
new file mode 100644
index 0000000..9217461
--- /dev/null
+++ b/source4/torture/rpc/torture_rpc.h
@@ -0,0 +1,126 @@
+/*
+ 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 "lib/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 "librpc/gen_ndr/ndr_spoolss.h"
+#include "torture/rpc/proto.h"
+
+struct torture_rpc_tcase {
+ struct torture_tcase tcase;
+ const struct ndr_interface_table *table;
+ const char *machine_name;
+ bool (*setup_fn)(struct torture_context *,
+ struct dcerpc_pipe *,
+ void *);
+ bool (*teardown_fn)(struct torture_context *,
+ struct dcerpc_pipe *,
+ void *);
+};
+
+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);
+NTSTATUS torture_rpc_connection_with_binding(struct torture_context *tctx,
+ struct dcerpc_binding *binding,
+ 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_rpc_tcase *torture_suite_add_rpc_setup_tcase(
+ struct torture_suite *suite,
+ const char *name,
+ const struct ndr_interface_table *table,
+ bool (*setup_fn)(struct torture_context *,
+ struct dcerpc_pipe *,
+ void *),
+ bool (*teardown_fn)(struct torture_context *,
+ struct dcerpc_pipe *,
+ void *));
+
+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_join(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn) (struct torture_context *, struct dcerpc_pipe *,
+ struct cli_credentials *, struct test_join *));
+_PUBLIC_ struct torture_test *torture_rpc_tcase_add_test_setup(
+ struct torture_rpc_tcase *tcase,
+ const char *name,
+ bool (*fn)(struct torture_context *,
+ struct dcerpc_pipe *,
+ void *),
+ void *userdata);
+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_bdc_rpc_iface_tcase(
+ struct torture_suite *suite,
+ const char *name,
+ const struct ndr_interface_table *table,
+ const char *machine_name);
+struct torture_rpc_tcase *torture_suite_add_machine_workstation_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/unixinfo.c b/source4/torture/rpc/unixinfo.c
new file mode 100644
index 0000000..227b002
--- /dev/null
+++ b/source4/torture/rpc/unixinfo.c
@@ -0,0 +1,149 @@
+/*
+ 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/rpc/torture_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)
+{
+ struct unixinfo_SidToUid r;
+ struct dom_sid *sid;
+ uint64_t uid;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432");
+ r.in.sid = *sid;
+ r.out.uid = &uid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_SidToUid_r(b, tctx, &r),
+ "SidToUid failed");
+ if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, r.out.result)) {
+ } else torture_assert_ntstatus_ok(tctx, r.out.result, "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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.uid = 1000;
+ r.out.sid = &sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_UidToSid_r(b, tctx, &r),
+ "UidToSid failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_GetPWUid_r(b, tctx, &r),
+ "GetPWUid failed");
+
+ torture_assert_ntstatus_ok(tctx, r.out.result, "GetPWUid failed");
+
+ return true;
+}
+
+/*
+ test the SidToGid interface
+*/
+static bool test_sidtogid(struct torture_context *tctx, struct dcerpc_pipe *p)
+{
+ struct unixinfo_SidToGid r;
+ struct dom_sid *sid;
+ uint64_t gid;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ sid = dom_sid_parse_talloc(tctx, "S-1-5-32-1234-5432");
+ r.in.sid = *sid;
+ r.out.gid = &gid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_SidToGid_r(b, tctx, &r),
+ "SidToGid failed");
+ if (NT_STATUS_EQUAL(NT_STATUS_NONE_MAPPED, r.out.result)) {
+ } else torture_assert_ntstatus_ok(tctx, r.out.result, "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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.gid = 1000;
+ r.out.sid = &sid;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_unixinfo_GidToSid_r(b, tctx, &r),
+ "GidToSid failed");
+ torture_assert_ntstatus_ok(tctx, r.out.result, "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 0000000..28e435c
--- /dev/null
+++ b/source4/torture/rpc/winreg.c
@@ -0,0 +1,3245 @@
+/*
+ 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,2010
+
+ 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_winreg_c.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "libcli/security/security.h"
+#include "torture/rpc/torture_rpc.h"
+#include "param/param.h"
+#include "lib/registry/registry.h"
+
+#define TEST_KEY_BASE "winreg_torture_test"
+#define TEST_KEY1 "spottyfoot"
+#define TEST_KEY2 "with a SD (#1)"
+#define TEST_KEY3 "with a subkey"
+#define TEST_KEY4 "sd_tests"
+#define TEST_SUBKEY "subkey"
+#define TEST_SUBKEY_SD "subkey_sd"
+#define TEST_SUBSUBKEY_SD "subkey_sd\\subsubkey_sd"
+#define TEST_VALUE "torture_value_name"
+#define TEST_KEY_VOLATILE "torture_volatile_key"
+#define TEST_SUBKEY_VOLATILE "torture_volatile_subkey"
+#define TEST_KEY_SYMLINK "torture_symlink_key"
+#define TEST_KEY_SYMLINK_DEST "torture_symlink_dest"
+
+#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_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct winreg_GetVersion r;
+ uint32_t v;
+
+ torture_comment(tctx, "Testing GetVersion\n");
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.out.version = &v;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_GetVersion_r(b, tctx, &r),
+ "GetVersion failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "GetVersion failed");
+
+ return true;
+}
+
+static bool test_NotifyChangeKeyValue(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct winreg_NotifyChangeKeyValue r;
+
+ ZERO_STRUCT(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_r(b, 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_opts(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *name,
+ const char *kclass,
+ uint32_t options,
+ uint32_t access_mask,
+ struct winreg_SecBuf *secdesc,
+ WERROR expected_result,
+ enum winreg_CreateAction *action_taken_p,
+ struct policy_handle *new_handle_p)
+{
+ struct winreg_CreateKey r;
+ struct policy_handle newhandle;
+ enum winreg_CreateAction action_taken = 0;
+
+ torture_comment(tctx, "Testing CreateKey(%s)\n", name);
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ init_winreg_String(&r.in.name, name);
+ init_winreg_String(&r.in.keyclass, kclass);
+ r.in.options = options;
+ r.in.access_mask = access_mask;
+ r.in.action_taken = &action_taken;
+ r.in.secdesc = secdesc;
+ r.out.new_handle = &newhandle;
+ r.out.action_taken = &action_taken;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CreateKey_r(b, tctx, &r),
+ "CreateKey failed");
+
+ torture_assert_werr_equal(tctx, r.out.result, expected_result, "CreateKey failed");
+
+ if (new_handle_p) {
+ *new_handle_p = newhandle;
+ }
+ if (action_taken_p) {
+ *action_taken_p = *r.out.action_taken;
+ }
+
+ return true;
+}
+
+static bool test_CreateKey(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle, const char *name,
+ const char *kclass)
+{
+ return test_CreateKey_opts(tctx, b, handle, name, kclass,
+ REG_OPTION_NON_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ NULL, /* secdesc */
+ WERR_OK,
+ NULL, /* action_taken */
+ NULL /* new_handle */);
+}
+
+/*
+ createkey testing with a SD
+*/
+static bool test_CreateKey_sd(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle, const char *name,
+ const char *kclass,
+ 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, 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;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.out.new_handle = newhandle;
+ init_winreg_String(&r.in.name, name);
+ init_winreg_String(&r.in.keyclass, kclass);
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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, 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_r(b, 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_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct winreg_CloseKey r;
+
+ ZERO_STRUCT(r);
+ r.in.handle = r.out.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_CloseKey_r(b, tctx, &r),
+ "CloseKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "CloseKey failed");
+
+ return true;
+}
+
+static bool test_FlushKey(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ struct winreg_FlushKey r;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_FlushKey_r(b, tctx, &r),
+ "FlushKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "FlushKey failed");
+
+ return true;
+}
+
+static bool test_OpenKey_opts(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *hive_handle,
+ const char *keyname,
+ uint32_t options,
+ uint32_t access_mask,
+ struct policy_handle *key_handle,
+ WERROR expected_result)
+{
+ struct winreg_OpenKey r;
+
+ ZERO_STRUCT(r);
+ r.in.parent_handle = hive_handle;
+ init_winreg_String(&r.in.keyname, keyname);
+ r.in.options = options;
+ r.in.access_mask = access_mask;
+ r.out.handle = key_handle;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_OpenKey_r(b, tctx, &r),
+ "OpenKey failed");
+
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "OpenKey failed");
+
+ return true;
+}
+
+static bool test_OpenKey(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *hive_handle,
+ const char *keyname, struct policy_handle *key_handle)
+{
+ return test_OpenKey_opts(tctx, b, hive_handle, keyname,
+ REG_OPTION_NON_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ key_handle,
+ WERR_OK);
+}
+
+static bool test_Cleanup(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle, const char *key)
+{
+ struct winreg_DeleteKey r;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+
+ init_winreg_String(&r.in.key, key);
+ dcerpc_winreg_DeleteKey_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "SecurityDescriptor get & set\n");
+
+ if (!test_OpenKey(b, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle,
+ WERR_OK, WERR_OK)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_OpenKey_opts(tctx, b, handle, key,
+ REG_OPTION_NON_VOLATILE,
+ access_mask,
+ &new_handle,
+ open_werr),
+ "failed to open key");
+
+ if (!W_ERROR_IS_OK(open_werr)) {
+ return true;
+ }
+
+ if (!_test_GetSetSecurityDescriptor(p, tctx, &new_handle,
+ get_werr, set_werr)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenKey(b, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ ret = test_dacl_trustee_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_assert(tctx,
+ test_OpenKey_opts(tctx, b, handle, key,
+ REG_OPTION_NON_VOLATILE,
+ SEC_FLAG_SYSTEM_SECURITY,
+ &new_handle,
+ WERR_OK),
+ "failed to open key");
+
+ ret = test_sacl_trustee_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenKey(b, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ ret = test_owner_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenKey(b, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ ret = test_group_present(p, tctx, &new_handle, sid);
+
+ test_CloseKey(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenKey(b, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!test_SetKeySecurity(p, tctx, &new_handle, sd)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!test_OpenKey(b, tctx, handle, key, &new_handle)) {
+ return false;
+ }
+
+ if (!test_GetKeySecurity(p, tctx, &new_handle, sd)) {
+ ret = false;
+ }
+
+ if (!test_CloseKey(b, 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;
+ bool ret = true;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *test_subkey_sd;
+ const char *test_subsubkey_sd;
+
+ torture_comment(tctx, "SecurityDescriptor inheritance\n");
+
+ if (!test_OpenKey(b, 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);
+
+ torture_assert_ntstatus_ok(tctx,
+ security_descriptor_dacl_add(sd, ace),
+ "failed to add ace");
+
+ /* 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;
+ }
+
+ torture_assert(tctx,
+ test_dacl_ace_present(p, tctx, &new_handle, ace),
+ "new ACE not present!");
+
+ if (!test_CloseKey(b, tctx, &new_handle)) {
+ return false;
+ }
+
+ test_subkey_sd = talloc_asprintf(tctx, "%s\\%s", key, TEST_SUBKEY_SD);
+
+ if (!test_CreateKey(b, tctx, handle, test_subkey_sd, NULL)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_OpenKey(b, tctx, handle, test_subkey_sd, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ torture_comment(tctx, "inherited ACE not present!\n");
+ ret = false;
+ goto out;
+ }
+
+ test_subsubkey_sd = talloc_asprintf(tctx, "%s\\%s", key, TEST_SUBSUBKEY_SD);
+
+ test_CloseKey(b, tctx, &new_handle);
+ if (!test_CreateKey(b, tctx, handle, test_subsubkey_sd, NULL)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_OpenKey(b, tctx, handle, test_subsubkey_sd, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (!test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ torture_comment(tctx, "inherited ACE not present!\n");
+ ret = false;
+ goto out;
+ }
+
+ out:
+ test_CloseKey(b, tctx, &new_handle);
+ test_Cleanup(b, tctx, handle, test_subkey_sd);
+ test_RestoreSecurity(p, tctx, handle, key, sd_orig);
+
+ return ret;
+}
+
+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;
+ bool ret = true;
+ uint8_t ace_flags = 0x0;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *test_subkey_sd;
+ const char *test_subsubkey_sd;
+
+ torture_comment(tctx, "SecurityDescriptor inheritance block\n");
+
+ if (!test_OpenKey(b, 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);
+
+ torture_assert_ntstatus_ok(tctx,
+ security_descriptor_dacl_add(sd, ace),
+ "failed to add ace");
+
+ if (!_test_SetKeySecurity(p, tctx, &new_handle, NULL, sd, WERR_OK)) {
+ return false;
+ }
+
+ torture_assert(tctx,
+ test_dacl_ace_present(p, tctx, &new_handle, ace),
+ "new ACE not present!");
+
+ if (!test_CloseKey(b, tctx, &new_handle)) {
+ return false;
+ }
+
+ test_subkey_sd = talloc_asprintf(tctx, "%s\\%s", key, TEST_SUBKEY_SD);
+ test_subsubkey_sd = talloc_asprintf(tctx, "%s\\%s", key, TEST_SUBSUBKEY_SD);
+
+ if (!test_CreateKey(b, tctx, handle, test_subsubkey_sd, NULL)) {
+ return false;
+ }
+
+ if (!test_OpenKey(b, tctx, handle, test_subsubkey_sd, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ torture_comment(tctx, "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)) {
+ torture_comment(tctx, "inherited trustee SID present but should not!\n");
+ ret = false;
+ goto out;
+ }
+
+ test_CloseKey(b, tctx, &new_handle);
+
+ if (!test_OpenKey(b, tctx, handle, test_subkey_sd, &new_handle)) {
+ ret = false;
+ goto out;
+ }
+
+ if (test_dacl_ace_present(p, tctx, &new_handle, ace)) {
+ torture_comment(tctx, "inherited ACE present but should not!\n");
+ ret = false;
+ goto out;
+ }
+
+ if (!test_dacl_trustee_flags_present(p, tctx, &new_handle, sid, ace_flags)) {
+ torture_comment(tctx, "inherited trustee SID with flags 0x%02x not present!\n",
+ ace_flags);
+ ret = false;
+ goto out;
+ }
+
+ out:
+ test_CloseKey(b, tctx, &new_handle);
+ test_Cleanup(b, 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_FILE_NOT_FOUND, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ torture_comment(tctx, "SecurityDescriptor (%s) sets for secinfo: "
+ "0x%08x, access_mask: 0x%08x\n",
+ test, sec_info, access_mask);
+
+ torture_assert(tctx,
+ test_OpenKey_opts(tctx, b, handle, key,
+ REG_OPTION_NON_VOLATILE,
+ access_mask,
+ &new_handle,
+ WERR_OK),
+ "failed to open key");
+
+ 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(b, tctx, &new_handle);
+ return false;
+ }
+
+ test_CloseKey(b, 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 = sd_owner,
+ .sec_info = 0,
+ .set_werr = WERR_OK,
+ .sid_present = false,
+ .fn = (secinfo_verify_fn)_test_owner_present,
+ },
+ {
+ .sd = sd_owner,
+ .sec_info = SECINFO_OWNER,
+ .set_werr = WERR_OK,
+ .sid_present = true,
+ .fn = (secinfo_verify_fn)_test_owner_present,
+ },
+ {
+ .sd = sd_owner,
+ .sec_info = SECINFO_GROUP,
+ .set_werr = WERR_INVALID_PARAMETER,
+ .sid_present = false,
+ },
+ {
+ .sd = sd_owner,
+ .sec_info = SECINFO_DACL,
+ .set_werr = WERR_OK,
+ .sid_present = true,
+ .fn = (secinfo_verify_fn)_test_owner_present,
+ },
+ {
+ .sd = sd_owner,
+ .sec_info = SECINFO_SACL,
+ .set_werr = WERR_ACCESS_DENIED,
+ .sid_present = false,
+ },
+ };
+
+ uint32_t sd_owner_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ /* SEC_STD_WRITE_OWNER, */
+ };
+
+ struct winreg_secinfo_table sec_info_group_tests[] = {
+ {
+ .sd = sd_group,
+ .sec_info = 0,
+ .set_werr = WERR_OK,
+ .sid_present = false,
+ .fn = (secinfo_verify_fn)_test_group_present,
+ },
+ {
+ .sd = sd_group,
+ .sec_info = SECINFO_OWNER,
+ .set_werr = WERR_INVALID_PARAMETER,
+ .sid_present = false,
+ },
+ {
+ .sd = sd_group,
+ .sec_info = SECINFO_GROUP,
+ .set_werr = WERR_OK,
+ .sid_present = true,
+ .fn = (secinfo_verify_fn)_test_group_present,
+ },
+ {
+ .sd = sd_group,
+ .sec_info = SECINFO_DACL,
+ .set_werr = WERR_OK,
+ .sid_present = true,
+ .fn = (secinfo_verify_fn)_test_group_present,
+ },
+ {
+ .sd = sd_group,
+ .sec_info = SECINFO_SACL,
+ .set_werr = WERR_ACCESS_DENIED,
+ .sid_present = false,
+ },
+ };
+
+ uint32_t sd_group_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ };
+
+ struct winreg_secinfo_table sec_info_dacl_tests[] = {
+ {
+ .sd = sd_dacl,
+ .sec_info = 0,
+ .set_werr = WERR_OK,
+ .sid_present = false,
+ .fn = (secinfo_verify_fn)_test_dacl_trustee_present,
+ },
+ {
+ .sd = sd_dacl,
+ .sec_info = SECINFO_OWNER,
+ .set_werr = WERR_INVALID_PARAMETER,
+ .sid_present = false,
+ },
+ {
+ .sd = sd_dacl,
+ .sec_info = SECINFO_GROUP,
+ .set_werr = WERR_INVALID_PARAMETER,
+ .sid_present = false,
+ },
+ {
+ .sd = sd_dacl,
+ .sec_info = SECINFO_DACL,
+ .set_werr = WERR_OK,
+ .sid_present = true,
+ .fn = (secinfo_verify_fn)_test_dacl_trustee_present
+ },
+ {
+ .sd = sd_dacl,
+ .sec_info = SECINFO_SACL,
+ .set_werr = WERR_ACCESS_DENIED,
+ .sid_present = false,
+ },
+ };
+
+ uint32_t sd_dacl_good_access_masks[] = {
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ SEC_STD_WRITE_DAC,
+ };
+
+ struct winreg_secinfo_table sec_info_sacl_tests[] = {
+ {
+ .sd = sd_sacl,
+ .sec_info = 0,
+ .set_werr = WERR_OK,
+ .sid_present = false,
+ .fn = (secinfo_verify_fn)_test_sacl_trustee_present,
+ },
+ {
+ .sd = sd_sacl,
+ .sec_info = SECINFO_OWNER,
+ .set_werr = WERR_INVALID_PARAMETER,
+ .sid_present = false,
+ },
+ {
+ .sd = sd_sacl,
+ .sec_info = SECINFO_GROUP,
+ .set_werr = WERR_INVALID_PARAMETER,
+ .sid_present = false,
+ },
+ {
+ .sd = sd_sacl,
+ .sec_info = SECINFO_DACL,
+ .set_werr = WERR_OK,
+ .sid_present = false,
+ .fn = (secinfo_verify_fn)_test_sacl_trustee_present,
+ },
+ {
+ .sd = sd_sacl,
+ .sec_info = SECINFO_SACL,
+ .set_werr = WERR_OK,
+ .sid_present = true,
+ .fn = (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))
+ {
+ torture_comment(tctx, "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))
+ {
+ torture_comment(tctx, "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))
+ {
+ torture_comment(tctx, "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))
+ {
+ torture_comment(tctx, "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)) {
+ torture_comment(tctx, "test_SecurityDescriptor failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorInheritance(p, tctx, handle, key)) {
+ torture_comment(tctx, "test_SecurityDescriptorInheritance failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorBlockInheritance(p, tctx, handle, key)) {
+ torture_comment(tctx, "test_SecurityDescriptorBlockInheritance failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorsSecInfo(p, tctx, handle, key)) {
+ torture_comment(tctx, "test_SecurityDescriptorsSecInfo failed\n");
+ ret = false;
+ }
+
+ if (!test_SecurityDescriptorsMasks(p, tctx, handle, key)) {
+ torture_comment(tctx, "test_SecurityDescriptorsMasks failed\n");
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_DeleteKey_opts(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *key,
+ WERROR expected_result)
+{
+ struct winreg_DeleteKey r;
+
+ torture_comment(tctx, "Testing DeleteKey(%s)\n", key);
+
+ r.in.handle = handle;
+ init_winreg_String(&r.in.key, key);
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_DeleteKey_r(b, tctx, &r),
+ "Delete Key failed");
+ torture_assert_werr_equal(tctx, r.out.result, expected_result,
+ "DeleteKey failed");
+
+ return true;
+}
+
+static bool test_DeleteKey(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle, const char *key)
+{
+ return test_DeleteKey_opts(b, tctx, handle, key, WERR_OK);
+}
+
+static bool test_QueryInfoKey(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ char *kclass,
+ uint32_t *pmax_valnamelen,
+ uint32_t *pmax_valbufsize)
+{
+ struct winreg_QueryInfoKey r;
+ uint32_t num_subkeys, max_subkeylen, max_classlen,
+ 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_classlen = &max_classlen;
+ 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, kclass);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryInfoKey_r(b, tctx, &r),
+ "QueryInfoKey failed");
+
+ torture_assert_werr_ok(tctx, r.out.result, "QueryInfoKey failed");
+
+ if (pmax_valnamelen) {
+ *pmax_valnamelen = max_valnamelen;
+ }
+
+ if (pmax_valbufsize) {
+ *pmax_valbufsize = max_valbufsize;
+ }
+
+ return true;
+}
+
+static bool test_SetValue(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *value_name,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t size)
+{
+ struct winreg_SetValue r;
+ struct winreg_String name;
+
+ torture_comment(tctx, "Testing SetValue(%s), type: %s, offered: 0x%08x)\n",
+ value_name, str_regtype(type), size);
+
+ init_winreg_String(&name, value_name);
+
+ r.in.handle = handle;
+ r.in.name = name;
+ r.in.type = type;
+ r.in.data = data;
+ r.in.size = size;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_SetValue_r(b, tctx, &r),
+ "winreg_SetValue failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "winreg_SetValue failed");
+
+ return true;
+}
+
+static bool test_DeleteValue(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *value_name)
+{
+ struct winreg_DeleteValue r;
+ struct winreg_String value;
+
+ torture_comment(tctx, "Testing DeleteValue(%s)\n", value_name);
+
+ init_winreg_String(&value, value_name);
+
+ r.in.handle = handle;
+ r.in.value = value;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_DeleteValue_r(b, tctx, &r),
+ "winreg_DeleteValue failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "winreg_DeleteValue 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 kclass, name;
+ NTSTATUS status;
+ NTTIME t = 0;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ kclass.name = "";
+ kclass.size = 1024;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.in.enum_index = 0;
+ r.in.name = &name;
+ r.in.keyclass = &kclass;
+ r.out.name = &name;
+ r.in.last_changed_time = &t;
+
+ do {
+ name.name = NULL;
+ name.size = 1024;
+
+ status = dcerpc_winreg_EnumKey_r(b, 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(b, 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_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *valuename)
+{
+ struct winreg_QueryMultipleValues r;
+ uint32_t bufsize=0;
+
+ ZERO_STRUCT(r);
+
+ r.in.key_handle = handle;
+ r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, 1);
+ r.in.values_in[0].ve_valuename = talloc(tctx, struct winreg_ValNameBuf);
+ r.in.values_in[0].ve_valuename->name = valuename;
+ /* size needs to be set manually for winreg_ValNameBuf */
+ r.in.values_in[0].ve_valuename->size = strlen_m_term(valuename)*2;
+
+ 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);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r),
+ "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_QueryMultipleValues_full(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t num_values,
+ const char * const *valuenames,
+ bool existing_value)
+{
+ struct winreg_QueryMultipleValues r;
+ uint32_t bufsize = 0;
+ int i;
+
+ torture_comment(tctx, "Testing QueryMultipleValues\n");
+
+ ZERO_STRUCT(r);
+
+ r.in.key_handle = handle;
+ r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, 0);
+ r.in.buffer_size = r.out.buffer_size = &bufsize;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r),
+ "QueryMultipleValues failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "QueryMultipleValues failed");
+
+ /* this test crashes w2k8 remote registry */
+#if 0
+ r.in.num_values = num_values;
+ r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, num_values);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r),
+ "QueryMultipleValues failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "QueryMultipleValues failed");
+#endif
+ r.in.num_values = num_values;
+ r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, num_values);
+ for (i=0; i < r.in.num_values; i++) {
+ r.in.values_in[i].ve_valuename = talloc_zero(tctx, struct winreg_ValNameBuf);
+ r.in.values_in[i].ve_valuename->name = talloc_strdup(tctx, valuenames[i]);
+ r.in.values_in[i].ve_valuename->size = strlen_m_term(r.in.values_in[i].ve_valuename->name)*2;
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r),
+ "QueryMultipleValues failed");
+ torture_assert_werr_equal(tctx, r.out.result, existing_value ? WERR_MORE_DATA : WERR_FILE_NOT_FOUND,
+ "QueryMultipleValues failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_FILE_NOT_FOUND)) {
+ return true;
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ *r.in.buffer_size = 0xff;
+ r.in.buffer = r.out.buffer = talloc_zero_array(tctx, uint8_t, *r.in.buffer_size);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues_r(b, tctx, &r),
+ "QueryMultipleValues failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "QueryMultipleValues failed");
+
+ return true;
+}
+
+
+static bool test_QueryMultipleValues2_full(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ uint32_t num_values,
+ const char * const *valuenames,
+ bool existing_value)
+{
+ struct winreg_QueryMultipleValues2 r;
+ uint32_t offered = 0, needed;
+ int i;
+
+ torture_comment(tctx, "Testing QueryMultipleValues2\n");
+
+ ZERO_STRUCT(r);
+
+ r.in.key_handle = handle;
+ r.in.offered = &offered;
+ r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, 0);
+ r.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r),
+ "QueryMultipleValues2 failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "QueryMultipleValues2 failed");
+
+ /* this test crashes w2k8 remote registry */
+#if 0
+ r.in.num_values = num_values;
+ r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, num_values);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r),
+ "QueryMultipleValues2 failed");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "QueryMultipleValues2 failed");
+#endif
+ r.in.num_values = num_values;
+ r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, num_values);
+ for (i=0; i < r.in.num_values; i++) {
+ r.in.values_in[i].ve_valuename = talloc_zero(tctx, struct winreg_ValNameBuf);
+ r.in.values_in[i].ve_valuename->name = talloc_strdup(tctx, valuenames[i]);
+ r.in.values_in[i].ve_valuename->size = strlen_m_term(r.in.values_in[i].ve_valuename->name)*2;
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r),
+ "QueryMultipleValues2 failed");
+ torture_assert_werr_equal(tctx, r.out.result, existing_value ? WERR_MORE_DATA : WERR_FILE_NOT_FOUND,
+ "QueryMultipleValues2 failed");
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_FILE_NOT_FOUND)) {
+ return true;
+ }
+
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ *r.in.offered = *r.out.needed;
+ r.in.buffer = r.out.buffer = talloc_zero_array(tctx, uint8_t, *r.in.offered);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r),
+ "QueryMultipleValues2 failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "QueryMultipleValues2 failed");
+
+ return true;
+}
+
+static bool test_QueryMultipleValues2(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *valuename)
+{
+ struct winreg_QueryMultipleValues2 r;
+ uint32_t offered = 0, needed;
+
+ ZERO_STRUCT(r);
+
+ r.in.key_handle = handle;
+ r.in.values_in = r.out.values_out = talloc_zero_array(tctx, struct QueryMultipleValue, 1);
+ r.in.values_in[0].ve_valuename = talloc(tctx, struct winreg_ValNameBuf);
+ r.in.values_in[0].ve_valuename->name = valuename;
+ /* size needs to be set manually for winreg_ValNameBuf */
+ r.in.values_in[0].ve_valuename->size = strlen_m_term(valuename)*2;
+
+ r.in.num_values = 1;
+ r.in.offered = &offered;
+ r.out.needed = &needed;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r),
+ "QueryMultipleValues2 failed");
+ if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) {
+ *r.in.offered = *r.out.needed;
+ r.in.buffer = r.out.buffer = talloc_zero_array(tctx, uint8_t, *r.in.offered);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_QueryMultipleValues2_r(b, tctx, &r),
+ "QueryMultipleValues2 failed");
+ }
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "QueryMultipleValues2 failed");
+
+ return true;
+}
+
+static bool test_QueryValue(struct dcerpc_binding_handle *b,
+ 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;
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.in.data = NULL;
+ r.in.value_name = talloc_zero(tctx, struct winreg_String);
+ r.in.value_name->name = valuename;
+ r.in.type = &zero_type;
+ r.in.data_size = &offered;
+ r.in.data_length = &zero;
+
+ status = dcerpc_winreg_QueryValue_r(b, 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_QueryValue_full(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle,
+ const char *valuename,
+ bool existing_value)
+{
+ struct winreg_QueryValue r;
+ struct winreg_String value_name;
+ enum winreg_Type type = REG_NONE;
+ uint32_t data_size = 0;
+ uint32_t real_data_size = 0;
+ uint32_t data_length = 0;
+ uint8_t *data = NULL;
+ WERROR expected_error = WERR_FILE_NOT_FOUND;
+ const char *errmsg_nonexisting = "expected WERR_FILE_NOT_FOUND for nonexisting value";
+
+ if (valuename == NULL) {
+ expected_error = WERR_INVALID_PARAMETER;
+ errmsg_nonexisting = "expected WERR_INVALID_PARAMETER for NULL valuename";
+ }
+
+ ZERO_STRUCT(r);
+
+ init_winreg_String(&value_name, NULL);
+
+ torture_comment(tctx, "Testing QueryValue(%s)\n", valuename);
+
+ r.in.handle = handle;
+ r.in.value_name = &value_name;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r), "QueryValue failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "expected WERR_INVALID_PARAMETER for NULL winreg_String.name");
+
+ init_winreg_String(&value_name, valuename);
+ r.in.value_name = &value_name;
+
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r),
+ "QueryValue failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "expected WERR_INVALID_PARAMETER for missing type length and size");
+
+ r.in.type = &type;
+ r.out.type = &type;
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r),
+ "QueryValue failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "expected WERR_INVALID_PARAMETER for missing length and size");
+
+ r.in.data_length = &data_length;
+ r.out.data_length = &data_length;
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r),
+ "QueryValue failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_PARAMETER,
+ "expected WERR_INVALID_PARAMETER for missing size");
+
+ r.in.data_size = &data_size;
+ r.out.data_size = &data_size;
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r),
+ "QueryValue failed");
+ if (existing_value) {
+ torture_assert_werr_ok(tctx, r.out.result,
+ "QueryValue failed");
+ } else {
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ errmsg_nonexisting);
+ }
+
+ real_data_size = *r.out.data_size;
+
+ data = talloc_zero_array(tctx, uint8_t, 0);
+ r.in.data = data;
+ r.out.data = data;
+ *r.in.data_size = 0;
+ *r.out.data_size = 0;
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r),
+ "QueryValue failed");
+ if (existing_value) {
+ torture_assert_werr_equal(tctx, r.out.result, WERR_MORE_DATA,
+ "expected WERR_MORE_DATA for query with too small buffer");
+ } else {
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ errmsg_nonexisting);
+ }
+
+ data = talloc_zero_array(tctx, uint8_t, real_data_size);
+ r.in.data = data;
+ r.out.data = data;
+ r.in.data_size = &real_data_size;
+ r.out.data_size = &real_data_size;
+ torture_assert_ntstatus_ok(tctx, dcerpc_winreg_QueryValue_r(b, tctx, &r),
+ "QueryValue failed");
+ if (existing_value) {
+ torture_assert_werr_ok(tctx, r.out.result,
+ "QueryValue failed");
+ } else {
+ torture_assert_werr_equal(tctx, r.out.result, expected_error,
+ errmsg_nonexisting);
+ }
+
+ return true;
+}
+
+static bool test_EnumValue(struct dcerpc_binding_handle *b,
+ 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 *data = NULL;
+ struct winreg_ValNameBuf name;
+ char n = '\0';
+
+ ZERO_STRUCT(r);
+ r.in.handle = handle;
+ r.in.enum_index = 0;
+ r.in.name = &name;
+ r.out.name = &name;
+ r.in.type = &type;
+ r.in.length = &zero;
+ r.in.size = &size;
+
+ do {
+ name.name = &n;
+ name.size = max_valnamelen + 2;
+ name.length = 0;
+
+ data = NULL;
+ if (size) {
+ data = talloc_array(tctx, uint8_t, size);
+ }
+ r.in.value = data;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_EnumValue_r(b, tctx, &r),
+ "EnumValue failed");
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+ ret &= test_QueryValue(b, tctx, handle,
+ r.out.name->name);
+ ret &= test_QueryMultipleValues(b, tctx, handle,
+ r.out.name->name);
+ ret &= test_QueryMultipleValues2(b, tctx, handle,
+ r.out.name->name);
+ }
+
+ talloc_free(data);
+
+ 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_binding_handle *b,
+ struct torture_context *tctx)
+{
+ struct winreg_AbortSystemShutdown r;
+ uint16_t server = 0x0;
+
+ ZERO_STRUCT(r);
+ r.in.server = &server;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_AbortSystemShutdown_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(r);
+ 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.do_reboot = 1;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_InitiateSystemShutdown_r(b, tctx, &r),
+ "InitiateSystemShutdown failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "InitiateSystemShutdown failed");
+
+ return test_AbortSystemShutdown(b, tctx);
+}
+
+
+static bool test_InitiateSystemShutdownEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ struct winreg_InitiateSystemShutdownEx r;
+ uint16_t hostname = 0x0;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ ZERO_STRUCT(r);
+ 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.do_reboot = 1;
+ r.in.reason = 0;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_InitiateSystemShutdownEx_r(b, tctx, &r),
+ "InitiateSystemShutdownEx failed");
+
+ torture_assert_werr_ok(tctx, r.out.result,
+ "InitiateSystemShutdownEx failed");
+
+ return test_AbortSystemShutdown(b, 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)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ uint32_t max_valnamelen = 0;
+ uint32_t max_valbufsize = 0;
+
+ if (depth == MAX_DEPTH)
+ return true;
+
+ if (!test_QueryInfoKey(b, tctx, handle, NULL,
+ &max_valnamelen, &max_valbufsize)) {
+ }
+
+ if (!test_NotifyChangeKeyValue(b, tctx, handle)) {
+ }
+
+ if (test_security && !test_GetKeySecurity(p, tctx, handle, NULL)) {
+ }
+
+ if (!test_EnumKey(p, tctx, handle, depth, test_security)) {
+ }
+
+ if (!test_EnumValue(b, tctx, handle, max_valnamelen, max_valbufsize)) {
+ }
+
+ if (!test_EnumValue(b, tctx, handle, max_valnamelen, 0xFFFF)) {
+ }
+
+ test_CloseKey(b, tctx, handle);
+
+ return true;
+}
+
+static bool test_SetValue_simple(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ const char *value_name = TEST_VALUE;
+ uint32_t value = 0x12345678;
+ uint64_t value2 = 0x12345678;
+ const char *string = "torture";
+ const char *array[2];
+ DATA_BLOB blob;
+ enum winreg_Type types[] = {
+ REG_DWORD,
+ REG_DWORD_BIG_ENDIAN,
+ REG_QWORD,
+ REG_BINARY,
+ REG_SZ,
+ REG_MULTI_SZ
+ };
+ int t;
+
+ array[0] = "array0";
+ array[1] = NULL;
+
+ torture_comment(tctx, "Testing SetValue (standard formats)\n");
+
+ for (t=0; t < ARRAY_SIZE(types); t++) {
+
+ enum winreg_Type w_type;
+ uint32_t w_size, w_length;
+ uint8_t *w_data;
+
+ switch (types[t]) {
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ blob = data_blob_talloc_zero(tctx, 4);
+ SIVAL(blob.data, 0, value);
+ break;
+ case REG_QWORD:
+ blob = data_blob_talloc_zero(tctx, 8);
+ SBVAL(blob.data, 0, value2);
+ break;
+ case REG_BINARY:
+ blob = data_blob_string_const("binary_blob");
+ break;
+ case REG_SZ:
+ torture_assert(tctx, push_reg_sz(tctx, &blob, string), "failed to push REG_SZ");
+ break;
+ case REG_MULTI_SZ:
+ torture_assert(tctx, push_reg_multi_sz(tctx, &blob, array), "failed to push REG_MULTI_SZ");
+ break;
+ default:
+ break;
+ }
+
+ torture_assert(tctx,
+ test_SetValue(b, tctx, handle, value_name, types[t], blob.data, blob.length),
+ "test_SetValue failed");
+ torture_assert(tctx,
+ test_QueryValue_full(b, tctx, handle, value_name, true),
+ talloc_asprintf(tctx, "test_QueryValue_full for %s value failed", value_name));
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, handle, value_name, &w_type, &w_size, &w_length, &w_data),
+ "test_winreg_QueryValue failed");
+ torture_assert(tctx,
+ test_DeleteValue(b, tctx, handle, value_name),
+ "test_DeleteValue failed");
+
+ torture_assert_int_equal(tctx, w_type, types[t], "winreg type mismatch");
+ torture_assert_int_equal(tctx, w_size, blob.length, "winreg size mismatch");
+ torture_assert_int_equal(tctx, w_length, blob.length, "winreg length mismatch");
+ torture_assert_mem_equal(tctx, w_data, blob.data, blob.length, "winreg buffer mismatch");
+ }
+
+ torture_comment(tctx, "Testing SetValue (standard formats) succeeded\n");
+
+ return true;
+}
+
+static bool test_SetValue_values(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ DATA_BLOB blob;
+ const char *values[] = {
+ "torture_value",
+ "torture value",
+ "torture,value",
+ "torture;value",
+ "torture/value",
+ "torture\\value",
+ "torture_value_name",
+ "torture value name",
+ "torture,value,name",
+ "torture;value;name",
+ "torture/value/name",
+ "torture\\value\\name",
+ };
+ int i;
+
+ torture_comment(tctx, "Testing SetValue (values)\n");
+
+ for (i=0; i < ARRAY_SIZE(values); i++) {
+
+ enum winreg_Type w_type;
+ uint32_t w_size, w_length;
+ uint8_t *w_data;
+
+ blob = data_blob_talloc(tctx, NULL, 32);
+
+ generate_random_buffer(blob.data, 32);
+
+ torture_assert(tctx,
+ test_SetValue(b, tctx, handle, values[i], REG_BINARY, blob.data, blob.length),
+ "test_SetValue failed");
+ torture_assert(tctx,
+ test_QueryValue_full(b, tctx, handle, values[i], true),
+ talloc_asprintf(tctx, "test_QueryValue_full for %s value failed", values[i]));
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, handle, values[i], &w_type, &w_size, &w_length, &w_data),
+ "test_winreg_QueryValue failed");
+ torture_assert(tctx,
+ test_DeleteValue(b, tctx, handle, values[i]),
+ "test_DeleteValue failed");
+
+ torture_assert_int_equal(tctx, w_type, REG_BINARY, "winreg type mismatch");
+ torture_assert_int_equal(tctx, w_size, blob.length, "winreg size mismatch");
+ torture_assert_int_equal(tctx, w_length, blob.length, "winreg length mismatch");
+ torture_assert_mem_equal(tctx, w_data, blob.data, blob.length, "winreg buffer mismatch");
+ }
+
+ torture_comment(tctx, "Testing SetValue (values) succeeded\n");
+
+ return true;
+}
+
+typedef NTSTATUS (*winreg_open_fn)(struct dcerpc_binding_handle *, TALLOC_CTX *, void *);
+
+static bool test_SetValue_extended(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ const char *value_name = TEST_VALUE;
+ enum winreg_Type types[] = {
+ REG_NONE,
+ REG_SZ,
+ REG_EXPAND_SZ,
+ REG_BINARY,
+ REG_DWORD,
+ REG_DWORD_BIG_ENDIAN,
+ REG_LINK,
+ REG_MULTI_SZ,
+ REG_RESOURCE_LIST,
+ REG_FULL_RESOURCE_DESCRIPTOR,
+ REG_RESOURCE_REQUIREMENTS_LIST,
+ REG_QWORD,
+ 12,
+ 13,
+ 14,
+ 55,
+ 123456,
+ 653210,
+ __LINE__
+ };
+ int t, l;
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_skip(tctx, "skipping extended SetValue test against Samba4");
+ }
+
+ torture_comment(tctx, "Testing SetValue (extended formats)\n");
+
+ for (t=0; t < ARRAY_SIZE(types); t++) {
+ for (l=0; l < 16; l++) {
+
+ enum winreg_Type w_type;
+ uint32_t w_size, w_length;
+ uint8_t *w_data;
+
+ uint32_t size;
+ uint8_t *data;
+
+ size = l;
+ data = talloc_array(tctx, uint8_t, size);
+
+ generate_random_buffer(data, size);
+
+ torture_assert(tctx,
+ test_SetValue(b, tctx, handle, value_name, types[t], data, size),
+ "test_SetValue failed");
+
+ torture_assert(tctx,
+ test_winreg_QueryValue(tctx, b, handle, value_name, &w_type, &w_size, &w_length, &w_data),
+ "test_winreg_QueryValue failed");
+
+ torture_assert(tctx,
+ test_DeleteValue(b, tctx, handle, value_name),
+ "test_DeleteValue failed");
+
+ torture_assert_int_equal(tctx, w_type, types[t], "winreg type mismatch");
+ torture_assert_int_equal(tctx, w_size, size, "winreg size mismatch");
+ torture_assert_int_equal(tctx, w_length, size, "winreg length mismatch");
+ torture_assert_mem_equal(tctx, w_data, data, size, "winreg buffer mismatch");
+ }
+ }
+
+ torture_comment(tctx, "Testing SetValue (extended formats) succeeded\n");
+
+ return true;
+}
+
+static bool test_create_keynames(struct dcerpc_binding_handle *b,
+ struct torture_context *tctx,
+ struct policy_handle *handle)
+{
+ const char *keys[] = {
+ "torture_key",
+ "torture key",
+ "torture,key",
+ "torture/key",
+ "torture\\key",
+ };
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(keys); i++) {
+
+ enum winreg_CreateAction action_taken;
+ struct policy_handle new_handle;
+ char *q, *tmp;
+
+ torture_assert(tctx,
+ test_CreateKey_opts(tctx, b, handle, keys[i], NULL,
+ REG_OPTION_NON_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ NULL,
+ WERR_OK,
+ &action_taken,
+ &new_handle),
+ talloc_asprintf(tctx, "failed to create '%s' key", keys[i]));
+
+ torture_assert_int_equal(tctx, action_taken, REG_CREATED_NEW_KEY, "unexpected action");
+
+ torture_assert(tctx,
+ test_DeleteKey_opts(b, tctx, handle, keys[i], WERR_OK),
+ "failed to delete key");
+
+ torture_assert(tctx,
+ test_DeleteKey_opts(b, tctx, handle, keys[i], WERR_FILE_NOT_FOUND),
+ "failed 2nd delete key");
+
+ tmp = talloc_strdup(tctx, keys[i]);
+
+ q = strchr(tmp, '\\');
+ if (q != NULL) {
+ *q = '\0';
+ q++;
+
+ torture_assert(tctx,
+ test_DeleteKey_opts(b, tctx, handle, tmp, WERR_OK),
+ "failed to delete key");
+
+ torture_assert(tctx,
+ test_DeleteKey_opts(b, tctx, handle, tmp, WERR_FILE_NOT_FOUND),
+ "failed 2nd delete key");
+ }
+ }
+
+ return true;
+}
+
+#define KEY_CURRENT_VERSION "SOFTWARE\\MICROSOFT\\WINDOWS NT\\CURRENTVERSION"
+#define VALUE_CURRENT_VERSION "CurrentVersion"
+#define VALUE_SYSTEM_ROOT "SystemRoot"
+
+static const struct {
+ const char *values[3];
+ uint32_t num_values;
+ bool existing_value;
+ const char *error_message;
+} multiple_values_tests[] = {
+ {
+ .values = { VALUE_CURRENT_VERSION, NULL, NULL },
+ .num_values = 1,
+ .existing_value = true,
+ .error_message = NULL
+ },{
+ .values = { VALUE_SYSTEM_ROOT, NULL, NULL },
+ .num_values = 1,
+ .existing_value = true,
+ .error_message = NULL
+ },{
+ .values = { VALUE_CURRENT_VERSION, VALUE_SYSTEM_ROOT, NULL },
+ .num_values = 2,
+ .existing_value = true,
+ .error_message = NULL
+ },{
+ .values = { VALUE_CURRENT_VERSION, VALUE_SYSTEM_ROOT,
+ VALUE_CURRENT_VERSION },
+ .num_values = 3,
+ .existing_value = true,
+ .error_message = NULL
+ },{
+ .values = { VALUE_CURRENT_VERSION, NULL, VALUE_SYSTEM_ROOT },
+ .num_values = 3,
+ .existing_value = false,
+ .error_message = NULL
+ },{
+ .values = { VALUE_CURRENT_VERSION, "", VALUE_SYSTEM_ROOT },
+ .num_values = 3,
+ .existing_value = false,
+ .error_message = NULL
+ },{
+ .values = { "IDoNotExist", NULL, NULL },
+ .num_values = 1,
+ .existing_value = false,
+ .error_message = NULL
+ },{
+ .values = { "IDoNotExist", VALUE_CURRENT_VERSION, NULL },
+ .num_values = 2,
+ .existing_value = false,
+ .error_message = NULL
+ },{
+ .values = { VALUE_CURRENT_VERSION, "IDoNotExist", NULL },
+ .num_values = 2,
+ .existing_value = false,
+ .error_message = NULL
+ }
+};
+
+static bool test_HKLM_wellknown(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle)
+{
+ struct policy_handle newhandle;
+ int i;
+
+ /* FIXME: s3 does not support SEC_FLAG_MAXIMUM_ALLOWED yet */
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_assert(tctx, test_OpenKey_opts(tctx, b, handle,
+ KEY_CURRENT_VERSION,
+ REG_OPTION_NON_VOLATILE,
+ KEY_QUERY_VALUE,
+ &newhandle,
+ WERR_OK),
+ "failed to open current version key");
+ } else {
+ torture_assert(tctx, test_OpenKey(b, tctx, handle, KEY_CURRENT_VERSION, &newhandle),
+ "failed to open current version key");
+ }
+
+ torture_assert(tctx, test_QueryValue_full(b, tctx, &newhandle, VALUE_CURRENT_VERSION, true),
+ "failed to query current version");
+ torture_assert(tctx, test_QueryValue_full(b, tctx, &newhandle, "IDoNotExist", false),
+ "succeeded to query nonexistent value");
+ torture_assert(tctx, test_QueryValue_full(b, tctx, &newhandle, NULL, false),
+ "succeeded to query value with NULL name");
+ torture_assert(tctx, test_QueryValue_full(b, tctx, &newhandle, "", false),
+ "succeeded to query nonexistent default value (\"\")");
+
+ if (torture_setting_bool(tctx, "samba4", false)) {
+ torture_comment(tctx, "skipping QueryMultipleValues{2} tests against Samba4\n");
+ goto close_key;
+ }
+
+ for (i=0; i < ARRAY_SIZE(multiple_values_tests); i++) {
+ const char *msg;
+ msg = talloc_asprintf(tctx,
+ "failed to query %d %sexisting values\n",
+ multiple_values_tests[i].num_values,
+ multiple_values_tests[i].existing_value ? "":"non");
+
+ torture_assert(tctx,
+ test_QueryMultipleValues_full(b, tctx, &newhandle,
+ multiple_values_tests[i].num_values,
+ multiple_values_tests[i].values,
+ multiple_values_tests[i].existing_value),
+ msg);
+ torture_assert(tctx,
+ test_QueryMultipleValues2_full(b, tctx, &newhandle,
+ multiple_values_tests[i].num_values,
+ multiple_values_tests[i].values,
+ multiple_values_tests[i].existing_value),
+ msg);
+ }
+
+ close_key:
+ torture_assert(tctx, test_CloseKey(b, tctx, &newhandle),
+ "failed to close current version key");
+
+ return true;
+}
+
+static bool test_OpenHive(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ int hkey)
+{
+ struct winreg_OpenHKLM r;
+
+ r.in.system_name = 0;
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+ r.out.handle = handle;
+
+ switch (hkey) {
+ case HKEY_LOCAL_MACHINE:
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_OpenHKLM_r(b, tctx, &r),
+ "failed to open HKLM");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to open HKLM");
+ break;
+ case HKEY_CURRENT_USER:
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_OpenHKCU_r(b, tctx, (struct winreg_OpenHKCU *)(void *)&r),
+ "failed to open HKCU");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to open HKCU");
+ break;
+ case HKEY_USERS:
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_OpenHKU_r(b, tctx, (struct winreg_OpenHKU *)(void *)&r),
+ "failed to open HKU");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to open HKU");
+ break;
+ case HKEY_CLASSES_ROOT:
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_winreg_OpenHKCR_r(b, tctx, (struct winreg_OpenHKCR *)(void *)&r),
+ "failed to open HKCR");
+ torture_assert_werr_ok(tctx, r.out.result,
+ "failed to open HKCR");
+ break;
+ default:
+ torture_warning(tctx, "unsupported hkey: 0x%08x\n", hkey);
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_volatile_keys(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ int hkey)
+{
+ struct policy_handle new_handle, hive_handle;
+ enum winreg_CreateAction action_taken = REG_ACTION_NONE;
+
+ ZERO_STRUCT(new_handle);
+ ZERO_STRUCT(hive_handle);
+
+ torture_comment(tctx, "Testing VOLATILE key\n");
+
+ test_DeleteKey(b, tctx, handle, TEST_KEY_VOLATILE);
+
+ torture_assert(tctx,
+ test_CreateKey_opts(tctx, b, handle, TEST_KEY_VOLATILE, NULL,
+ REG_OPTION_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ NULL,
+ WERR_OK,
+ &action_taken,
+ &new_handle),
+ "failed to create REG_OPTION_VOLATILE type key");
+
+ torture_assert_int_equal(tctx, action_taken, REG_CREATED_NEW_KEY, "unexpected action");
+
+ torture_assert(tctx,
+ test_CreateKey_opts(tctx, b, &new_handle, TEST_SUBKEY_VOLATILE, NULL,
+ REG_OPTION_NON_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ NULL,
+ WERR_CHILD_MUST_BE_VOLATILE,
+ NULL,
+ NULL),
+ "failed to fail create REG_OPTION_VOLATILE type key");
+
+ torture_assert(tctx,
+ test_CloseKey(b, tctx, &new_handle),
+ "failed to close");
+
+ torture_assert(tctx,
+ test_OpenKey_opts(tctx, b, handle, TEST_KEY_VOLATILE,
+ REG_OPTION_NON_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &new_handle,
+ WERR_OK),
+ "failed to open volatile key");
+
+ torture_assert(tctx,
+ test_DeleteKey(b, tctx, handle, TEST_KEY_VOLATILE),
+ "failed to delete key");
+
+ torture_assert(tctx,
+ test_CreateKey_opts(tctx, b, handle, TEST_KEY_VOLATILE, NULL,
+ REG_OPTION_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ NULL,
+ WERR_OK,
+ &action_taken,
+ &new_handle),
+ "failed to create REG_OPTION_VOLATILE type key");
+
+ torture_assert_int_equal(tctx, action_taken, REG_CREATED_NEW_KEY, "unexpected action");
+
+ torture_assert(tctx,
+ test_CloseKey(b, tctx, &new_handle),
+ "failed to close");
+
+ torture_assert(tctx,
+ test_OpenKey_opts(tctx, b, handle, TEST_KEY_VOLATILE,
+ REG_OPTION_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &new_handle,
+ WERR_OK),
+ "failed to open volatile key");
+
+ torture_assert(tctx,
+ test_CloseKey(b, tctx, &new_handle),
+ "failed to close");
+
+ torture_assert(tctx,
+ test_OpenHive(tctx, b, &hive_handle, hkey),
+ "failed top open hive");
+
+ torture_assert(tctx,
+ test_OpenKey_opts(tctx, b, &hive_handle, TEST_KEY_VOLATILE,
+ REG_OPTION_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &new_handle,
+ WERR_FILE_NOT_FOUND),
+ "failed to open volatile key");
+
+ torture_assert(tctx,
+ test_OpenKey_opts(tctx, b, &hive_handle, TEST_KEY_VOLATILE,
+ REG_OPTION_NON_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &new_handle,
+ WERR_FILE_NOT_FOUND),
+ "failed to open volatile key");
+
+ torture_assert(tctx,
+ test_CloseKey(b, tctx, &hive_handle),
+ "failed to close");
+
+ torture_assert(tctx,
+ test_DeleteKey(b, tctx, handle, TEST_KEY_VOLATILE),
+ "failed to delete key");
+
+
+ torture_comment(tctx, "Testing VOLATILE key succeeded\n");
+
+ return true;
+}
+
+static const char *kernel_mode_registry_path(struct torture_context *tctx,
+ int hkey,
+ const char *sid_string,
+ const char *path)
+{
+ switch (hkey) {
+ case HKEY_LOCAL_MACHINE:
+ return talloc_asprintf(tctx, "\\Registry\\MACHINE\\%s", path);
+ case HKEY_CURRENT_USER:
+ return talloc_asprintf(tctx, "\\Registry\\USER\\%s\\%s", sid_string, path);
+ case HKEY_USERS:
+ return talloc_asprintf(tctx, "\\Registry\\USER\\%s", path);
+ case HKEY_CLASSES_ROOT:
+ return talloc_asprintf(tctx, "\\Registry\\MACHINE\\Software\\Classes\\%s", path);
+ default:
+ torture_warning(tctx, "unsupported hkey: 0x%08x\n", hkey);
+ return NULL;
+ }
+}
+
+static bool test_symlink_keys(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key,
+ int hkey)
+{
+ struct policy_handle new_handle;
+ enum winreg_CreateAction action_taken;
+ DATA_BLOB blob;
+ uint32_t value = 42;
+ const char *test_key_symlink_dest;
+ const char *test_key_symlink;
+ const char *kernel_mode_path;
+
+ /* disable until we know how to delete a symbolic link */
+ torture_skip(tctx, "symlink test disabled");
+
+ torture_comment(tctx, "Testing REG_OPTION_CREATE_LINK key\n");
+
+ /* create destination key with testvalue */
+ test_key_symlink = talloc_asprintf(tctx, "%s\\%s",
+ key, TEST_KEY_SYMLINK);
+ test_key_symlink_dest = talloc_asprintf(tctx, "%s\\%s",
+ key, TEST_KEY_SYMLINK_DEST);
+
+ test_DeleteKey(b, tctx, handle, test_key_symlink);
+
+ torture_assert(tctx,
+ test_CreateKey_opts(tctx, b, handle, test_key_symlink_dest, NULL,
+ 0,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ NULL,
+ WERR_OK,
+ &action_taken,
+ &new_handle),
+ "failed to create symlink destination");
+
+ blob = data_blob_talloc_zero(tctx, 4);
+ SIVAL(blob.data, 0, value);
+
+ torture_assert(tctx,
+ test_SetValue(b, tctx, &new_handle, "TestValue", REG_DWORD, blob.data, blob.length),
+ "failed to create TestValue");
+
+ torture_assert(tctx,
+ test_CloseKey(b, tctx, &new_handle),
+ "failed to close");
+
+ /* create symlink */
+
+ torture_assert(tctx,
+ test_CreateKey_opts(tctx, b, handle, test_key_symlink, NULL,
+ REG_OPTION_CREATE_LINK | REG_OPTION_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ NULL,
+ WERR_OK,
+ &action_taken,
+ &new_handle),
+ "failed to create REG_OPTION_CREATE_LINK type key");
+
+ torture_assert_int_equal(tctx, action_taken, REG_CREATED_NEW_KEY, "unexpected action");
+
+ kernel_mode_path = kernel_mode_registry_path(tctx, hkey, NULL, test_key_symlink_dest);
+
+ torture_assert(tctx,
+ convert_string_talloc(tctx, CH_UNIX, CH_UTF16,
+ kernel_mode_path,
+ strlen(kernel_mode_path), /* not NULL terminated */
+ &blob.data, &blob.length),
+ "failed to convert");
+
+ torture_assert(tctx,
+ test_SetValue(b, tctx, &new_handle, "SymbolicLinkValue", REG_LINK, blob.data, blob.length),
+ "failed to create SymbolicLinkValue value");
+
+ torture_assert(tctx,
+ test_CloseKey(b, tctx, &new_handle),
+ "failed to close");
+
+ /* test follow symlink */
+
+ torture_assert(tctx,
+ test_OpenKey_opts(tctx, b, handle, test_key_symlink,
+ 0,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &new_handle,
+ WERR_OK),
+ "failed to follow symlink key");
+
+ torture_assert(tctx,
+ test_QueryValue(b, tctx, &new_handle, "TestValue"),
+ "failed to query value");
+
+ torture_assert(tctx,
+ test_CloseKey(b, tctx, &new_handle),
+ "failed to close");
+
+ /* delete link */
+
+ torture_assert(tctx,
+ test_OpenKey_opts(tctx, b, handle, test_key_symlink,
+ REG_OPTION_OPEN_LINK | REG_OPTION_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &new_handle,
+ WERR_OK),
+ "failed to open symlink key");
+
+ torture_assert(tctx,
+ test_DeleteValue(b, tctx, &new_handle, "SymbolicLinkValue"),
+ "failed to delete value SymbolicLinkValue");
+
+ torture_assert(tctx,
+ test_CloseKey(b, tctx, &new_handle),
+ "failed to close");
+
+ torture_assert(tctx,
+ test_DeleteKey(b, tctx, handle, test_key_symlink),
+ "failed to delete key");
+
+ /* delete destination */
+
+ torture_assert(tctx,
+ test_DeleteKey(b, tctx, handle, test_key_symlink_dest),
+ "failed to delete key");
+
+ return true;
+}
+
+static bool test_CreateKey_keytypes(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *key,
+ int hkey)
+{
+
+ if (torture_setting_bool(tctx, "samba3", false) ||
+ torture_setting_bool(tctx, "samba4", false)) {
+ torture_skip(tctx, "skipping CreateKey keytypes test against Samba");
+ }
+
+ torture_assert(tctx,
+ test_volatile_keys(tctx, b, handle, hkey),
+ "failed to test volatile keys");
+
+ torture_assert(tctx,
+ test_symlink_keys(tctx, b, handle, key, hkey),
+ "failed to test symlink keys");
+
+ return true;
+}
+
+static bool test_key_base(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *handle,
+ const char *base_key,
+ int hkey)
+{
+ struct policy_handle newhandle;
+ bool ret = true, created = false, deleted = false;
+ bool created3 = false;
+ const char *test_key1;
+ const char *test_key3;
+ const char *test_subkey;
+
+ test_Cleanup(b, tctx, handle, base_key);
+
+ if (!test_CreateKey(b, tctx, handle, base_key, NULL)) {
+ torture_comment(tctx,
+ "CreateKey(%s) failed\n", base_key);
+ }
+
+ test_key1 = talloc_asprintf(tctx, "%s\\%s", base_key, TEST_KEY1);
+
+ if (!test_CreateKey(b, tctx, handle, test_key1, NULL)) {
+ torture_comment(tctx,
+ "CreateKey failed - not considering a failure\n");
+ } else {
+ created = true;
+ }
+
+ if (created) {
+ if (!test_FlushKey(b, tctx, handle)) {
+ torture_comment(tctx, "FlushKey failed\n");
+ ret = false;
+ }
+
+ if (!test_OpenKey(b, tctx, handle, test_key1, &newhandle)) {
+ torture_fail(tctx,
+ "CreateKey failed (OpenKey after Create didn't work)\n");
+ }
+
+ if (hkey == HKEY_CURRENT_USER) {
+ torture_assert(tctx, test_SetValue_simple(b, tctx, &newhandle),
+ "simple SetValue test failed");
+ torture_assert(tctx, test_SetValue_values(b, tctx, &newhandle),
+ "values SetValue test failed");
+ torture_assert(tctx, test_SetValue_extended(b, tctx, &newhandle),
+ "extended SetValue test failed");
+ torture_assert(tctx, test_create_keynames(b, tctx, &newhandle),
+ "keyname CreateKey test failed");
+ } else {
+ torture_assert(tctx, test_CreateKey_keytypes(tctx, b, &newhandle, test_key1, hkey),
+ "keytype test failed");
+ }
+
+ if (!test_CloseKey(b, tctx, &newhandle)) {
+ torture_fail(tctx,
+ "CreateKey failed (CloseKey after Open didn't work)\n");
+ }
+
+ if (!test_DeleteKey(b, tctx, handle, test_key1)) {
+ torture_comment(tctx, "DeleteKey(%s) failed\n",
+ test_key1);
+ ret = false;
+ } else {
+ deleted = true;
+ }
+
+ if (!test_FlushKey(b, tctx, handle)) {
+ torture_comment(tctx, "FlushKey failed\n");
+ ret = false;
+ }
+
+ if (deleted) {
+ if (!test_OpenKey_opts(tctx, b, handle, test_key1,
+ REG_OPTION_NON_VOLATILE,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &newhandle,
+ WERR_FILE_NOT_FOUND)) {
+ torture_comment(tctx,
+ "DeleteKey failed (OpenKey after Delete "
+ "did not return WERR_FILE_NOT_FOUND)\n");
+ ret = false;
+ }
+ }
+
+ test_key3 = talloc_asprintf(tctx, "%s\\%s", base_key, TEST_KEY3);
+
+ if (test_CreateKey(b, tctx, handle, test_key3, NULL)) {
+ created3 = true;
+ }
+
+ test_subkey = talloc_asprintf(tctx, "%s\\%s", test_key3, TEST_SUBKEY);
+
+ if (created3) {
+ if (test_CreateKey(b, tctx, handle, test_subkey, NULL)) {
+ if (!test_DeleteKey(b, tctx, handle, test_subkey)) {
+ torture_comment(tctx, "DeleteKey(%s) failed\n", test_subkey);
+ ret = false;
+ }
+ }
+
+ if (!test_DeleteKey(b, tctx, handle, test_key3)) {
+ torture_comment(tctx, "DeleteKey(%s) failed\n", test_key3);
+ ret = false;
+ }
+ }
+ }
+
+ test_Cleanup(b, tctx, handle, base_key);
+
+ return ret;
+}
+
+static bool test_key_base_sd(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *handle,
+ const char *base_key)
+{
+ struct policy_handle newhandle;
+ bool ret = true, created2 = false, created4 = false;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *test_key2;
+ const char *test_key4;
+
+ torture_skip(tctx, "security descriptor test disabled\n");
+
+ if (torture_setting_bool(tctx, "samba3", false) ||
+ torture_setting_bool(tctx, "samba4", false)) {
+ torture_skip(tctx, "skipping security descriptor tests against Samba");
+ }
+
+ test_Cleanup(b, tctx, handle, base_key);
+
+ if (!test_CreateKey(b, tctx, handle, base_key, NULL)) {
+ torture_comment(tctx,
+ "CreateKey(%s) failed\n", base_key);
+ }
+
+ test_key2 = talloc_asprintf(tctx, "%s\\%s", base_key, TEST_KEY2);
+
+ if (test_CreateKey_sd(b, tctx, handle, test_key2,
+ NULL, &newhandle)) {
+ created2 = true;
+ }
+
+ if (created2 && !test_CloseKey(b, tctx, &newhandle)) {
+ torture_comment(tctx, "CloseKey failed\n");
+ ret = false;
+ }
+
+ test_key4 = talloc_asprintf(tctx, "%s\\%s", base_key, TEST_KEY4);
+
+ if (test_CreateKey_sd(b, tctx, handle, test_key4, NULL, &newhandle)) {
+ created4 = true;
+ }
+
+ if (created4 && !test_CloseKey(b, tctx, &newhandle)) {
+ torture_comment(tctx, "CloseKey failed\n");
+ ret = false;
+ }
+
+ if (created4 && !test_SecurityDescriptors(p, tctx, handle, test_key4)) {
+ ret = false;
+ }
+
+ if (created4 && !test_DeleteKey(b, tctx, handle, test_key4)) {
+ torture_comment(tctx, "DeleteKey(%s) failed\n", test_key4);
+ ret = false;
+ }
+
+ if (created2 && !test_DeleteKey(b, tctx, handle, test_key4)) {
+ torture_comment(tctx, "DeleteKey(%s) failed\n", test_key4);
+ ret = false;
+ }
+
+ test_Cleanup(b, tctx, handle, base_key);
+
+ return ret;
+}
+
+static bool test_Open(struct torture_context *tctx, struct dcerpc_pipe *p,
+ void *userdata)
+{
+ struct policy_handle handle;
+ bool ret = true;
+ struct winreg_OpenHKLM r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ const char *torture_base_key;
+ int hkey = 0;
+
+ winreg_open_fn open_fn = (winreg_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(b, tctx, &r),
+ "open");
+
+ if (!test_GetVersion(b, tctx, &handle)) {
+ torture_comment(tctx, "GetVersion failed\n");
+ ret = false;
+ }
+
+ if (open_fn == (winreg_open_fn)dcerpc_winreg_OpenHKLM_r) {
+ hkey = HKEY_LOCAL_MACHINE;
+ torture_base_key = "SOFTWARE\\Samba\\" TEST_KEY_BASE;
+ } else if (open_fn == (winreg_open_fn)dcerpc_winreg_OpenHKU_r) {
+ hkey = HKEY_USERS;
+ torture_base_key = TEST_KEY_BASE;
+ } else if (open_fn == (winreg_open_fn)dcerpc_winreg_OpenHKCR_r) {
+ hkey = HKEY_CLASSES_ROOT;
+ torture_base_key = TEST_KEY_BASE;
+ } else if (open_fn == (winreg_open_fn)dcerpc_winreg_OpenHKCU_r) {
+ hkey = HKEY_CURRENT_USER;
+ torture_base_key = TEST_KEY_BASE;
+ } else {
+ torture_fail(tctx, "unsupported hkey");
+ }
+
+ if (hkey == HKEY_LOCAL_MACHINE) {
+ torture_assert(tctx,
+ test_HKLM_wellknown(tctx, b, &handle),
+ "failed to test HKLM wellknown keys");
+ }
+
+ if (!test_key_base(tctx, b, &handle, torture_base_key, hkey)) {
+ torture_warning(tctx, "failed to test TEST_KEY_BASE(%s)",
+ torture_base_key);
+ ret = false;
+ }
+
+ if (!test_key_base_sd(tctx, p, &handle, torture_base_key)) {
+ torture_warning(tctx, "failed to test TEST_KEY_BASE(%s) sd",
+ torture_base_key);
+ ret = false;
+ }
+
+ /* The HKCR hive has a very large fanout */
+ if (hkey == HKEY_CLASSES_ROOT) {
+ if(!test_key(p, tctx, &handle, MAX_DEPTH - 1, false)) {
+ ret = false;
+ }
+ } else if (hkey == HKEY_LOCAL_MACHINE) {
+ /* FIXME we are not allowed to enum values in the HKLM root */
+ } else {
+ if (!test_key(p, tctx, &handle, 0, false)) {
+ ret = false;
+ }
+ }
+
+ 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;
+
+ torture_rpc_tcase_add_test_ex(tcase, "HKLM",
+ test_Open,
+ (void *)dcerpc_winreg_OpenHKLM_r);
+ torture_rpc_tcase_add_test_ex(tcase, "HKU",
+ test_Open,
+ (void *)dcerpc_winreg_OpenHKU_r);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCR",
+ test_Open,
+ (void *)dcerpc_winreg_OpenHKCR_r);
+ torture_rpc_tcase_add_test_ex(tcase, "HKCU",
+ test_Open,
+ (void *)dcerpc_winreg_OpenHKCU_r);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/witness.c b/source4/torture/rpc/witness.c
new file mode 100644
index 0000000..602e8ca
--- /dev/null
+++ b/source4/torture/rpc/witness.c
@@ -0,0 +1,911 @@
+/*
+ Unix SMB/CIFS implementation.
+ test suite for rpc witness operations
+
+ Copyright (C) Guenther Deschner 2015
+
+ 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/rpc/torture_rpc.h"
+#include "librpc/gen_ndr/ndr_witness_c.h"
+#include "librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "librpc/gen_ndr/ndr_clusapi_c.h"
+#include "param/param.h"
+#include <tevent.h>
+#include "lib/cmdline/cmdline.h"
+
+struct torture_test_clusapi_state {
+ struct dcerpc_pipe *p;
+};
+
+struct torture_test_witness_state {
+ const char *net_name;
+ const char *share_name;
+ struct witness_interfaceList *list;
+ struct policy_handle context_handle;
+ struct torture_test_clusapi_state clusapi;
+};
+
+static bool test_witness_GetInterfaceList(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ void *data)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct witness_GetInterfaceList r;
+ struct witness_interfaceList *l;
+ struct torture_test_witness_state *state =
+ (struct torture_test_witness_state *)data;
+
+ r.out.interface_list = &l;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_GetInterfaceList_r(b, tctx, &r),
+ "GetInterfaceList failed");
+
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetInterfaceList failed");
+
+ state->list = l;
+
+ return true;
+}
+
+static bool find_sofs_share(struct torture_context *tctx,
+ const char **sofs_sharename)
+{
+ struct dcerpc_pipe *p;
+ struct dcerpc_binding_handle *b;
+ struct srvsvc_NetShareEnumAll r;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr1 ctr1;
+ uint32_t resume_handle = 0;
+ uint32_t totalentries = 0;
+ int i;
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_connection_transport(tctx, &p, &ndr_table_srvsvc,
+ NCACN_NP, 0, 0),
+ "failed to setup srvsvc connection");
+
+ b = p->binding_handle;
+
+ ZERO_STRUCT(ctr1);
+
+ info_ctr.level = 1;
+ info_ctr.ctr.ctr1 = &ctr1;
+
+ r.in.server_unc = dcerpc_server_name(p);
+ r.in.max_buffer = -1;
+ r.in.info_ctr = &info_ctr;
+ r.in.resume_handle = &resume_handle;
+ r.out.totalentries = &totalentries;
+ r.out.info_ctr = &info_ctr;
+ r.out.resume_handle = &resume_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_srvsvc_NetShareEnumAll_r(b, tctx, &r),
+ "failed to call srvsvc_NetShareEnumAll");
+
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "failed to call srvsvc_NetShareEnumAll");
+
+ for (i=0; i < r.out.info_ctr->ctr.ctr1->count; i++) {
+
+ if (r.out.info_ctr->ctr.ctr1->array[i].type == STYPE_CLUSTER_SOFS) {
+ *sofs_sharename = talloc_strdup(tctx, r.out.info_ctr->ctr.ctr1->array[i].name);
+ if (*sofs_sharename == NULL) {
+ return false;
+ }
+ torture_comment(tctx, "using SOFS share: %s\n", *sofs_sharename);
+ return true;
+ }
+ if (r.out.info_ctr->ctr.ctr1->array[i].type == STYPE_DISKTREE) {
+ *sofs_sharename = talloc_strdup(tctx, r.out.info_ctr->ctr.ctr1->array[i].name);
+ if (*sofs_sharename == NULL) {
+ return false;
+ }
+ torture_comment(tctx, "assuming SOFS share: %s\n", *sofs_sharename);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool init_witness_test_state(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct torture_test_witness_state *state)
+{
+ if (state->net_name == NULL) {
+ state->net_name = lpcfg_parm_string(tctx->lp_ctx, NULL, "torture", "net_name");
+ }
+
+ if (state->list == NULL) {
+ torture_assert(tctx,
+ test_witness_GetInterfaceList(tctx, p, state),
+ "failed to retrieve GetInterfaceList");
+ }
+
+ if (state->share_name == NULL) {
+ find_sofs_share(tctx, &state->share_name);
+ }
+
+ return true;
+}
+
+static bool test_witness_UnRegister_with_handle(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *context_handle)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct witness_UnRegister r;
+
+ r.in.context_handle = *context_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_UnRegister_r(b, tctx, &r),
+ "UnRegister failed");
+
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "UnRegister failed");
+
+ /* make sure we are not able/allowed to reuse context handles after they
+ * have been unregistered */
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_UnRegister_r(b, tctx, &r),
+ "UnRegister failed");
+
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_PARAMETER,
+ "UnRegister failed");
+
+ return true;
+}
+
+static bool test_witness_UnRegister(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ void *data)
+{
+ /* acquire handle and free afterwards */
+ return true;
+}
+
+static bool get_ip_address_from_interface(struct torture_context *tctx,
+ struct witness_interfaceInfo *i,
+ const char **ip_address)
+{
+ if (i->flags & WITNESS_INFO_IPv4_VALID) {
+ *ip_address = talloc_strdup(tctx, i->ipv4);
+ torture_assert(tctx, *ip_address, "talloc_strdup failed");
+ return true;
+ }
+
+ if (i->flags & WITNESS_INFO_IPv6_VALID) {
+ *ip_address = talloc_strdup(tctx, i->ipv6);
+ torture_assert(tctx, *ip_address, "talloc_strdup failed");
+ return true;
+ }
+
+ return false;
+}
+
+static bool check_valid_interface(struct torture_context *tctx,
+ struct witness_interfaceInfo *i)
+{
+ /* continue looking for an interface that allows witness
+ * registration */
+ if (!(i->flags & WITNESS_INFO_WITNESS_IF)) {
+ return false;
+ }
+
+ /* witness should be available of course */
+ if (i->state != WITNESS_STATE_AVAILABLE) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_witness_Register(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ void *data)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct witness_Register r;
+ struct policy_handle context_handle;
+ struct torture_test_witness_state *state =
+ (struct torture_test_witness_state *)data;
+ int i;
+
+ struct {
+ enum witness_version version;
+ const char *net_name;
+ const char *ip_address;
+ const char *client_computer_name;
+ NTSTATUS expected_status;
+ WERROR expected_result;
+ } tests[] = {
+ {
+ .version = 0,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = 1,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = 123456,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = -1,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = WITNESS_V2,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = WITNESS_V1,
+ .net_name = "",
+ .ip_address = "",
+ .client_computer_name = "",
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_INVALID_PARAMETER
+ },{
+ .version = WITNESS_V1,
+ .net_name = NULL,
+ .ip_address = NULL,
+ .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx),
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_INVALID_PARAMETER
+ },{
+ .version = WITNESS_V2,
+ .net_name = NULL,
+ .ip_address = NULL,
+ .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx),
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = WITNESS_V1,
+ .net_name = dcerpc_server_name(p),
+ .ip_address = NULL,
+ .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx),
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_INVALID_PARAMETER
+ }
+
+ };
+
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+
+ ZERO_STRUCT(r);
+
+ r.out.context_handle = &context_handle;
+
+ r.in.version = tests[i].version;
+ r.in.net_name = tests[i].net_name;
+ r.in.ip_address = tests[i].ip_address;
+ r.in.client_computer_name = tests[i].client_computer_name;
+
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_witness_Register_r(b, tctx, &r),
+ tests[i].expected_status,
+ "Register failed");
+
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ tests[i].expected_result,
+ "Register failed");
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+
+ /* we have a handle, make sure to unregister it */
+ torture_assert(tctx,
+ test_witness_UnRegister_with_handle(tctx, p, r.out.context_handle),
+ "Failed to unregister");
+ }
+ }
+
+ init_witness_test_state(tctx, p, state);
+
+ for (i=0; state->list && i < state->list->num_interfaces; i++) {
+
+ const char *ip_address;
+ struct witness_interfaceInfo interface = state->list->interfaces[i];
+
+ if (!check_valid_interface(tctx, &interface)) {
+ continue;
+ }
+
+ torture_assert(tctx,
+ get_ip_address_from_interface(tctx, &interface, &ip_address),
+ "failed to get ip_address from interface");
+
+ r.in.version = WITNESS_V1;
+ r.in.net_name = state->net_name;
+ r.in.ip_address = ip_address;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_Register_r(b, tctx, &r),
+ "Register failed");
+
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "Register failed");
+
+ torture_assert(tctx,
+ test_witness_UnRegister_with_handle(tctx, p, r.out.context_handle),
+ "Failed to unregister");
+ }
+
+ return true;
+}
+
+static bool test_witness_RegisterEx(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ void *data)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct witness_RegisterEx r;
+ struct policy_handle context_handle;
+ struct torture_test_witness_state *state =
+ (struct torture_test_witness_state *)data;
+ int i;
+
+ struct {
+ enum witness_version version;
+ const char *net_name;
+ const char *ip_address;
+ const char *client_computer_name;
+ NTSTATUS expected_status;
+ WERROR expected_result;
+ } tests[] = {
+ {
+ .version = 0,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = 1,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = 123456,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = -1,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = WITNESS_V1,
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = WITNESS_V2,
+ .net_name = "",
+ .ip_address = "",
+ .client_computer_name = "",
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_INVALID_PARAMETER
+ },{
+ .version = WITNESS_V2,
+ .net_name = NULL,
+ .ip_address = NULL,
+ .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx),
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_INVALID_PARAMETER
+ },{
+ .version = WITNESS_V1,
+ .net_name = NULL,
+ .ip_address = NULL,
+ .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx),
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_REVISION_MISMATCH
+ },{
+ .version = WITNESS_V2,
+ .net_name = dcerpc_server_name(p),
+ .ip_address = NULL,
+ .client_computer_name = lpcfg_netbios_name(tctx->lp_ctx),
+ .expected_status = NT_STATUS_OK,
+ .expected_result = WERR_INVALID_PARAMETER
+ }
+
+ };
+
+ for (i=0; i < ARRAY_SIZE(tests); i++) {
+
+ ZERO_STRUCT(r);
+
+ r.out.context_handle = &context_handle;
+
+ r.in.version = tests[i].version;
+ r.in.net_name = tests[i].net_name;
+ r.in.ip_address = tests[i].ip_address;
+ r.in.client_computer_name = tests[i].client_computer_name;
+
+ torture_assert_ntstatus_equal(tctx,
+ dcerpc_witness_RegisterEx_r(b, tctx, &r),
+ tests[i].expected_status,
+ "RegisterEx failed");
+
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ tests[i].expected_result,
+ "RegisterEx failed");
+
+ if (W_ERROR_IS_OK(r.out.result)) {
+
+ /* we have a handle, make sure to unregister it */
+ torture_assert(tctx,
+ test_witness_UnRegister_with_handle(tctx, p, r.out.context_handle),
+ "Failed to unregister");
+ }
+ }
+
+ init_witness_test_state(tctx, p, state);
+
+ for (i=0; state->list && i < state->list->num_interfaces; i++) {
+
+ const char *ip_address;
+ struct witness_interfaceInfo interface = state->list->interfaces[i];
+
+ if (!check_valid_interface(tctx, &interface)) {
+ continue;
+ }
+
+ torture_assert(tctx,
+ get_ip_address_from_interface(tctx, &interface, &ip_address),
+ "failed to get ip_address from interface");
+
+ r.in.version = WITNESS_V2;
+ r.in.net_name = state->net_name;
+ r.in.ip_address = ip_address;
+
+ /*
+ * a valid request with an invalid sharename fails with
+ * WERR_INVALID_STATE
+ */
+ r.in.share_name = "any_invalid_share_name";
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_RegisterEx_r(b, tctx, &r),
+ "RegisterEx failed");
+
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_INVALID_STATE,
+ "RegisterEx failed");
+
+ r.in.share_name = NULL;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_RegisterEx_r(b, tctx, &r),
+ "RegisterEx failed");
+
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "RegisterEx failed");
+
+ torture_assert(tctx,
+ test_witness_UnRegister_with_handle(tctx, p, r.out.context_handle),
+ "Failed to unregister");
+ }
+
+ return true;
+}
+
+static bool setup_clusapi_connection(struct torture_context *tctx,
+ struct torture_test_witness_state *s)
+{
+ struct dcerpc_binding *binding;
+
+ if (s->clusapi.p) {
+ return true;
+ }
+
+ torture_assert_ntstatus_ok(tctx,
+ torture_rpc_binding(tctx, &binding),
+ "failed to retrieve torture binding");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_binding_set_transport(binding, NCACN_IP_TCP),
+ "failed to set transport");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_binding_set_flags(binding, DCERPC_SEAL, 0),
+ "failed to set dcerpc flags");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_pipe_connect_b(tctx, &s->clusapi.p, binding,
+ &ndr_table_clusapi,
+ samba_cmdline_get_creds(),
+ tctx->ev, tctx->lp_ctx),
+ "failed to connect dcerpc pipe");
+
+ return true;
+}
+
+#if 0
+static bool cluster_get_nodes(struct torture_context *tctx,
+ struct torture_test_witness_state *s)
+{
+ struct clusapi_CreateEnum r;
+ struct ENUM_LIST *ReturnEnum;
+ WERROR rpc_status;
+ struct dcerpc_binding_handle *b;
+
+ torture_assert(tctx,
+ setup_clusapi_connection(tctx, s),
+ "failed to setup clusapi connection");
+
+ b = s->clusapi.p->binding_handle;
+
+ r.in.dwType = CLUSTER_ENUM_NODE;
+ r.out.ReturnEnum = &ReturnEnum;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_CreateEnum_r(b, tctx, &r),
+ "failed to enumerate nodes");
+
+ return true;
+}
+#endif
+
+static bool test_GetResourceState_int(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ struct policy_handle *hResource,
+ enum clusapi_ClusterResourceState *State)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct clusapi_GetResourceState r;
+ const char *NodeName;
+ const char *GroupName;
+ WERROR rpc_status;
+
+ r.in.hResource = *hResource;
+ r.out.State = State;
+ r.out.NodeName = &NodeName;
+ r.out.GroupName = &GroupName;
+ r.out.rpc_status = &rpc_status;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_clusapi_GetResourceState_r(b, tctx, &r),
+ "GetResourceState failed");
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "GetResourceState failed");
+
+ return true;
+}
+
+static bool toggle_cluster_resource_state(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ const char *resource_name,
+ enum clusapi_ClusterResourceState *old_state,
+ enum clusapi_ClusterResourceState *new_state)
+{
+ struct policy_handle hResource;
+ enum clusapi_ClusterResourceState State;
+
+ torture_assert(tctx,
+ test_OpenResource_int(tctx, p, resource_name, &hResource),
+ "failed to open resource");
+ torture_assert(tctx,
+ test_GetResourceState_int(tctx, p, &hResource, &State),
+ "failed to query resource state");
+
+ if (old_state) {
+ *old_state = State;
+ }
+
+ switch (State) {
+ case ClusterResourceOffline:
+ if (!test_OnlineResource_int(tctx, p, &hResource)) {
+ test_CloseResource_int(tctx, p, &hResource);
+ torture_warning(tctx, "failed to set resource online");
+ return false;
+ }
+ break;
+ case ClusterResourceOnline:
+ if (!test_OfflineResource_int(tctx, p, &hResource)) {
+ test_CloseResource_int(tctx, p, &hResource);
+ torture_warning(tctx, "failed to set resource offline");
+ return false;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ torture_assert(tctx,
+ test_GetResourceState_int(tctx, p, &hResource, &State),
+ "failed to query resource state");
+
+ if (new_state) {
+ *new_state = State;
+ }
+
+ test_CloseResource_int(tctx, p, &hResource);
+
+ return true;
+}
+
+static bool test_witness_AsyncNotify(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ void *data)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct witness_AsyncNotify r;
+ struct witness_notifyResponse *response;
+ struct torture_test_witness_state *state =
+ (struct torture_test_witness_state *)data;
+ int i;
+
+ init_witness_test_state(tctx, p, state);
+
+ setup_clusapi_connection(tctx, state);
+
+ for (i=0; state->list && i < state->list->num_interfaces; i++) {
+
+ const char *ip_address;
+ struct witness_interfaceInfo interface = state->list->interfaces[i];
+ struct witness_Register reg;
+ struct tevent_req *req;
+ enum clusapi_ClusterResourceState old_state, new_state;
+
+ if (!check_valid_interface(tctx, &interface)) {
+ continue;
+ }
+
+ torture_assert(tctx,
+ get_ip_address_from_interface(tctx, &interface, &ip_address),
+ "failed to get ip_address from interface");
+
+ reg.in.version = WITNESS_V1;
+ reg.in.net_name = state->net_name;
+ reg.in.ip_address = ip_address;
+ reg.in.client_computer_name = lpcfg_netbios_name(tctx->lp_ctx);
+ reg.out.context_handle = &state->context_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_Register_r(b, tctx, &reg),
+ "Register failed");
+
+ torture_assert_werr_ok(tctx,
+ reg.out.result,
+ "Register failed");
+
+ r.in.context_handle = state->context_handle;
+ r.out.response = &response;
+
+ req = dcerpc_witness_AsyncNotify_r_send(tctx, tctx->ev, b, &r);
+ torture_assert(tctx, req, "failed to create request");
+
+ torture_assert(tctx,
+ toggle_cluster_resource_state(tctx, state->clusapi.p, state->net_name, &old_state, &new_state),
+ "failed to toggle cluster resource state");
+ torture_assert(tctx, old_state != new_state, "failed to change cluster resource state");
+
+ torture_assert(tctx,
+ tevent_req_poll(req, tctx->ev),
+ "failed to call event loop");
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_AsyncNotify_r_recv(req, tctx),
+ "failed to receive reply");
+
+ torture_assert_int_equal(tctx, response->num, 1, "num");
+ torture_assert_int_equal(tctx, response->type, WITNESS_NOTIFY_RESOURCE_CHANGE, "type");
+
+ /*
+ * TODO: find out how ClusterResourceOfflinePending and
+ * ClusterResourceOnlinePending are represented as witness
+ * types.
+ */
+
+ if (new_state == ClusterResourceOffline) {
+ torture_assert_int_equal(tctx, response->messages[0].resource_change.type, WITNESS_RESOURCE_STATE_UNAVAILABLE, "resource_change.type");
+ }
+ if (new_state == ClusterResourceOnline) {
+ torture_assert_int_equal(tctx, response->messages[0].resource_change.type, WITNESS_RESOURCE_STATE_AVAILABLE, "resource_change.type");
+ }
+ torture_assert(tctx,
+ test_witness_UnRegister_with_handle(tctx, p, &state->context_handle),
+ "Failed to unregister");
+
+ ZERO_STRUCT(state->context_handle);
+
+ torture_assert(tctx,
+ toggle_cluster_resource_state(tctx, state->clusapi.p, state->net_name, &old_state, &new_state),
+ "failed to toggle cluster resource state");
+ torture_assert(tctx, old_state != new_state, "failed to change cluster resource state");
+ }
+
+ return true;
+}
+
+static bool test_do_witness_RegisterEx(struct torture_context *tctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t version,
+ const char *net_name,
+ const char *share_name,
+ const char *ip_address,
+ const char *client_computer_name,
+ uint32_t flags,
+ uint32_t timeout,
+ struct policy_handle *context_handle)
+{
+ struct witness_RegisterEx r;
+
+ r.in.version = version;
+ r.in.net_name = net_name;
+ r.in.share_name = NULL;
+ r.in.ip_address = ip_address;
+ r.in.client_computer_name = client_computer_name;
+ r.in.flags = flags;
+ r.in.timeout = timeout;
+ r.out.context_handle = context_handle;
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_RegisterEx_r(b, tctx, &r),
+ "RegisterEx failed");
+
+ torture_assert_werr_ok(tctx,
+ r.out.result,
+ "RegisterEx failed");
+
+ return true;
+}
+
+static void torture_subunit_report_time(struct torture_context *tctx)
+{
+ struct timespec tp;
+ struct tm *tmp;
+ char timestr[200];
+
+ if (clock_gettime(CLOCK_REALTIME, &tp) != 0) {
+ torture_comment(tctx, "failed to call clock_gettime");
+ return;
+ }
+
+ tmp = gmtime(&tp.tv_sec);
+ if (!tmp) {
+ torture_comment(tctx, "failed to call gmtime");
+ return;
+ }
+
+ if (strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", tmp) <= 0) {
+ torture_comment(tctx, "failed to call strftime");
+ return;
+ }
+
+ torture_comment(tctx, "time: %s.%06ld\n", timestr, tp.tv_nsec / 1000);
+}
+
+static bool test_witness_AsyncNotify_timeouts(struct torture_context *tctx,
+ struct dcerpc_pipe *p,
+ void *data)
+{
+ struct dcerpc_binding_handle *b = p->binding_handle;
+ struct witness_AsyncNotify r;
+ struct witness_notifyResponse *response;
+ struct torture_test_witness_state *state =
+ (struct torture_test_witness_state *)data;
+ int i;
+
+ init_witness_test_state(tctx, p, state);
+
+ setup_clusapi_connection(tctx, state);
+
+ for (i=0; state->list && i < state->list->num_interfaces; i++) {
+
+ const char *ip_address;
+ struct witness_interfaceInfo interface = state->list->interfaces[i];
+ uint32_t timeouts[] = {
+ 0, 1, 10, 100, 120
+ };
+ int t;
+ uint32_t old_timeout;
+
+ if (!check_valid_interface(tctx, &interface)) {
+ continue;
+ }
+
+ torture_assert(tctx,
+ get_ip_address_from_interface(tctx, &interface, &ip_address),
+ "failed to get ip_address from interface");
+
+ for (t=0; t < ARRAY_SIZE(timeouts); t++) {
+
+ torture_comment(tctx, "Testing Async Notify with timeout of %d milliseconds", timeouts[t]);
+
+ torture_assert(tctx,
+ test_do_witness_RegisterEx(tctx, b,
+ WITNESS_V2,
+ state->net_name,
+ NULL,
+ ip_address,
+ lpcfg_netbios_name(tctx->lp_ctx),
+ 0,
+ timeouts[t],
+ &state->context_handle),
+ "failed to RegisterEx");
+
+ r.in.context_handle = state->context_handle;
+ r.out.response = &response;
+
+ old_timeout = dcerpc_binding_handle_set_timeout(b, UINT_MAX);
+
+ torture_subunit_report_time(tctx);
+
+ torture_assert_ntstatus_ok(tctx,
+ dcerpc_witness_AsyncNotify_r(b, tctx, &r),
+ "AsyncNotify failed");
+ torture_assert_werr_equal(tctx,
+ r.out.result,
+ WERR_TIMEOUT,
+ "AsyncNotify failed");
+
+ torture_subunit_report_time(tctx);
+
+ dcerpc_binding_handle_set_timeout(b, old_timeout);
+
+ torture_assert(tctx,
+ test_witness_UnRegister_with_handle(tctx, p, &state->context_handle),
+ "Failed to unregister");
+
+ ZERO_STRUCT(state->context_handle);
+ }
+ }
+
+ return true;
+}
+
+struct torture_suite *torture_rpc_witness(TALLOC_CTX *mem_ctx)
+{
+ struct torture_rpc_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "witness");
+ struct torture_test_witness_state *state;
+
+ tcase = torture_suite_add_rpc_iface_tcase(suite, "witness",
+ &ndr_table_witness);
+
+ state = talloc_zero(tcase, struct torture_test_witness_state);
+
+ torture_rpc_tcase_add_test_ex(tcase, "GetInterfaceList",
+ test_witness_GetInterfaceList, state);
+ torture_rpc_tcase_add_test_ex(tcase, "Register",
+ test_witness_Register, state);
+ torture_rpc_tcase_add_test_ex(tcase, "UnRegister",
+ test_witness_UnRegister, state);
+ torture_rpc_tcase_add_test_ex(tcase, "RegisterEx",
+ test_witness_RegisterEx, state);
+ torture_rpc_tcase_add_test_ex(tcase, "AsyncNotify",
+ test_witness_AsyncNotify, state);
+ torture_rpc_tcase_add_test_ex(tcase, "AsyncNotify_timeouts",
+ test_witness_AsyncNotify_timeouts, state);
+
+ return suite;
+}
diff --git a/source4/torture/rpc/wkssvc.c b/source4/torture/rpc/wkssvc.c
new file mode 100644
index 0000000..c43e16f
--- /dev/null
+++ b/source4/torture/rpc/wkssvc.c
@@ -0,0 +1,1458 @@
+/*
+ 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/torture_rpc.h"
+#include "lib/cmdline/cmdline.h"
+#include "param/param.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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrWkstaTransportAdd failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ WERR_INVALID_PARAMETER,
+ "NetrWkstaTransportAdd level 0 failed");
+
+ return true;
+}
+
+static bool test_NetrWkstaTransportDel(struct torture_context *tctx,
+ struct dcerpc_pipe *p)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrWkstaTransportDel r;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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 = lpcfg_workgroup(tctx->lp_ctx);
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+ const char *user = cli_credentials_get_username(creds);
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_PARAMETER },
+ { dom, 1, WERR_INVALID_PARAMETER },
+ { dom, 1101, WERR_INVALID_PARAMETER },
+ { user, 0, WERR_INVALID_PARAMETER },
+ { user, 1, WERR_INVALID_PARAMETER },
+ { user, 1101, WERR_INVALID_PARAMETER },
+ };
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrUseAdd failed");
+ torture_assert_werr_equal(tctx, r.out.result, WERR_INVALID_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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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_NERR_USENOTFOUND))
+ {
+ 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx);
+
+ torture_comment(tctx, "Testing NetrLogonDomainNameAdd\n");
+
+ status = dcerpc_wkssvc_NetrLogonDomainNameAdd_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.domain_name = lpcfg_workgroup(tctx->lp_ctx);
+
+ torture_comment(tctx, "Testing NetrLogonDomainNameDel\n");
+
+ status = dcerpc_wkssvc_NetrLogonDomainNameDel_r(b, 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,
+ size_t *num_names)
+{
+ NTSTATUS status;
+ struct wkssvc_NetrEnumerateComputerNames r;
+ struct wkssvc_ComputerNamesCtr *ctr;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.name = lpcfg_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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ for (i=0; i<ARRAY_SIZE(levels); i++) {
+
+ r.in.server_name = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.name = lpcfg_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_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status,
+ "NetrValidateName2 failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ W_ERROR(HRES_ERROR_V(HRES_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;
+ size_t num_names = 0;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ size_t num_names = 0;
+ int i;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ size_t 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ size_t 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ uint16_t *msg;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ if (!push_ucs2_talloc(tctx, &msg, message, &size)) {
+ return false;
+ }
+
+ 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 = (uint8_t *)msg;
+ r.in.message_size = size;
+
+ torture_comment(tctx, "Testing NetrMessageBufferSend\n");
+
+ status = dcerpc_wkssvc_NetrMessageBufferSend_r(b, 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 = "";
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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 = "";
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ 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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = lpcfg_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_r(b, 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;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = lpcfg_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_r(b, tctx, &r);
+ torture_assert_ntstatus_ok(tctx, status, "NetrGetJoinableOus2 failed");
+ torture_assert_werr_equal(tctx, r.out.result,
+ W_ERROR(HRES_ERROR_V(HRES_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 = samba_cmdline_get_creds();
+ const char *user = cli_credentials_get_username(creds);
+ const char *admin_account = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ admin_account = talloc_asprintf(tctx, "%s\\%s",
+ lpcfg_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_r(b, 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 = samba_cmdline_get_creds();
+ const char *user = cli_credentials_get_username(creds);
+ const char *admin_account = NULL;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ admin_account = talloc_asprintf(tctx, "%s\\%s",
+ lpcfg_workgroup(tctx->lp_ctx),
+ user);
+
+ r.in.server_name = dcerpc_server_name(p);
+ r.in.domain_name = lpcfg_dnsdomain(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_r(b, 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;
+}
+
+/*
+ * 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;
+ WERROR werr;
+ DATA_BLOB session_key;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ /* FIXME: this test assumes to join workstations / servers and does not
+ * handle DCs (WERR_NERR_SETUPDOMAINCONTROLLER) */
+
+ if (!test_GetJoinInformation(tctx, p, &join_status, &join_name))
+ {
+ return false;
+ }
+
+ switch (join_status) {
+ case NET_SETUP_DOMAIN_NAME:
+ expected_err = WERR_NERR_SETUPALREADYJOINED;
+ break;
+ case NET_SETUP_UNKNOWN_STATUS:
+ case NET_SETUP_UNJOINED:
+ case NET_SETUP_WORKGROUP_NAME:
+ 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;
+ }
+
+ werr = encode_wkssvc_join_password_buffer(tctx,
+ domain_admin_password,
+ &session_key,
+ &pwd_buf);
+ if (!W_ERROR_IS_OK(werr)) {
+ return false;
+ }
+
+ 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_r(b, 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 != NET_SETUP_DOMAIN_NAME) {
+ 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;
+ WERROR werr;
+ DATA_BLOB session_key;
+ struct dcerpc_binding_handle *b = p->binding_handle;
+
+ /* FIXME: this test assumes to join workstations / servers and does not
+ * handle DCs (WERR_NERR_SETUPDOMAINCONTROLLER) */
+
+ if (!test_GetJoinInformation(tctx, p, &join_status, &join_name))
+ {
+ return false;
+ }
+
+ switch (join_status) {
+ case NET_SETUP_UNJOINED:
+ expected_err = WERR_NERR_SETUPNOTJOINED;
+ break;
+ case NET_SETUP_DOMAIN_NAME:
+ case NET_SETUP_UNKNOWN_STATUS:
+ case NET_SETUP_WORKGROUP_NAME:
+ 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;
+ }
+
+ werr = encode_wkssvc_join_password_buffer(tctx,
+ domain_admin_password,
+ &session_key,
+ &pwd_buf);
+ if (!W_ERROR_IS_OK(werr)) {
+ return false;
+ }
+
+ 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_r(b, 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 NET_SETUP_UNJOINED:
+ case NET_SETUP_WORKGROUP_NAME:
+ break;
+ case NET_SETUP_UNKNOWN_STATUS:
+ case NET_SETUP_DOMAIN_NAME:
+ 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;
+}